- fixed the checkTimerHandlers() to flush the ASAP notification queue, even
[wmaker-crm.git] / WINGs / wutil.c
blob32e31262c45d14b6b12c9f0dda135228bae0989a
3 /*
4 * This event handling stuff was based on Tk.
5 * adapted from wevent.c
6 */
8 #include "WINGsP.h"
10 #include "../src/config.h"
12 #include <sys/types.h>
13 #include <unistd.h>
15 #ifdef HAVE_POLL_H
16 #include <poll.h>
17 #endif
20 #include <X11/Xos.h>
22 #ifdef HAVE_SYS_SELECT_H
23 # include <sys/select.h>
24 #endif
26 #include <time.h>
28 #ifndef X_GETTIMEOFDAY
29 #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
30 #endif
35 typedef struct TimerHandler {
36 WMCallback *callback; /* procedure to call */
37 struct timeval when; /* when to call the callback */
38 void *clientData;
39 struct TimerHandler *next;
40 } TimerHandler;
43 typedef struct IdleHandler {
44 WMCallback *callback;
45 void *clientData;
46 struct IdleHandler *next;
47 } IdleHandler;
50 typedef struct InputHandler {
51 WMInputProc *callback;
52 void *clientData;
53 int fd;
54 int mask;
55 struct InputHandler *next;
56 } InputHandler;
59 /* queue of timer event handlers */
60 static TimerHandler *timerHandler=NULL;
62 static IdleHandler *idleHandler=NULL;
64 static InputHandler *inputHandler=NULL;
68 #define timerPending() (timerHandler)
71 static void
72 rightNow(struct timeval *tv) {
73 X_GETTIMEOFDAY(tv);
76 /* is t1 after t2 ? */
77 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
78 (((t1).tv_sec == (t2).tv_sec) \
79 && ((t1).tv_usec > (t2).tv_usec)))
82 static void
83 addmillisecs(struct timeval *tv, int milliseconds)
85 tv->tv_usec += milliseconds*1000;
87 tv->tv_sec += tv->tv_usec/1000000;
88 tv->tv_usec = tv->tv_usec%1000000;
92 WMHandlerID
93 WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
95 TimerHandler *handler, *tmp;
97 handler = malloc(sizeof(TimerHandler));
98 if (!handler)
99 return NULL;
101 rightNow(&handler->when);
102 addmillisecs(&handler->when, milliseconds);
103 handler->callback = callback;
104 handler->clientData = cdata;
105 /* insert callback in queue, sorted by time left */
106 if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
107 /* first in the queue */
108 handler->next = timerHandler;
109 timerHandler = handler;
110 } else {
111 tmp = timerHandler;
112 while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
113 tmp = tmp->next;
115 handler->next = tmp->next;
116 tmp->next = handler;
118 return handler;
123 void
124 WMDeleteTimerWithClientData(void *cdata)
126 TimerHandler *handler, *tmp;
128 if (!cdata || !timerHandler)
129 return;
131 tmp = timerHandler;
132 if (tmp->clientData==cdata) {
133 timerHandler = tmp->next;
134 wfree(tmp);
135 } else {
136 while (tmp->next) {
137 if (tmp->next->clientData==cdata) {
138 handler = tmp->next;
139 tmp->next = handler->next;
140 wfree(handler);
141 break;
143 tmp = tmp->next;
150 void
151 WMDeleteTimerHandler(WMHandlerID handlerID)
153 TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
155 if (!handler || !timerHandler)
156 return;
158 tmp = timerHandler;
159 if (tmp==handler) {
160 timerHandler = handler->next;
161 wfree(handler);
162 } else {
163 while (tmp->next) {
164 if (tmp->next==handler) {
165 tmp->next=handler->next;
166 wfree(handler);
167 break;
169 tmp = tmp->next;
176 WMHandlerID
177 WMAddIdleHandler(WMCallback *callback, void *cdata)
179 IdleHandler *handler, *tmp;
181 handler = malloc(sizeof(IdleHandler));
182 if (!handler)
183 return NULL;
185 handler->callback = callback;
186 handler->clientData = cdata;
187 handler->next = NULL;
188 /* add callback at end of queue */
189 if (!idleHandler) {
190 idleHandler = handler;
191 } else {
192 tmp = idleHandler;
193 while (tmp->next) {
194 tmp = tmp->next;
196 tmp->next = handler;
199 return handler;
204 void
205 WMDeleteIdleHandler(WMHandlerID handlerID)
207 IdleHandler *tmp, *handler = (IdleHandler*)handlerID;
209 if (!handler || !idleHandler)
210 return;
212 tmp = idleHandler;
213 if (tmp == handler) {
214 idleHandler = handler->next;
215 wfree(handler);
216 } else {
217 while (tmp->next) {
218 if (tmp->next == handler) {
219 tmp->next = handler->next;
220 wfree(handler);
221 break;
223 tmp = tmp->next;
230 WMHandlerID
231 WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
233 InputHandler *handler;
235 handler = wmalloc(sizeof(InputHandler));
237 handler->fd = fd;
238 handler->mask = condition;
239 handler->callback = proc;
240 handler->clientData = clientData;
242 handler->next = inputHandler;
244 inputHandler = handler;
246 return handler;
250 void
251 WMDeleteInputHandler(WMHandlerID handlerID)
253 InputHandler *tmp, *handler = (InputHandler*)handlerID;
255 if (!handler || !inputHandler)
256 return;
258 tmp = inputHandler;
259 if (tmp == handler) {
260 inputHandler = handler->next;
261 wfree(handler);
262 } else {
263 while (tmp->next) {
264 if (tmp->next == handler) {
265 tmp->next = handler->next;
266 wfree(handler);
267 break;
269 tmp = tmp->next;
275 static Bool
276 checkIdleHandlers()
278 IdleHandler *handler, *tmp;
280 if (!idleHandler) {
281 W_FlushIdleNotificationQueue();
282 /* make sure an observer in queue didn't added an idle handler */
283 return (idleHandler!=NULL);
286 handler = idleHandler;
288 /* we will process all idleHandlers so, empty the handler list */
289 idleHandler = NULL;
291 while (handler) {
292 tmp = handler->next;
293 (*handler->callback)(handler->clientData);
294 /* remove the handler */
295 wfree(handler);
297 handler = tmp;
300 W_FlushIdleNotificationQueue();
302 /* this is not necesarrily False, because one handler can re-add itself */
303 return (idleHandler!=NULL);
308 static void
309 checkTimerHandlers()
311 TimerHandler *handler;
312 struct timeval now;
314 if (!timerHandler) {
315 W_FlushASAPNotificationQueue();
316 return;
319 rightNow(&now);
321 while (timerHandler && IS_AFTER(now, timerHandler->when)) {
322 handler = timerHandler;
323 timerHandler = timerHandler->next;
324 handler->next = NULL;
325 (*handler->callback)(handler->clientData);
326 wfree(handler);
329 W_FlushASAPNotificationQueue();
334 static void
335 delayUntilNextTimerEvent(struct timeval *delay)
337 struct timeval now;
339 if (!timerHandler) {
340 /* The return value of this function is only valid if there _are_
341 * active timers. */
342 delay->tv_sec = 0;
343 delay->tv_usec = 0;
344 return;
347 rightNow(&now);
348 if (IS_AFTER(now, timerHandler->when)) {
349 delay->tv_sec = 0;
350 delay->tv_usec = 0;
351 } else {
352 delay->tv_sec = timerHandler->when.tv_sec - now.tv_sec;
353 delay->tv_usec = timerHandler->when.tv_usec - now.tv_usec;
354 if (delay->tv_usec < 0) {
355 delay->tv_usec += 1000000;
356 delay->tv_sec--;
363 * This functions will handle input events on all registered file descriptors.
364 * Input:
365 * - waitForInput - True if we want the function to wait until an event
366 * appears on a file descriptor we watch, False if we
367 * want the function to immediately return if there is
368 * no data available on the file descriptors we watch.
369 * Output:
370 * if waitForInput is False, the function will return False if there are no
371 * input handlers registered, or if there is no data
372 * available on the registered ones, and will return True
373 * if there is at least one input handler that has data
374 * available.
375 * if waitForInput is True, the function will return False if there are no
376 * input handlers registered, else it will block until an
377 * event appears on one of the file descriptors it watches
378 * and then it will return True.
380 * If the retured value is True, the input handlers for the corresponding file
381 * descriptors are also called.
384 static Bool
385 handleInputEvents(Bool waitForInput)
387 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
388 struct pollfd *fds;
389 InputHandler *handler;
390 int count, timeout, nfds, k;
392 if (!inputHandler) {
393 W_FlushASAPNotificationQueue();
394 return False;
397 for (nfds = 0, handler = inputHandler;
398 handler != 0; handler = handler->next) nfds++;
400 fds = wmalloc(nfds * sizeof(struct pollfd));
402 for (k = 0, handler = inputHandler;
403 handler;
404 handler = handler->next, k++) {
405 fds[k].fd = handler->fd;
406 fds[k].events = 0;
407 if (handler->mask & WIReadMask)
408 fds[k].events |= POLLIN;
410 if (handler->mask & WIWriteMask)
411 fds[k].events |= POLLOUT;
413 #if 0 /* FIXME */
414 if (handler->mask & WIExceptMask)
415 FD_SET(handler->fd, &eset);
416 #endif
420 * If we don't wait for input, set timeout to return immediately,
421 * else setup the timeout to the estimated time until the
422 * next timer expires or if no timer is pending to infinite.
424 if (!waitForInput) {
425 timeout = 0;
426 } else if (timerPending()) {
427 struct timeval tv;
428 delayUntilNextTimerEvent(&tv);
429 timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
430 } else {
431 timeout = -1;
434 count = poll(fds, nfds, timeout);
436 if (count > 0) {
437 handler = inputHandler;
438 k = 0;
439 while (handler) {
440 int mask;
441 InputHandler *next;
443 mask = 0;
445 if (fds[k].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI))
446 mask |= WIReadMask;
448 if (fds[k].revents & (POLLOUT | POLLWRBAND))
449 mask |= WIWriteMask;
451 if (fds[k].revents & (POLLHUP | POLLNVAL | POLLERR))
452 mask |= WIExceptMask;
454 next = handler->next;
456 if (mask!=0 && handler->callback) {
457 (*handler->callback)(handler->fd, mask,
458 handler->clientData);
461 handler = next;
462 k++;
466 wfree(fds);
468 W_FlushASAPNotificationQueue();
470 return (count > 0);
471 #else /* not HAVE_POLL */
472 #ifdef HAVE_SELECT
473 struct timeval timeout;
474 struct timeval *timeoutPtr;
475 fd_set rset, wset, eset;
476 int maxfd;
477 int count;
478 InputHandler *handler = inputHandler;
480 if (!inputHandler) {
481 W_FlushASAPNotificationQueue();
482 return False;
485 FD_ZERO(&rset);
486 FD_ZERO(&wset);
487 FD_ZERO(&eset);
489 maxfd = 0;
491 while (handler) {
492 if (handler->mask & WIReadMask)
493 FD_SET(handler->fd, &rset);
495 if (handler->mask & WIWriteMask)
496 FD_SET(handler->fd, &wset);
498 if (handler->mask & WIExceptMask)
499 FD_SET(handler->fd, &eset);
501 if (maxfd < handler->fd)
502 maxfd = handler->fd;
504 handler = handler->next;
508 * If we don't wait for input, set timeout to return immediately,
509 * else setup the timeout to the estimated time until the
510 * next timer expires or if no timer is pending to infinite.
512 if (!waitForInput) {
513 timeout.tv_sec = 0;
514 timeout.tv_usec = 0;
515 timeoutPtr = &timeout;
516 } else if (timerPending()) {
517 delayUntilNextTimerEvent(&timeout);
518 timeoutPtr = &timeout;
519 } else {
520 timeoutPtr = (struct timeval*)0;
523 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
525 if (count > 0) {
526 handler = inputHandler;
528 while (handler) {
529 int mask;
530 InputHandler *next;
532 mask = 0;
534 if (FD_ISSET(handler->fd, &rset))
535 mask |= WIReadMask;
537 if (FD_ISSET(handler->fd, &wset))
538 mask |= WIWriteMask;
540 if (FD_ISSET(handler->fd, &eset))
541 mask |= WIExceptMask;
543 next = handler->next;
545 if (mask!=0 && handler->callback) {
546 (*handler->callback)(handler->fd, mask,
547 handler->clientData);
550 handler = next;
554 W_FlushASAPNotificationQueue();
556 return (count > 0);
557 #else /* not HAVE_SELECT, not HAVE_POLL */
558 Neither select nor poll. You lose.
559 #endif /* HAVE_SELECT */
560 #endif /* HAVE_POLL */
564 void
565 WHandleEvents()
567 /* Check any expired timers */
568 checkTimerHandlers();
570 /* We need to make sure that we have some input handler before calling
571 * checkIdleHandlers() in a while loop, because else the while loop
572 * can run forever (if some idle handler reinitiates itself).
574 if (inputHandler) {
575 /* Do idle and timer stuff while there are no input events */
576 /* We check InputHandler again because some idle handler could have
577 * removed them */
578 while (checkIdleHandlers() && inputHandler && !handleInputEvents(False)) {
579 /* dispatch timer events */
580 checkTimerHandlers();
582 } else {
583 checkIdleHandlers();
584 /* dispatch timer events */
585 checkTimerHandlers();
588 handleInputEvents(True);
590 /* Check any expired timers */
591 checkTimerHandlers();