Fixed a bug in input event handling.
[wmaker-crm.git] / WINGs / wutil.c
blobd939c04bb97af2c3b1b91e5eaff6ed299e042805
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 ((handler->mask & WIReadMask) &&
446 fds[k].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI))
447 mask |= WIReadMask;
449 if ((handler->mask & WIWriteMask) &&
450 fds[k].revents & (POLLOUT | POLLWRBAND))
451 mask |= WIWriteMask;
453 if ((handler->mask & WIExceptMask) &&
454 fds[k].revents & (POLLHUP | POLLNVAL | POLLERR))
455 mask |= WIExceptMask;
457 next = handler->next;
459 if (mask!=0 && handler->callback) {
460 (*handler->callback)(handler->fd, mask,
461 handler->clientData);
464 handler = next;
465 k++;
469 wfree(fds);
471 W_FlushASAPNotificationQueue();
473 return (count > 0);
474 #else /* not HAVE_POLL */
475 #ifdef HAVE_SELECT
476 struct timeval timeout;
477 struct timeval *timeoutPtr;
478 fd_set rset, wset, eset;
479 int maxfd;
480 int count;
481 InputHandler *handler = inputHandler;
483 if (!inputHandler) {
484 W_FlushASAPNotificationQueue();
485 return False;
488 FD_ZERO(&rset);
489 FD_ZERO(&wset);
490 FD_ZERO(&eset);
492 maxfd = 0;
494 while (handler) {
495 if (handler->mask & WIReadMask)
496 FD_SET(handler->fd, &rset);
498 if (handler->mask & WIWriteMask)
499 FD_SET(handler->fd, &wset);
501 if (handler->mask & WIExceptMask)
502 FD_SET(handler->fd, &eset);
504 if (maxfd < handler->fd)
505 maxfd = handler->fd;
507 handler = handler->next;
511 * If we don't wait for input, set timeout to return immediately,
512 * else setup the timeout to the estimated time until the
513 * next timer expires or if no timer is pending to infinite.
515 if (!waitForInput) {
516 timeout.tv_sec = 0;
517 timeout.tv_usec = 0;
518 timeoutPtr = &timeout;
519 } else if (timerPending()) {
520 delayUntilNextTimerEvent(&timeout);
521 timeoutPtr = &timeout;
522 } else {
523 timeoutPtr = (struct timeval*)0;
526 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
528 if (count > 0) {
529 handler = inputHandler;
531 while (handler) {
532 int mask;
533 InputHandler *next;
535 mask = 0;
537 if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
538 mask |= WIReadMask;
540 if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
541 mask |= WIWriteMask;
543 if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
544 mask |= WIExceptMask;
546 next = handler->next;
548 if (mask!=0 && handler->callback) {
549 (*handler->callback)(handler->fd, mask,
550 handler->clientData);
553 handler = next;
557 W_FlushASAPNotificationQueue();
559 return (count > 0);
560 #else /* not HAVE_SELECT, not HAVE_POLL */
561 Neither select nor poll. You lose.
562 #endif /* HAVE_SELECT */
563 #endif /* HAVE_POLL */
567 void
568 WHandleEvents()
570 /* Check any expired timers */
571 checkTimerHandlers();
573 /* We need to make sure that we have some input handler before calling
574 * checkIdleHandlers() in a while loop, because else the while loop
575 * can run forever (if some idle handler reinitiates itself).
577 if (inputHandler) {
578 /* Do idle and timer stuff while there are no input events */
579 /* We check InputHandler again because some idle handler could have
580 * removed them */
581 while (checkIdleHandlers() && inputHandler && !handleInputEvents(False)) {
582 /* dispatch timer events */
583 checkTimerHandlers();
585 } else {
586 checkIdleHandlers();
587 /* dispatch timer events */
588 checkTimerHandlers();
591 handleInputEvents(True);
593 /* Check any expired timers */
594 checkTimerHandlers();