Заметки / Таймер в графическом приложении

GCC, Linux
 Столкнулся со сложностью при создании таймера в графическом приложении на основе xlib под Ubuntu (18.04 LTS). Если немного дополнить этот пример, добавив таймер с полуторасекундной задержкой, то надпись "timer" не появится, хотя таймер сработает:

timer.c
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h> 
#include <X11/Xlib.h> 

#include <sys/time.h> #include <signal.h>
extern int errno; char msg [256]={"Hello, World!"}; int sdf; int width, height, tmp; Display *d; Window w; GC gc; XEvent e;
void timer_on () { XDrawString (d, w, gc, 50, 150, "timer", 5); }
void paint () { XGetGeometry (d, w, (Window *)&tmp, &tmp, &tmp, &width, &height, &tmp, &tmp); XSetForeground (d, gc, 0); XFillRectangle (d, w, gc, 0, 0, width, height); XSetForeground (d, gc, 255*256*256+255*256+255); XDrawString (d, w, gc, 50, 50, msg, strlen (msg)); } void main () { if ((d=XOpenDisplay (getenv ("DISPLAY")))==NULL) { printf ("Can't connect X server: %s\n", strerror (errno)); exit (1); } sdf=DefaultScreen (d); w=XCreateSimpleWindow (d, RootWindow (d, sdf), 0, 0, 250, 250, 1, BlackPixel (d, sdf), WhitePixel (d, sdf)); XSelectInput (d, w, ExposureMask | KeyPressMask | KeyReleaseMask); XMapWindow (d, w); // create the Graphics Context gc=XCreateGC (d, w, 0, 0);
// http://forums.codeguru.com/showthread.php?356101-How-to-use-timer-in-Unix struct itimerval tout_val; tout_val.it_interval.tv_sec = 0; tout_val.it_interval.tv_usec = 0; tout_val.it_value.tv_sec = 1; tout_val.it_value.tv_usec = 500000; setitimer(ITIMER_REAL, &tout_val,0); signal(SIGALRM,timer_on);
while (1) { XNextEvent (d, &e); if (e.type==Expose) { paint (); } if (e.type==KeyPress) { // Esc if (e.xkey.keycode==9) { break; } } } XFreeGC (d, gc); XDestroyWindow (d, w); XCloseDisplay (d); exit (1); }

Можно попробовать перенести отрисовку проверочной надписи в цикл "while", надеясь что её отрисовка выполнится наравне с функцией "paint":

timer.c
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h> 
#include <X11/Xlib.h> 

#include <sys/time.h>
#include <signal.h>

 extern int errno; 
   char msg [256]={"Hello, World!"};
    int sdf; 
    int width, height, tmp;
Display *d; 
 Window w;
     GC gc;
 XEvent e; 

bool timer_show=false; void timer_on () { timer_show=true; }
void paint () { XGetGeometry (d, w, (Window *)&tmp, &tmp, &tmp, &width, &height, &tmp, &tmp); XSetForeground (d, gc, 0); XFillRectangle (d, w, gc, 0, 0, width, height); XSetForeground (d, gc, 255*256*256+255*256+255); XDrawString (d, w, gc, 50, 50, msg, strlen (msg)); } void main () { if ((d=XOpenDisplay (getenv ("DISPLAY")))==NULL) { printf ("Can't connect X server: %s\n", strerror (errno)); exit (1); } sdf=DefaultScreen (d); w=XCreateSimpleWindow (d, RootWindow (d, sdf), 0, 0, 250, 250, 1, BlackPixel (d, sdf), WhitePixel (d, sdf)); XSelectInput (d, w, ExposureMask | KeyPressMask | KeyReleaseMask); XMapWindow (d, w); // create the Graphics Context gc=XCreateGC (d, w, 0, 0); // http://forums.codeguru.com/showthread.php?356101-How-to-use-timer-in-Unix struct itimerval tout_val; tout_val.it_interval.tv_sec = 0; tout_val.it_interval.tv_usec = 0; tout_val.it_value.tv_sec = 1; tout_val.it_value.tv_usec = 500000; setitimer(ITIMER_REAL, &tout_val,0); signal(SIGALRM,timer_on); while (1) {
if (timer_show) { XDrawString (d, w, gc, 50, 150, "timer", 5); timer_show=false; }
XNextEvent (d, &e); if (e.type==Expose) { paint (); } if (e.type==KeyPress) { // Esc if (e.xkey.keycode==9) { break; } } } XFreeGC (d, gc); XDestroyWindow (d, w); XCloseDisplay (d); exit (1); }

...но это также не даст эффекта: текст появится только после нажатия клавиши. Очевидно, что для отображения текста по событию таймера нужно создать дополнительно оконное событие, захватываемое "XNextEvent". Пробуем:

timer.c
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h> 
#include <X11/Xlib.h> 

#include <sys/time.h>
#include <signal.h>

 extern int errno; 
   char msg [256]={"Hello, World!"};
    int sdf; 
    int width, height, tmp;
Display *d; 
 Window w;
     GC gc;
 XEvent e; 

bool timer_show=false; void timer_on () { timer_show=true; // https://stackoverflow.com/questions/10785491/how-to-allow-a-worker-thread-to-updata-an-x11-window XEvent exppp; exppp.type = Expose; exppp.xexpose.window = w; XSendEvent(d,w,False,ExposureMask,&exppp); XFlush(d); }
void paint () { XGetGeometry (d, w, (Window *)&tmp, &tmp, &tmp, &width, &height, &tmp, &tmp); XSetForeground (d, gc, 0); XFillRectangle (d, w, gc, 0, 0, width, height); XSetForeground (d, gc, 255*256*256+255*256+255); XDrawString (d, w, gc, 50, 50, msg, strlen (msg)); } void main () { if ((d=XOpenDisplay (getenv ("DISPLAY")))==NULL) { printf ("Can't connect X server: %s\n", strerror (errno)); exit (1); } sdf=DefaultScreen (d); w=XCreateSimpleWindow (d, RootWindow (d, sdf), 0, 0, 250, 250, 1, BlackPixel (d, sdf), WhitePixel (d, sdf)); XSelectInput (d, w, ExposureMask | KeyPressMask | KeyReleaseMask); XMapWindow (d, w); // create the Graphics Context gc=XCreateGC (d, w, 0, 0); // http://forums.codeguru.com/showthread.php?356101-How-to-use-timer-in-Unix struct itimerval tout_val; tout_val.it_interval.tv_sec = 0; tout_val.it_interval.tv_usec = 0; tout_val.it_value.tv_sec = 1; tout_val.it_value.tv_usec = 500000; setitimer(ITIMER_REAL, &tout_val,0); signal(SIGALRM,timer_on); while (1) {
if (timer_show) { XDrawString (d, w, gc, 50, 150, "timer", 5); timer_show=false; }
XNextEvent (d, &e); if (e.type==Expose) { paint (); } if (e.type==KeyPress) { // Esc if (e.xkey.keycode==9) { break; } } } XFreeGC (d, gc); XDestroyWindow (d, w); XCloseDisplay (d); exit (1); }

Сейчас пример выполнился корректно:

Итог:

Написал данную заметку, так как при перенесе на Linux своей программы смог найти в Сети информацию только о консольных таймерах. Также показался необычным механизм данного функционала.

01.04.2019