- moved WINGs's internal handlers (timer, idle and input) to handlers.c
[wmaker-crm.git] / WINGs / handlers.c
blob418fa1882fb4277049c1b7ef34cdc6c07fd30c7f
2 /*
3 * WINGs internal handlers: timer, idle and input handlers
4 */
6 #include "WINGsP.h"
8 #include "../src/config.h"
10 #include <sys/types.h>
11 #include <unistd.h>
13 #include <X11/Xos.h>
15 #ifdef HAVE_SYS_SELECT_H
16 # include <sys/select.h>
17 #endif
19 #include <time.h>
21 #ifndef X_GETTIMEOFDAY
22 #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
23 #endif
28 typedef struct TimerHandler {
29 WMCallback *callback; /* procedure to call */
30 struct timeval when; /* when to call the callback */
31 void *clientData;
32 struct TimerHandler *next;
33 int nextDelay; /* 0 if it's one-shot */
34 } TimerHandler;
37 typedef struct IdleHandler {
38 WMCallback *callback;
39 void *clientData;
40 } IdleHandler;
43 typedef struct InputHandler {
44 WMInputProc *callback;
45 void *clientData;
46 int fd;
47 int mask;
48 } InputHandler;
51 /* queue of timer event handlers */
52 static TimerHandler *timerHandler=NULL;
54 static WMBag *idleHandler=NULL;
56 static WMBag *inputHandler=NULL;
58 // this should go to wevent.c and wutil.c too
59 #define timerPending() (timerHandler)
63 static void
64 rightNow(struct timeval *tv) {
65 X_GETTIMEOFDAY(tv);
68 /* is t1 after t2 ? */
69 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
70 (((t1).tv_sec == (t2).tv_sec) \
71 && ((t1).tv_usec > (t2).tv_usec)))
73 #define IS_ZERO(tv) (tv.tv_sec == 0 && tv.tv_usec == 0)
75 #define SET_ZERO(tv) tv.tv_sec = 0, tv.tv_usec = 0
77 static void
78 addmillisecs(struct timeval *tv, int milliseconds)
80 tv->tv_usec += milliseconds*1000;
82 tv->tv_sec += tv->tv_usec/1000000;
83 tv->tv_usec = tv->tv_usec%1000000;
87 static void
88 enqueueTimerHandler(TimerHandler *handler)
90 TimerHandler *tmp;
92 /* insert callback in queue, sorted by time left */
93 if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
94 /* first in the queue */
95 handler->next = timerHandler;
96 timerHandler = handler;
97 } else {
98 tmp = timerHandler;
99 while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
100 tmp = tmp->next;
102 handler->next = tmp->next;
103 tmp->next = handler;
108 static void
109 delayUntilNextTimerEvent(struct timeval *delay)
111 struct timeval now;
112 TimerHandler *handler;
114 handler = timerHandler;
115 while (handler && IS_ZERO(handler->when)) handler = handler->next;
117 if (!handler) {
118 /* The return value of this function is only valid if there _are_
119 timers active. */
120 delay->tv_sec = 0;
121 delay->tv_usec = 0;
122 return;
125 rightNow(&now);
126 if (IS_AFTER(now, handler->when)) {
127 delay->tv_sec = 0;
128 delay->tv_usec = 0;
129 } else {
130 delay->tv_sec = handler->when.tv_sec - now.tv_sec;
131 delay->tv_usec = handler->when.tv_usec - now.tv_usec;
132 if (delay->tv_usec < 0) {
133 delay->tv_usec += 1000000;
134 delay->tv_sec--;
140 WMHandlerID
141 WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
143 TimerHandler *handler;
145 handler = malloc(sizeof(TimerHandler));
146 if (!handler)
147 return NULL;
149 rightNow(&handler->when);
150 addmillisecs(&handler->when, milliseconds);
151 handler->callback = callback;
152 handler->clientData = cdata;
153 handler->nextDelay = 0;
155 enqueueTimerHandler(handler);
157 return handler;
161 WMHandlerID
162 WMAddPersistentTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
164 TimerHandler *handler = WMAddTimerHandler(milliseconds, callback, cdata);
166 if (handler != NULL)
167 handler->nextDelay = milliseconds;
169 return handler;
174 void
175 WMDeleteTimerWithClientData(void *cdata)
177 TimerHandler *handler, *tmp;
179 if (!cdata || !timerHandler)
180 return;
182 tmp = timerHandler;
183 if (tmp->clientData==cdata) {
184 tmp->nextDelay = 0;
185 if (!IS_ZERO(tmp->when)) {
186 timerHandler = tmp->next;
187 wfree(tmp);
189 } else {
190 while (tmp->next) {
191 if (tmp->next->clientData==cdata) {
192 handler = tmp->next;
193 handler->nextDelay = 0;
194 if (IS_ZERO(handler->when))
195 break;
196 tmp->next = handler->next;
197 wfree(handler);
198 break;
200 tmp = tmp->next;
207 void
208 WMDeleteTimerHandler(WMHandlerID handlerID)
210 TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
212 if (!handler || !timerHandler)
213 return;
215 tmp = timerHandler;
217 handler->nextDelay = 0;
219 if (IS_ZERO(handler->when))
220 return;
222 if (tmp==handler) {
223 timerHandler = handler->next;
224 wfree(handler);
225 } else {
226 while (tmp->next) {
227 if (tmp->next==handler) {
228 tmp->next=handler->next;
229 wfree(handler);
230 break;
232 tmp = tmp->next;
239 WMHandlerID
240 WMAddIdleHandler(WMCallback *callback, void *cdata)
242 IdleHandler *handler;
244 handler = malloc(sizeof(IdleHandler));
245 if (!handler)
246 return NULL;
248 handler->callback = callback;
249 handler->clientData = cdata;
250 /* add handler at end of queue */
251 if (!idleHandler) {
252 idleHandler = WMCreateBag(16);
254 WMPutInBag(idleHandler, handler);
256 return handler;
260 void
261 WMDeleteIdleHandler(WMHandlerID handlerID)
263 IdleHandler *handler = (IdleHandler*)handlerID;
264 int pos;
266 if (!handler || !idleHandler)
267 return;
269 pos = WMGetFirstInBag(idleHandler, handler);
270 if (pos != WBNotFound) {
271 wfree(handler);
272 WMDeleteFromBag(idleHandler, pos);
278 WMHandlerID
279 WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
281 InputHandler *handler;
283 handler = wmalloc(sizeof(InputHandler));
285 handler->fd = fd;
286 handler->mask = condition;
287 handler->callback = proc;
288 handler->clientData = clientData;
290 if (!inputHandler)
291 inputHandler = WMCreateBag(16);
292 WMPutInBag(inputHandler, handler);
294 return handler;
299 void
300 WMDeleteInputHandler(WMHandlerID handlerID)
302 InputHandler *handler = (InputHandler*)handlerID;
303 int pos;
305 if (!handler || !inputHandler)
306 return;
308 pos = WMGetFirstInBag(inputHandler, handler);
309 if (pos != WBNotFound) {
310 wfree(handler);
311 WMDeleteFromBag(inputHandler, pos);
316 Bool
317 W_CheckIdleHandlers(void)
319 IdleHandler *handler;
320 WMBag *handlerCopy;
321 WMBagIterator iter;
323 if (!idleHandler || WMGetBagItemCount(idleHandler)==0) {
324 W_FlushIdleNotificationQueue();
325 /* make sure an observer in queue didn't added an idle handler */
326 return (idleHandler!=NULL && WMGetBagItemCount(idleHandler)>0);
329 handlerCopy = WMCreateBag(WMGetBagItemCount(idleHandler));
330 WMAppendBag(handlerCopy, idleHandler);
332 for (handler = WMBagFirst(handlerCopy, &iter);
333 iter != NULL;
334 handler = WMBagNext(handlerCopy, &iter)) {
335 /* check if the handler still exist or was removed by a callback */
336 if (WMGetFirstInBag(idleHandler, handler) == WBNotFound)
337 continue;
339 (*handler->callback)(handler->clientData);
340 WMDeleteIdleHandler(handler);
343 WMFreeBag(handlerCopy);
345 W_FlushIdleNotificationQueue();
347 /* this is not necesarrily False, because one handler can re-add itself */
348 return (WMGetBagItemCount(idleHandler)>0);
353 void
354 W_CheckTimerHandlers(void)
356 TimerHandler *handler;
357 struct timeval now;
359 if (!timerHandler) {
360 W_FlushASAPNotificationQueue();
361 return;
364 rightNow(&now);
366 handler = timerHandler;
367 while (handler && IS_AFTER(now, handler->when)) {
368 if (!IS_ZERO(handler->when)) {
369 SET_ZERO(handler->when);
370 (*handler->callback)(handler->clientData);
372 handler = handler->next;
375 while (timerHandler && IS_ZERO(timerHandler->when)) {
376 handler = timerHandler;
377 timerHandler = timerHandler->next;
379 if (handler->nextDelay > 0) {
380 handler->when = now;
381 addmillisecs(&handler->when, handler->nextDelay);
382 enqueueTimerHandler(handler);
383 } else {
384 wfree(handler);
388 W_FlushASAPNotificationQueue();
392 Bool
393 W_HaveInputHandlers(void)
395 return (inputHandler && WMGetBagItemCount(inputHandler)>0);
400 * This functions will handle input events on all registered file descriptors.
401 * Input:
402 * - waitForInput - True if we want the function to wait until an event
403 * appears on a file descriptor we watch, False if we
404 * want the function to immediately return if there is
405 * no data available on the file descriptors we watch.
406 * - inputfd - Extra input file descriptor to watch for input.
407 * This is only used when called from wevent.c to watch
408 * on ConnectionNumber(dpy) to avoid blocking of X events
409 * if we wait for input from other file handlers.
410 * Output:
411 * if waitForInput is False, the function will return False if there are no
412 * input handlers registered, or if there is no data
413 * available on the registered ones, and will return True
414 * if there is at least one input handler that has data
415 * available.
416 * if waitForInput is True, the function will return False if there are no
417 * input handlers registered, else it will block until an
418 * event appears on one of the file descriptors it watches
419 * and then it will return True.
421 * If the retured value is True, the input handlers for the corresponding file
422 * descriptors are also called.
424 * Parametersshould be passed like this:
425 * - from wevent.c:
426 * waitForInput = True
427 * inputfd = ConnectionNumber(dpy)
428 * - from wutil.c:
429 * waitForInput - value passed from the function who calls this function
430 * inputfd = -1
433 Bool
434 W_HandleInputEvents(Bool waitForInput, int inputfd)
436 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
437 struct poll fd *fds;
438 InputHandler *handler;
439 int count, timeout, nfds, i, extrafd, retval;
441 extrafd = (inputfd < 0) ? 0 : 1;
443 if (inputHandler)
444 nfds = WMGetBagItemCount(inputHandler);
445 else
446 nfds = 0;
448 if (!extrafd && nfds==0) {
449 W_FlushASAPNotificationQueue();
450 return False;
453 fds = wmalloc((nfds+extrafd) * sizeof(struct pollfd));
454 if (extrafd) {
455 /* put this to the end of array to avoid using ranges from 1 to nfds+1 */
456 fds[nfds].fd = inputfd;
457 fds[nfds].events = POLLIN;
460 for (i = 0; i<nfds; i++) {
461 handler = WMGetFromBag(inputHandler, i);
462 fds[i].fd = handler->fd;
463 fds[i].events = 0;
464 if (handler->mask & WIReadMask)
465 fds[i].events |= POLLIN;
467 if (handler->mask & WIWriteMask)
468 fds[i].events |= POLLOUT;
470 #if 0 /* FIXME */
471 if (handler->mask & WIExceptMask)
472 FD_SET(handler->fd, &eset);
473 #endif
477 * Setup the timeout to the estimated time until the
478 * next timer expires.
480 if (!waitForInput) {
481 timeout = 0;
482 } else if (timerPending()) {
483 struct timeval tv;
484 delayUntilNextTimerEvent(&tv);
485 timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
486 } else {
487 timeout = -1;
490 count = poll(fds, nfds+extrafd, timeout);
492 if (count>0 && nfds>0) {
493 WMBag *handlerCopy = WMCreateBag(nfds);
495 for (i=0; i<nfds; i++)
496 WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
498 for (i=0; i<nfds; i++) {
499 int mask;
501 handler = WMGetFromBag(handlerCopy, i);
502 /* check if the handler still exist or was removed by a callback */
503 if (WMGetFirstInBag(inputHandler, handler) == WBNotFound)
504 continue;
506 mask = 0;
508 if ((handler->mask & WIReadMask) &&
509 (fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)))
510 mask |= WIReadMask;
512 if ((handler->mask & WIWriteMask) &&
513 (fds[i].revents & (POLLOUT | POLLWRBAND)))
514 mask |= WIWriteMask;
516 if ((handler->mask & WIExceptMask) &&
517 (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
518 mask |= WIExceptMask;
520 if (mask!=0 && handler->callback) {
521 (*handler->callback)(handler->fd, mask,
522 handler->clientData);
526 WMFreeBag(handlerCopy);
529 /* we may only return count>0, because the returned value is not checked
530 * anywhere anyway. -Dan
532 retval = ((inputfd < 0) ? (count > 0) :
533 fds[nfds].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI));
535 wfree(fds);
537 W_FlushASAPNotificationQueue();
539 return retval;
540 #else
541 #ifdef HAVE_SELECT
542 struct timeval timeout;
543 struct timeval *timeoutPtr;
544 fd_set rset, wset, eset;
545 int maxfd, nfds, i;
546 int count;
547 InputHandler *handler;
549 if (inputHandler)
550 nfds = WMGetBagItemCount(inputHandler);
551 else
552 nfds = 0;
554 if (inputfd<0 && nfds==0) {
555 W_FlushASAPNotificationQueue();
556 return False;
559 FD_ZERO(&rset);
560 FD_ZERO(&wset);
561 FD_ZERO(&eset);
563 if (inputfd < 0) {
564 maxfd = 0;
565 } else {
566 FD_SET(inputfd, &rset);
567 maxfd = inputfd;
570 for (i=0; i<nfds; i++) {
571 handler = WMGetFromBag(inputHandler, i);
572 if (handler->mask & WIReadMask)
573 FD_SET(handler->fd, &rset);
575 if (handler->mask & WIWriteMask)
576 FD_SET(handler->fd, &wset);
578 if (handler->mask & WIExceptMask)
579 FD_SET(handler->fd, &eset);
581 if (maxfd < handler->fd)
582 maxfd = handler->fd;
586 * Setup the timeout to the estimated time until the
587 * next timer expires.
589 if (!waitForInput) {
590 SET_ZERO(timeout);
591 timeoutPtr = &timeout;
592 } else if (timerPending()) {
593 delayUntilNextTimerEvent(&timeout);
594 timeoutPtr = &timeout;
595 } else {
596 timeoutPtr = (struct timeval*)0;
599 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
601 if (count>0 && nfds>0) {
602 WMBag *handlerCopy = WMCreateBag(nfds);
604 for (i=0; i<nfds; i++)
605 WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
607 for (i=0; i<nfds; i++) {
608 int mask;
610 handler = WMGetFromBag(handlerCopy, i);
611 /* check if the handler still exist or was removed by a callback */
612 if (WMGetFirstInBag(inputHandler, handler) == WBNotFound)
613 continue;
615 mask = 0;
617 if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
618 mask |= WIReadMask;
620 if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
621 mask |= WIWriteMask;
623 if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
624 mask |= WIExceptMask;
626 if (mask!=0 && handler->callback) {
627 (*handler->callback)(handler->fd, mask,
628 handler->clientData);
632 WMFreeBag(handlerCopy);
635 W_FlushASAPNotificationQueue();
637 return ((inputfd < 0) ? (count > 0) : FD_ISSET(inputfd, &rset));
638 #else /* not HAVE_SELECT, not HAVE_POLL */
639 Neither select nor poll. You lose.
640 #endif /* HAVE_SELECT */
641 #endif /* HAVE_POLL */