- added WMRemoveFromArrayMatching(array, match, cdata), which will remove the
[wmaker-crm.git] / WINGs / handlers.c
blob72e884252049d6d6f34957272c91b83022fabcaf
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) {
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;
263 int pos;
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;
298 int pos;
300 if (!handler || !inputHandler)
301 return;
303 WMRemoveFromArray(inputHandler, handler);
307 Bool
308 W_CheckIdleHandlers(void)
310 IdleHandler *handler;
311 WMArray *handlerCopy;
312 WMArrayIterator iter;
314 if (!idleHandler || WMGetArrayItemCount(idleHandler)==0) {
315 W_FlushIdleNotificationQueue();
316 /* make sure an observer in queue didn't added an idle handler */
317 return (idleHandler!=NULL && WMGetArrayItemCount(idleHandler)>0);
320 handlerCopy = WMDuplicateArray(idleHandler);
322 WM_ITERATE_ARRAY(handlerCopy, handler, iter) {
323 /* check if the handler still exist or was removed by a callback */
324 if (WMGetFirstInArray(idleHandler, handler) == WANotFound)
325 continue;
327 (*handler->callback)(handler->clientData);
328 WMDeleteIdleHandler(handler);
331 WMFreeArray(handlerCopy);
333 W_FlushIdleNotificationQueue();
335 /* this is not necesarrily False, because one handler can re-add itself */
336 return (WMGetArrayItemCount(idleHandler)>0);
341 void
342 W_CheckTimerHandlers(void)
344 TimerHandler *handler;
345 struct timeval now;
347 if (!timerHandler) {
348 W_FlushASAPNotificationQueue();
349 return;
352 rightNow(&now);
354 handler = timerHandler;
355 while (handler && IS_AFTER(now, handler->when)) {
356 if (!IS_ZERO(handler->when)) {
357 SET_ZERO(handler->when);
358 (*handler->callback)(handler->clientData);
360 handler = handler->next;
363 while (timerHandler && IS_ZERO(timerHandler->when)) {
364 handler = timerHandler;
365 timerHandler = timerHandler->next;
367 if (handler->nextDelay > 0) {
368 handler->when = now;
369 addmillisecs(&handler->when, handler->nextDelay);
370 enqueueTimerHandler(handler);
371 } else {
372 wfree(handler);
376 W_FlushASAPNotificationQueue();
381 * This functions will handle input events on all registered file descriptors.
382 * Input:
383 * - waitForInput - True if we want the function to wait until an event
384 * appears on a file descriptor we watch, False if we
385 * want the function to immediately return if there is
386 * no data available on the file descriptors we watch.
387 * - inputfd - Extra input file descriptor to watch for input.
388 * This is only used when called from wevent.c to watch
389 * on ConnectionNumber(dpy) to avoid blocking of X events
390 * if we wait for input from other file handlers.
391 * Output:
392 * if waitForInput is False, the function will return False if there are no
393 * input handlers registered, or if there is no data
394 * available on the registered ones, and will return True
395 * if there is at least one input handler that has data
396 * available.
397 * if waitForInput is True, the function will return False if there are no
398 * input handlers registered, else it will block until an
399 * event appears on one of the file descriptors it watches
400 * and then it will return True.
402 * If the retured value is True, the input handlers for the corresponding file
403 * descriptors are also called.
405 * Parametersshould be passed like this:
406 * - from wevent.c:
407 * waitForInput - apropriate value passed by the function who called us
408 * inputfd = ConnectionNumber(dpy)
409 * - from wutil.c:
410 * waitForInput - apropriate value passed by the function who called us
411 * inputfd = -1
414 Bool
415 W_HandleInputEvents(Bool waitForInput, int inputfd)
417 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
418 struct poll fd *fds;
419 InputHandler *handler;
420 int count, timeout, nfds, i, extrafd;
422 extrafd = (inputfd < 0) ? 0 : 1;
424 if (inputHandler)
425 nfds = WMGetArrayItemCount(inputHandler);
426 else
427 nfds = 0;
429 if (!extrafd && nfds==0) {
430 W_FlushASAPNotificationQueue();
431 return False;
434 fds = wmalloc((nfds+extrafd) * sizeof(struct pollfd));
435 if (extrafd) {
436 /* put this to the end of array to avoid using ranges from 1 to nfds+1 */
437 fds[nfds].fd = inputfd;
438 fds[nfds].events = POLLIN;
441 /* use WM_ITERATE_ARRAY() here */
442 for (i = 0; i<nfds; i++) {
443 handler = WMGetFromArray(inputHandler, i);
444 fds[i].fd = handler->fd;
445 fds[i].events = 0;
446 if (handler->mask & WIReadMask)
447 fds[i].events |= POLLIN;
449 if (handler->mask & WIWriteMask)
450 fds[i].events |= POLLOUT;
452 #if 0 /* FIXME */
453 if (handler->mask & WIExceptMask)
454 FD_SET(handler->fd, &eset);
455 #endif
459 * Setup the timeout to the estimated time until the
460 * next timer expires.
462 if (!waitForInput) {
463 timeout = 0;
464 } else if (timerPending()) {
465 struct timeval tv;
466 delayUntilNextTimerEvent(&tv);
467 timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
468 } else {
469 timeout = -1;
472 count = poll(fds, nfds+extrafd, timeout);
474 if (count>0 && nfds>0) {
475 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
476 int mask;
478 /* use WM_ITERATE_ARRAY() here */
479 for (i=0; i<nfds; i++) {
480 handler = WMGetFromArray(handlerCopy, i);
481 /* check if the handler still exist or was removed by a callback */
482 if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
483 continue;
485 mask = 0;
487 if ((handler->mask & WIReadMask) &&
488 (fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)))
489 mask |= WIReadMask;
491 if ((handler->mask & WIWriteMask) &&
492 (fds[i].revents & (POLLOUT | POLLWRBAND)))
493 mask |= WIWriteMask;
495 if ((handler->mask & WIExceptMask) &&
496 (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
497 mask |= WIExceptMask;
499 if (mask!=0 && handler->callback) {
500 (*handler->callback)(handler->fd, mask,
501 handler->clientData);
505 WMFreeArray(handlerCopy);
508 wfree(fds);
510 W_FlushASAPNotificationQueue();
512 return (count > 0);
513 #else
514 #ifdef HAVE_SELECT
515 struct timeval timeout;
516 struct timeval *timeoutPtr;
517 fd_set rset, wset, eset;
518 int maxfd, nfds, i;
519 int count;
520 InputHandler *handler;
522 if (inputHandler)
523 nfds = WMGetArrayItemCount(inputHandler);
524 else
525 nfds = 0;
527 if (inputfd<0 && nfds==0) {
528 W_FlushASAPNotificationQueue();
529 return False;
532 FD_ZERO(&rset);
533 FD_ZERO(&wset);
534 FD_ZERO(&eset);
536 if (inputfd < 0) {
537 maxfd = 0;
538 } else {
539 FD_SET(inputfd, &rset);
540 maxfd = inputfd;
543 /* use WM_ITERATE_ARRAY() here */
544 for (i=0; i<nfds; i++) {
545 handler = WMGetFromArray(inputHandler, i);
546 if (handler->mask & WIReadMask)
547 FD_SET(handler->fd, &rset);
549 if (handler->mask & WIWriteMask)
550 FD_SET(handler->fd, &wset);
552 if (handler->mask & WIExceptMask)
553 FD_SET(handler->fd, &eset);
555 if (maxfd < handler->fd)
556 maxfd = handler->fd;
560 * Setup the timeout to the estimated time until the
561 * next timer expires.
563 if (!waitForInput) {
564 SET_ZERO(timeout);
565 timeoutPtr = &timeout;
566 } else if (timerPending()) {
567 delayUntilNextTimerEvent(&timeout);
568 timeoutPtr = &timeout;
569 } else {
570 timeoutPtr = (struct timeval*)0;
573 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
575 if (count>0 && nfds>0) {
576 WMArray *handlerCopy = WMDuplicateArray(inputHandler);
577 int mask;
579 /* use WM_ITERATE_ARRAY() here */
580 for (i=0; i<nfds; i++) {
581 handler = WMGetFromArray(handlerCopy, i);
582 /* check if the handler still exist or was removed by a callback */
583 if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
584 continue;
586 mask = 0;
588 if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
589 mask |= WIReadMask;
591 if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
592 mask |= WIWriteMask;
594 if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
595 mask |= WIExceptMask;
597 if (mask!=0 && handler->callback) {
598 (*handler->callback)(handler->fd, mask,
599 handler->clientData);
603 WMFreeArray(handlerCopy);
606 W_FlushASAPNotificationQueue();
608 /* --oldway-- return ((inputfd < 0) ? (count > 0) : FD_ISSET(inputfd, &rset));*/
609 return (count > 0);
610 #else /* not HAVE_SELECT, not HAVE_POLL */
611 Neither select nor poll. You lose.
612 #endif /* HAVE_SELECT */
613 #endif /* HAVE_POLL */