Заметки / Таймер в графическом приложении
|
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);
gc=XCreateGC (d, w, 0, 0);
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)
{
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);
gc=XCreateGC (d, w, 0, 0);
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)
{
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;
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);
gc=XCreateGC (d, w, 0, 0);
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)
{
if (e.xkey.keycode==9)
{
break;
}
}
}
XFreeGC (d, gc);
XDestroyWindow (d, w);
XCloseDisplay (d);
exit (1);
}
Сейчас пример выполнился корректно:
Итог:
Написал данную заметку, так как при перенесе на Linux своей программы смог найти в Сети информацию только о консольных таймерах. Также показался необычным механизм данного функционала.
|
|