changed indentation to use spaces only
[wmaker-crm.git] / WINGs / handlers.c
blobf6bb26ee147fb8e66e54db32413605c56a6be72a
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 WMArray *idleHandler=NULL;
56 static WMArray *inputHandler=NULL;
58 #define timerPending() (timerHandler)
62 static void
63 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 = WMCreateArrayWithDestructor(16, wfree);
254 WMAddToArray(idleHandler, handler);
256 return handler;
260 void
261 WMDeleteIdleHandler(WMHandlerID handlerID)
263 IdleHandler *handler = (IdleHandler*)handlerID;
265 if (!handler || !idleHandler)
266 return;
268 WMRemoveFromArray(idleHandler, handler);
273 WMHandlerID
274 WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
276 InputHandler *handler;
278 handler = wmalloc(sizeof(InputHandler));
280 handler->fd = fd;
281 handler->mask = condition;
282 handler->callback = proc;
283 handler->clientData = clientData;
285 if (!inputHandler)
286 inputHandler = WMCreateArrayWithDestructor(16, wfree);
287 WMAddToArray(inputHandler, handler);
289 return handler;
294 void
295 WMDeleteInputHandler(WMHandlerID handlerID)
297 InputHandler *handler = (InputHandler*)handlerID;
299 if (!handler || !inputHandler)
300 return;
302 WMRemoveFromArray(inputHandler, handler);
306 Bool
307 W_CheckIdleHandlers(void)
309 IdleHandler *handler;
310 WMArray *handlerCopy;
311 WMArrayIterator iter;
313 if (!idleHandler || WMGetArrayItemCount(idleHandler)==0) {
314 W_FlushIdleNotificationQueue();
315 /* make sure an observer in queue didn't added an idle handler */
316 return (idleHandler!=NULL && WMGetArrayItemCount(idleHandler)>0);
319 handlerCopy = WMDuplicateArray(idleHandler);
321 WM_ITERATE_ARRAY(handlerCopy, handler, iter) {
322 /* check if the handler still exist or was removed by a callback */
323 if (WMGetFirstInArray(idleHandler, handler) == WANotFound)
324 continue;
326 (*handler->callback)(handler->clientData);
327 WMDeleteIdleHandler(handler);
330 WMFreeArray(handlerCopy);
332 W_FlushIdleNotificationQueue();
334 /* this is not necesarrily False, because one handler can re-add itself */
335 return (WMGetArrayItemCount(idleHandler)>0);
340 void
341 W_CheckTimerHandlers(void)
343 TimerHandler *handler;
344 struct timeval now;
346 if (!timerHandler) {
347 W_FlushASAPNotificationQueue();
348 return;
351 rightNow(&now);
353 handler = timerHandler;
354 while (handler && IS_AFTER(now, handler->when)) {
355 if (!IS_ZERO(handler->when)) {
356 SET_ZERO(handler->when);
357 (*handler->callback)(handler->clientData);
359 handler = handler->next;
362 while (timerHandler && IS_ZERO(timerHandler->when)) {
363 handler = timerHandler;
364 timerHandler = timerHandler->next;
366 if (handler->nextDelay > 0) {
367 handler->when = now;
368 addmillisecs(&handler->when, handler->nextDelay);
369 enqueueTimerHandler(handler);
370 } else {
371 wfree(handler);
375 W_FlushASAPNotificationQueue();
380 * This functions will handle input events on all registered file descriptors.
381 * Input:
382 * - waitForInput - True if we want the function to wait until an event
383 * appears on a file descriptor we watch, False if we
384 * want the function to immediately return if there is
385 * no data available on the file descriptors we watch.
386 * - inputfd - Extra input file descriptor to watch for input.
387 * This is only used when called from wevent.c to watch
388 * on ConnectionNumber(dpy) to avoid blocking of X events
389 * if we wait for input from other file handlers.
390 * Output:
391 * if waitForInput is False, the function will return False if there are no
392 * input handlers registered, or if there is no data
393 * available on the registered ones, and will return True
394 * if there is at least one input handler that has data
395 * available.
396 * if waitForInput is True, the function will return False if there are no
397 * input handlers registered, else it will block until an
398 * event appears on one of the file descriptors it watches
399 * and then it will return True.
401 * If the retured value is True, the input handlers for the corresponding file
402 * descriptors are also called.
404 * Parametersshould be passed like this:
405 * - from wevent.c:
406 * waitForInput - apropriate value passed by the function who called us
407 * inputfd = ConnectionNumber(dpy)
408 * - from wutil.c:
409 * waitForInput - apropriate value passed by the function who called us
410 * inputfd = -1
413 Bool
414 W_HandleInputEvents(Bool waitForInput, int inputfd)
416 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
417 struct poll fd *fds;
418 InputHandler *handler;
419 int count, timeout, nfds, i, extrafd;
421 extrafd = (inputfd < 0) ? 0 : 1;
423 if (inputHandler)
424 nfds = WMGetArrayItemCount(inputHandler);
425 else
426 nfds = 0;
428 if (!extrafd && nfds==0) {
429 W_FlushASAPNotificationQueue();
430 return False;
433 fds = wmalloc((nfds+extrafd) * sizeof(struct pollfd));
434 if (extrafd) {
435 /* put this to the end of array to avoid using ranges from 1 to nfds+1 */
436 fds[nfds].fd = inputfd;
437 fds[nfds].events = POLLIN;
440 /* use WM_ITERATE_ARRAY() here */
441 for (i = 0; i<nfds; i++) {
442 handler = WMGetFromArray(inputHandler, i);
443 fds[i].fd = handler->fd;
444 fds[i].events = 0;
445 if (handler->mask & WIReadMask)
446 fds[i].events |= POLLIN;
448 if (handler->mask & WIWriteMask)
449 fds[i].events |= POLLOUT;
451 #if 0 /* FIXME */
452 if (handler->mask & WIExceptMask)
453 FD_SET(handler->fd, &eset);
454 #endif
458 * Setup the timeout to the estimated time until the
459 * next timer expires.
461 if (!waitForInput) {
462 timeout = 0;
463 } else if (timerPending()) {
464 struct timeval tv;
465 delayUntilNextTimerEvent(&tv);
466 timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
467 } else {
468 timeout = -1;
471 count = poll(fds, nfds+extrafd, timeout);
473 if (count>0 && nfds>0) {
474 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
475 int mask;
477 /* use WM_ITERATE_ARRAY() here */
478 for (i=0; i<nfds; i++) {
479 handler = WMGetFromArray(handlerCopy, i);
480 /* check if the handler still exist or was removed by a callback */
481 if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
482 continue;
484 mask = 0;
486 if ((handler->mask & WIReadMask) &&
487 (fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)))
488 mask |= WIReadMask;
490 if ((handler->mask & WIWriteMask) &&
491 (fds[i].revents & (POLLOUT | POLLWRBAND)))
492 mask |= WIWriteMask;
494 if ((handler->mask & WIExceptMask) &&
495 (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
496 mask |= WIExceptMask;
498 if (mask!=0 && handler->callback) {
499 (*handler->callback)(handler->fd, mask,
500 handler->clientData);
504 WMFreeArray(handlerCopy);
507 wfree(fds);
509 W_FlushASAPNotificationQueue();
511 return (count > 0);
512 #else
513 #ifdef HAVE_SELECT
514 struct timeval timeout;
515 struct timeval *timeoutPtr;
516 fd_set rset, wset, eset;
517 int maxfd, nfds, i;
518 int count;
519 InputHandler *handler;
521 if (inputHandler)
522 nfds = WMGetArrayItemCount(inputHandler);
523 else
524 nfds = 0;
526 if (inputfd<0 && nfds==0) {
527 W_FlushASAPNotificationQueue();
528 return False;
531 FD_ZERO(&rset);
532 FD_ZERO(&wset);
533 FD_ZERO(&eset);
535 if (inputfd < 0) {
536 maxfd = 0;
537 } else {
538 FD_SET(inputfd, &rset);
539 maxfd = inputfd;
542 /* use WM_ITERATE_ARRAY() here */
543 for (i=0; i<nfds; i++) {
544 handler = WMGetFromArray(inputHandler, i);
545 if (handler->mask & WIReadMask)
546 FD_SET(handler->fd, &rset);
548 if (handler->mask & WIWriteMask)
549 FD_SET(handler->fd, &wset);
551 if (handler->mask & WIExceptMask)
552 FD_SET(handler->fd, &eset);
554 if (maxfd < handler->fd)
555 maxfd = handler->fd;
559 * Setup the timeout to the estimated time until the
560 * next timer expires.
562 if (!waitForInput) {
563 SET_ZERO(timeout);
564 timeoutPtr = &timeout;
565 } else if (timerPending()) {
566 delayUntilNextTimerEvent(&timeout);
567 timeoutPtr = &timeout;
568 } else {
569 timeoutPtr = (struct timeval*)0;
572 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
574 if (count>0 && nfds>0) {
575 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
576 int mask;
578 /* use WM_ITERATE_ARRAY() here */
579 for (i=0; i<nfds; i++) {
580 handler = WMGetFromArray(handlerCopy, i);
581 /* check if the handler still exist or was removed by a callback */
582 if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
583 continue;
585 mask = 0;
587 if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
588 mask |= WIReadMask;
590 if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
591 mask |= WIWriteMask;
593 if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
594 mask |= WIExceptMask;
596 if (mask!=0 && handler->callback) {
597 (*handler->callback)(handler->fd, mask,
598 handler->clientData);
602 WMFreeArray(handlerCopy);
605 W_FlushASAPNotificationQueue();
607 return (count > 0);
608 #else /* not HAVE_SELECT, not HAVE_POLL */
609 # error Neither select nor poll. You lose.
610 #endif /* HAVE_SELECT */
611 #endif /* HAVE_POLL */