Fix periodic focus bug
[wmaker-crm.git] / WINGs / handlers.c
blob84978f01baf74999da10c7c77c746454a89f31b5
2 /*
3 * WINGs internal handlers: timer, idle and input handlers
4 */
6 #include "wconfig.h"
7 #include "WINGsP.h"
9 #include <sys/types.h>
10 #include <unistd.h>
12 #include <X11/Xos.h>
14 #ifdef HAVE_SYS_SELECT_H
15 # include <sys/select.h>
16 #endif
18 #include <time.h>
20 #ifndef X_GETTIMEOFDAY
21 #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
22 #endif
27 typedef struct TimerHandler {
28 WMCallback *callback; /* procedure to call */
29 struct timeval when; /* when to call the callback */
30 void *clientData;
31 struct TimerHandler *next;
32 int nextDelay; /* 0 if it's one-shot */
33 } TimerHandler;
36 typedef struct IdleHandler {
37 WMCallback *callback;
38 void *clientData;
39 } IdleHandler;
42 typedef struct InputHandler {
43 WMInputProc *callback;
44 void *clientData;
45 int fd;
46 int mask;
47 } InputHandler;
50 /* queue of timer event handlers */
51 static TimerHandler *timerHandler=NULL;
53 static WMArray *idleHandler=NULL;
55 static WMArray *inputHandler=NULL;
57 #define timerPending() (timerHandler)
61 static void
62 rightNow(struct timeval *tv)
64 X_GETTIMEOFDAY(tv);
67 /* is t1 after t2 ? */
68 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
69 (((t1).tv_sec == (t2).tv_sec) \
70 && ((t1).tv_usec > (t2).tv_usec)))
72 #define IS_ZERO(tv) (tv.tv_sec == 0 && tv.tv_usec == 0)
74 #define SET_ZERO(tv) tv.tv_sec = 0, tv.tv_usec = 0
76 static void
77 addmillisecs(struct timeval *tv, int milliseconds)
79 tv->tv_usec += milliseconds*1000;
81 tv->tv_sec += tv->tv_usec/1000000;
82 tv->tv_usec = tv->tv_usec%1000000;
86 static void
87 enqueueTimerHandler(TimerHandler *handler)
89 TimerHandler *tmp;
91 /* insert callback in queue, sorted by time left */
92 if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
93 /* first in the queue */
94 handler->next = timerHandler;
95 timerHandler = handler;
96 } else {
97 tmp = timerHandler;
98 while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
99 tmp = tmp->next;
101 handler->next = tmp->next;
102 tmp->next = handler;
107 static void
108 delayUntilNextTimerEvent(struct timeval *delay)
110 struct timeval now;
111 TimerHandler *handler;
113 handler = timerHandler;
114 while (handler && IS_ZERO(handler->when)) handler = handler->next;
116 if (!handler) {
117 /* The return value of this function is only valid if there _are_
118 timers active. */
119 delay->tv_sec = 0;
120 delay->tv_usec = 0;
121 return;
124 rightNow(&now);
125 if (IS_AFTER(now, handler->when)) {
126 delay->tv_sec = 0;
127 delay->tv_usec = 0;
128 } else {
129 delay->tv_sec = handler->when.tv_sec - now.tv_sec;
130 delay->tv_usec = handler->when.tv_usec - now.tv_usec;
131 if (delay->tv_usec < 0) {
132 delay->tv_usec += 1000000;
133 delay->tv_sec--;
139 WMHandlerID
140 WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
142 TimerHandler *handler;
144 handler = malloc(sizeof(TimerHandler));
145 if (!handler)
146 return NULL;
148 rightNow(&handler->when);
149 addmillisecs(&handler->when, milliseconds);
150 handler->callback = callback;
151 handler->clientData = cdata;
152 handler->nextDelay = 0;
154 enqueueTimerHandler(handler);
156 return handler;
160 WMHandlerID
161 WMAddPersistentTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
163 TimerHandler *handler = WMAddTimerHandler(milliseconds, callback, cdata);
165 if (handler != NULL)
166 handler->nextDelay = milliseconds;
168 return handler;
173 void
174 WMDeleteTimerWithClientData(void *cdata)
176 TimerHandler *handler, *tmp;
178 if (!cdata || !timerHandler)
179 return;
181 tmp = timerHandler;
182 if (tmp->clientData==cdata) {
183 tmp->nextDelay = 0;
184 if (!IS_ZERO(tmp->when)) {
185 timerHandler = tmp->next;
186 wfree(tmp);
188 } else {
189 while (tmp->next) {
190 if (tmp->next->clientData==cdata) {
191 handler = tmp->next;
192 handler->nextDelay = 0;
193 if (IS_ZERO(handler->when))
194 break;
195 tmp->next = handler->next;
196 wfree(handler);
197 break;
199 tmp = tmp->next;
206 void
207 WMDeleteTimerHandler(WMHandlerID handlerID)
209 TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
211 if (!handler || !timerHandler)
212 return;
214 tmp = timerHandler;
216 handler->nextDelay = 0;
218 if (IS_ZERO(handler->when))
219 return;
221 if (tmp==handler) {
222 timerHandler = handler->next;
223 wfree(handler);
224 } else {
225 while (tmp->next) {
226 if (tmp->next==handler) {
227 tmp->next=handler->next;
228 wfree(handler);
229 break;
231 tmp = tmp->next;
238 WMHandlerID
239 WMAddIdleHandler(WMCallback *callback, void *cdata)
241 IdleHandler *handler;
243 handler = malloc(sizeof(IdleHandler));
244 if (!handler)
245 return NULL;
247 handler->callback = callback;
248 handler->clientData = cdata;
249 /* add handler at end of queue */
250 if (!idleHandler) {
251 idleHandler = WMCreateArrayWithDestructor(16, wfree);
253 WMAddToArray(idleHandler, handler);
255 return handler;
259 void
260 WMDeleteIdleHandler(WMHandlerID handlerID)
262 IdleHandler *handler = (IdleHandler*)handlerID;
264 if (!handler || !idleHandler)
265 return;
267 WMRemoveFromArray(idleHandler, handler);
272 WMHandlerID
273 WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
275 InputHandler *handler;
277 handler = wmalloc(sizeof(InputHandler));
279 handler->fd = fd;
280 handler->mask = condition;
281 handler->callback = proc;
282 handler->clientData = clientData;
284 if (!inputHandler)
285 inputHandler = WMCreateArrayWithDestructor(16, wfree);
286 WMAddToArray(inputHandler, handler);
288 return handler;
293 void
294 WMDeleteInputHandler(WMHandlerID handlerID)
296 InputHandler *handler = (InputHandler*)handlerID;
298 if (!handler || !inputHandler)
299 return;
301 WMRemoveFromArray(inputHandler, handler);
305 Bool
306 W_CheckIdleHandlers(void)
308 IdleHandler *handler;
309 WMArray *handlerCopy;
310 WMArrayIterator iter;
312 if (!idleHandler || WMGetArrayItemCount(idleHandler)==0) {
313 W_FlushIdleNotificationQueue();
314 /* make sure an observer in queue didn't added an idle handler */
315 return (idleHandler!=NULL && WMGetArrayItemCount(idleHandler)>0);
318 handlerCopy = WMDuplicateArray(idleHandler);
320 WM_ITERATE_ARRAY(handlerCopy, handler, iter) {
321 /* check if the handler still exist or was removed by a callback */
322 if (WMGetFirstInArray(idleHandler, handler) == WANotFound)
323 continue;
325 (*handler->callback)(handler->clientData);
326 WMDeleteIdleHandler(handler);
329 WMFreeArray(handlerCopy);
331 W_FlushIdleNotificationQueue();
333 /* this is not necesarrily False, because one handler can re-add itself */
334 return (WMGetArrayItemCount(idleHandler)>0);
339 void
340 W_CheckTimerHandlers(void)
342 TimerHandler *handler;
343 struct timeval now;
345 if (!timerHandler) {
346 W_FlushASAPNotificationQueue();
347 return;
350 rightNow(&now);
352 handler = timerHandler;
353 while (handler && IS_AFTER(now, handler->when)) {
354 if (!IS_ZERO(handler->when)) {
355 SET_ZERO(handler->when);
356 (*handler->callback)(handler->clientData);
358 handler = handler->next;
361 while (timerHandler && IS_ZERO(timerHandler->when)) {
362 handler = timerHandler;
363 timerHandler = timerHandler->next;
365 if (handler->nextDelay > 0) {
366 handler->when = now;
367 addmillisecs(&handler->when, handler->nextDelay);
368 enqueueTimerHandler(handler);
369 } else {
370 wfree(handler);
374 W_FlushASAPNotificationQueue();
379 * This functions will handle input events on all registered file descriptors.
380 * Input:
381 * - waitForInput - True if we want the function to wait until an event
382 * appears on a file descriptor we watch, False if we
383 * want the function to immediately return if there is
384 * no data available on the file descriptors we watch.
385 * - inputfd - Extra input file descriptor to watch for input.
386 * This is only used when called from wevent.c to watch
387 * on ConnectionNumber(dpy) to avoid blocking of X events
388 * if we wait for input from other file handlers.
389 * Output:
390 * if waitForInput is False, the function will return False if there are no
391 * input handlers registered, or if there is no data
392 * available on the registered ones, and will return True
393 * if there is at least one input handler that has data
394 * available.
395 * if waitForInput is True, the function will return False if there are no
396 * input handlers registered, else it will block until an
397 * event appears on one of the file descriptors it watches
398 * and then it will return True.
400 * If the retured value is True, the input handlers for the corresponding file
401 * descriptors are also called.
403 * Parametersshould be passed like this:
404 * - from wevent.c:
405 * waitForInput - apropriate value passed by the function who called us
406 * inputfd = ConnectionNumber(dpy)
407 * - from wutil.c:
408 * waitForInput - apropriate value passed by the function who called us
409 * inputfd = -1
412 Bool
413 W_HandleInputEvents(Bool waitForInput, int inputfd)
415 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
416 struct poll fd *fds;
417 InputHandler *handler;
418 int count, timeout, nfds, i, extrafd;
420 extrafd = (inputfd < 0) ? 0 : 1;
422 if (inputHandler)
423 nfds = WMGetArrayItemCount(inputHandler);
424 else
425 nfds = 0;
427 if (!extrafd && nfds==0) {
428 W_FlushASAPNotificationQueue();
429 return False;
432 fds = wmalloc((nfds+extrafd) * sizeof(struct pollfd));
433 if (extrafd) {
434 /* put this to the end of array to avoid using ranges from 1 to nfds+1 */
435 fds[nfds].fd = inputfd;
436 fds[nfds].events = POLLIN;
439 /* use WM_ITERATE_ARRAY() here */
440 for (i = 0; i<nfds; i++) {
441 handler = WMGetFromArray(inputHandler, i);
442 fds[i].fd = handler->fd;
443 fds[i].events = 0;
444 if (handler->mask & WIReadMask)
445 fds[i].events |= POLLIN;
447 if (handler->mask & WIWriteMask)
448 fds[i].events |= POLLOUT;
450 #if 0 /* FIXME */
451 if (handler->mask & WIExceptMask)
452 FD_SET(handler->fd, &eset);
453 #endif
457 * Setup the timeout to the estimated time until the
458 * next timer expires.
460 if (!waitForInput) {
461 timeout = 0;
462 } else if (timerPending()) {
463 struct timeval tv;
464 delayUntilNextTimerEvent(&tv);
465 timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
466 } else {
467 timeout = -1;
470 count = poll(fds, nfds+extrafd, timeout);
472 if (count>0 && nfds>0) {
473 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
474 int mask;
476 /* use WM_ITERATE_ARRAY() here */
477 for (i=0; i<nfds; i++) {
478 handler = WMGetFromArray(handlerCopy, i);
479 /* check if the handler still exist or was removed by a callback */
480 if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
481 continue;
483 mask = 0;
485 if ((handler->mask & WIReadMask) &&
486 (fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)))
487 mask |= WIReadMask;
489 if ((handler->mask & WIWriteMask) &&
490 (fds[i].revents & (POLLOUT | POLLWRBAND)))
491 mask |= WIWriteMask;
493 if ((handler->mask & WIExceptMask) &&
494 (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
495 mask |= WIExceptMask;
497 if (mask!=0 && handler->callback) {
498 (*handler->callback)(handler->fd, mask,
499 handler->clientData);
503 WMFreeArray(handlerCopy);
506 wfree(fds);
508 W_FlushASAPNotificationQueue();
510 return (count > 0);
511 #else
512 #ifdef HAVE_SELECT
513 struct timeval timeout;
514 struct timeval *timeoutPtr;
515 fd_set rset, wset, eset;
516 int maxfd, nfds, i;
517 int count;
518 InputHandler *handler;
520 if (inputHandler)
521 nfds = WMGetArrayItemCount(inputHandler);
522 else
523 nfds = 0;
525 if (inputfd<0 && nfds==0) {
526 W_FlushASAPNotificationQueue();
527 return False;
530 FD_ZERO(&rset);
531 FD_ZERO(&wset);
532 FD_ZERO(&eset);
534 if (inputfd < 0) {
535 maxfd = 0;
536 } else {
537 FD_SET(inputfd, &rset);
538 maxfd = inputfd;
541 /* use WM_ITERATE_ARRAY() here */
542 for (i=0; i<nfds; i++) {
543 handler = WMGetFromArray(inputHandler, i);
544 if (handler->mask & WIReadMask)
545 FD_SET(handler->fd, &rset);
547 if (handler->mask & WIWriteMask)
548 FD_SET(handler->fd, &wset);
550 if (handler->mask & WIExceptMask)
551 FD_SET(handler->fd, &eset);
553 if (maxfd < handler->fd)
554 maxfd = handler->fd;
558 * Setup the timeout to the estimated time until the
559 * next timer expires.
561 if (!waitForInput) {
562 SET_ZERO(timeout);
563 timeoutPtr = &timeout;
564 } else if (timerPending()) {
565 delayUntilNextTimerEvent(&timeout);
566 timeoutPtr = &timeout;
567 } else {
568 timeoutPtr = (struct timeval*)0;
571 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
573 if (count>0 && nfds>0) {
574 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
575 int mask;
577 /* use WM_ITERATE_ARRAY() here */
578 for (i=0; i<nfds; i++) {
579 handler = WMGetFromArray(handlerCopy, i);
580 /* check if the handler still exist or was removed by a callback */
581 if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
582 continue;
584 mask = 0;
586 if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
587 mask |= WIReadMask;
589 if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
590 mask |= WIWriteMask;
592 if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
593 mask |= WIExceptMask;
595 if (mask!=0 && handler->callback) {
596 (*handler->callback)(handler->fd, mask,
597 handler->clientData);
601 WMFreeArray(handlerCopy);
604 W_FlushASAPNotificationQueue();
606 return (count > 0);
607 #else /* not HAVE_SELECT, not HAVE_POLL */
608 # error Neither select nor poll. You lose.
609 #endif /* HAVE_SELECT */
610 #endif /* HAVE_POLL */