fixed bugs of resized widgets
[wmaker-crm.git] / WINGs / wutil.c
blobfa7c7716ee4edb6c12ddd3674cc1742450df8b46
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 } IdleHandler;
49 typedef struct InputHandler {
50 WMInputProc *callback;
51 void *clientData;
52 int fd;
53 int mask;
54 } InputHandler;
57 /* queue of timer event handlers */
58 static TimerHandler *timerHandler=NULL;
60 static WMBag *idleHandler=NULL;
62 static WMBag *inputHandler=NULL;
66 #define timerPending() (timerHandler)
69 static void
70 rightNow(struct timeval *tv) {
71 X_GETTIMEOFDAY(tv);
74 /* is t1 after t2 ? */
75 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
76 (((t1).tv_sec == (t2).tv_sec) \
77 && ((t1).tv_usec > (t2).tv_usec)))
80 static void
81 addmillisecs(struct timeval *tv, int milliseconds)
83 tv->tv_usec += milliseconds*1000;
85 tv->tv_sec += tv->tv_usec/1000000;
86 tv->tv_usec = tv->tv_usec%1000000;
90 WMHandlerID
91 WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
93 TimerHandler *handler, *tmp;
95 handler = malloc(sizeof(TimerHandler));
96 if (!handler)
97 return NULL;
99 rightNow(&handler->when);
100 addmillisecs(&handler->when, milliseconds);
101 handler->callback = callback;
102 handler->clientData = cdata;
103 /* insert callback in queue, sorted by time left */
104 if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
105 /* first in the queue */
106 handler->next = timerHandler;
107 timerHandler = handler;
108 } else {
109 tmp = timerHandler;
110 while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
111 tmp = tmp->next;
113 handler->next = tmp->next;
114 tmp->next = handler;
116 return handler;
121 void
122 WMDeleteTimerWithClientData(void *cdata)
124 TimerHandler *handler, *tmp;
126 if (!cdata || !timerHandler)
127 return;
129 tmp = timerHandler;
130 if (tmp->clientData==cdata) {
131 timerHandler = tmp->next;
132 wfree(tmp);
133 } else {
134 while (tmp->next) {
135 if (tmp->next->clientData==cdata) {
136 handler = tmp->next;
137 tmp->next = handler->next;
138 wfree(handler);
139 break;
141 tmp = tmp->next;
148 void
149 WMDeleteTimerHandler(WMHandlerID handlerID)
151 TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
153 if (!handler || !timerHandler)
154 return;
156 tmp = timerHandler;
157 if (tmp==handler) {
158 timerHandler = handler->next;
159 wfree(handler);
160 } else {
161 while (tmp->next) {
162 if (tmp->next==handler) {
163 tmp->next=handler->next;
164 wfree(handler);
165 break;
167 tmp = tmp->next;
174 WMHandlerID
175 WMAddIdleHandler(WMCallback *callback, void *cdata)
177 IdleHandler *handler;
179 handler = malloc(sizeof(IdleHandler));
180 if (!handler)
181 return NULL;
183 handler->callback = callback;
184 handler->clientData = cdata;
185 /* add handler at end of queue */
186 if (!idleHandler) {
187 idleHandler = WMCreateBag(16);
189 WMPutInBag(idleHandler, handler);
191 return handler;
196 void
197 WMDeleteIdleHandler(WMHandlerID handlerID)
199 IdleHandler *handler = (IdleHandler*)handlerID;
200 int pos;
202 if (!handler || !idleHandler)
203 return;
205 pos = WMGetFirstInBag(idleHandler, handler);
206 if (pos >= 0) {
207 wfree(handler);
208 WMDeleteFromBag(idleHandler, pos);
214 WMHandlerID
215 WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
217 InputHandler *handler;
219 handler = wmalloc(sizeof(InputHandler));
221 handler->fd = fd;
222 handler->mask = condition;
223 handler->callback = proc;
224 handler->clientData = clientData;
226 if (!inputHandler)
227 inputHandler = WMCreateBag(16);
228 WMPutInBag(inputHandler, handler);
230 return handler;
235 void
236 WMDeleteInputHandler(WMHandlerID handlerID)
238 InputHandler *handler = (InputHandler*)handlerID;
239 int pos;
241 if (!handler || !inputHandler)
242 return;
244 pos = WMGetFirstInBag(inputHandler, handler);
245 if (pos >= 0) {
246 wfree(handler);
247 WMDeleteFromBag(inputHandler, pos);
253 static Bool
254 checkIdleHandlers()
256 IdleHandler *handler;
257 WMBag *handlerCopy;
258 int i, n;
260 if (!idleHandler || WMGetBagItemCount(idleHandler)==0) {
261 W_FlushIdleNotificationQueue();
262 /* make sure an observer in queue didn't added an idle handler */
263 return (idleHandler!=NULL && WMGetBagItemCount(idleHandler)>0);
266 n = WMGetBagItemCount(idleHandler);
267 handlerCopy = WMCreateBag(n);
268 for (i=0; i<n; i++)
269 WMPutInBag(handlerCopy, WMGetFromBag(idleHandler, i));
271 for (i=0; i<n; i++) {
272 handler = WMGetFromBag(handlerCopy, i);
273 /* check if the handler still exist or was removed by a callback */
274 if (WMGetFirstInBag(idleHandler, handler)<0)
275 continue;
277 (*handler->callback)(handler->clientData);
278 WMDeleteIdleHandler(handler);
279 wfree(handler);
282 WMFreeBag(handlerCopy);
284 W_FlushIdleNotificationQueue();
286 /* this is not necesarrily False, because one handler can re-add itself */
287 return (WMGetBagItemCount(idleHandler)>0);
292 static void
293 checkTimerHandlers()
295 TimerHandler *handler;
296 struct timeval now;
298 if (!timerHandler) {
299 W_FlushASAPNotificationQueue();
300 return;
303 rightNow(&now);
305 while (timerHandler && IS_AFTER(now, timerHandler->when)) {
306 handler = timerHandler;
307 timerHandler = timerHandler->next;
308 handler->next = NULL;
309 (*handler->callback)(handler->clientData);
310 wfree(handler);
313 W_FlushASAPNotificationQueue();
318 static void
319 delayUntilNextTimerEvent(struct timeval *delay)
321 struct timeval now;
323 if (!timerHandler) {
324 /* The return value of this function is only valid if there _are_
325 * active timers. */
326 delay->tv_sec = 0;
327 delay->tv_usec = 0;
328 return;
331 rightNow(&now);
332 if (IS_AFTER(now, timerHandler->when)) {
333 delay->tv_sec = 0;
334 delay->tv_usec = 0;
335 } else {
336 delay->tv_sec = timerHandler->when.tv_sec - now.tv_sec;
337 delay->tv_usec = timerHandler->when.tv_usec - now.tv_usec;
338 if (delay->tv_usec < 0) {
339 delay->tv_usec += 1000000;
340 delay->tv_sec--;
347 * This functions will handle input events on all registered file descriptors.
348 * Input:
349 * - waitForInput - True if we want the function to wait until an event
350 * appears on a file descriptor we watch, False if we
351 * want the function to immediately return if there is
352 * no data available on the file descriptors we watch.
353 * Output:
354 * if waitForInput is False, the function will return False if there are no
355 * input handlers registered, or if there is no data
356 * available on the registered ones, and will return True
357 * if there is at least one input handler that has data
358 * available.
359 * if waitForInput is True, the function will return False if there are no
360 * input handlers registered, else it will block until an
361 * event appears on one of the file descriptors it watches
362 * and then it will return True.
364 * If the retured value is True, the input handlers for the corresponding file
365 * descriptors are also called.
368 static Bool
369 handleInputEvents(Bool waitForInput)
371 #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
372 struct pollfd *fds;
373 InputHandler *handler;
374 int count, timeout, nfds, i;
376 if (!inputHandler || (nfds=WMGetBagItemCount(inputHandler))==0) {
377 W_FlushASAPNotificationQueue();
378 return False;
381 fds = wmalloc(nfds * sizeof(struct pollfd));
383 for (i = 0; i<nfds; i++) {
384 handler = WMGetFromBag(inputHandler, i);
385 fds[i].fd = handler->fd;
386 fds[i].events = 0;
387 if (handler->mask & WIReadMask)
388 fds[i].events |= POLLIN;
390 if (handler->mask & WIWriteMask)
391 fds[i].events |= POLLOUT;
393 #if 0 /* FIXME */
394 if (handler->mask & WIExceptMask)
395 FD_SET(handler->fd, &eset);
396 #endif
400 * If we don't wait for input, set timeout to return immediately,
401 * else setup the timeout to the estimated time until the
402 * next timer expires or if no timer is pending to infinite.
404 if (!waitForInput) {
405 timeout = 0;
406 } else if (timerPending()) {
407 struct timeval tv;
408 delayUntilNextTimerEvent(&tv);
409 timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
410 } else {
411 timeout = -1;
414 count = poll(fds, nfds, timeout);
416 if (count > 0) {
417 WMBag *handlerCopy = WMCreateBag(nfds);
419 for (i=0; i<nfds, i++)
420 WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
422 for (i=0; i<nfds; i++) {
423 int mask;
425 handler = WMGetFromBag(handlerCopy, i);
426 /* check if the handler still exist or was removed by a callback */
427 if (WMGetFirstInBag(inputHandler, handler)<0)
428 continue;
430 mask = 0;
432 if ((handler->mask & WIReadMask) &&
433 (fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)))
434 mask |= WIReadMask;
436 if ((handler->mask & WIWriteMask) &&
437 (fds[i].revents & (POLLOUT | POLLWRBAND)))
438 mask |= WIWriteMask;
440 if ((handler->mask & WIExceptMask) &&
441 (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
442 mask |= WIExceptMask;
444 if (mask!=0 && handler->callback) {
445 (*handler->callback)(handler->fd, mask,
446 handler->clientData);
450 WMFreeBag(handlerCopy);
453 wfree(fds);
455 W_FlushASAPNotificationQueue();
457 return (count > 0);
458 #else /* not HAVE_POLL */
459 #ifdef HAVE_SELECT
460 struct timeval timeout;
461 struct timeval *timeoutPtr;
462 fd_set rset, wset, eset;
463 int maxfd, nfds, i;
464 int count;
465 InputHandler *handler;
467 if (!inputHandler || (nfds=WMGetBagItemCount(inputHandler))==0) {
468 W_FlushASAPNotificationQueue();
469 return False;
472 FD_ZERO(&rset);
473 FD_ZERO(&wset);
474 FD_ZERO(&eset);
476 maxfd = 0;
478 for (i=0; i<nfds; i++) {
479 handler = WMGetFromBag(inputHandler, i);
480 if (handler->mask & WIReadMask)
481 FD_SET(handler->fd, &rset);
483 if (handler->mask & WIWriteMask)
484 FD_SET(handler->fd, &wset);
486 if (handler->mask & WIExceptMask)
487 FD_SET(handler->fd, &eset);
489 if (maxfd < handler->fd)
490 maxfd = handler->fd;
494 * If we don't wait for input, set timeout to return immediately,
495 * else setup the timeout to the estimated time until the
496 * next timer expires or if no timer is pending to infinite.
498 if (!waitForInput) {
499 timeout.tv_sec = 0;
500 timeout.tv_usec = 0;
501 timeoutPtr = &timeout;
502 } else if (timerPending()) {
503 delayUntilNextTimerEvent(&timeout);
504 timeoutPtr = &timeout;
505 } else {
506 timeoutPtr = (struct timeval*)0;
509 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
511 if (count > 0) {
512 WMBag *handlerCopy = WMCreateBag(nfds);
514 for (i=0; i<nfds; i++)
515 WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
517 for (i=0; i<nfds; i++) {
518 int mask;
520 handler = WMGetFromBag(handlerCopy, i);
521 /* check if the handler still exist or was removed by a callback */
522 if (WMGetFirstInBag(inputHandler, handler)<0)
523 continue;
525 mask = 0;
527 if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
528 mask |= WIReadMask;
530 if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
531 mask |= WIWriteMask;
533 if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
534 mask |= WIExceptMask;
536 if (mask!=0 && handler->callback) {
537 (*handler->callback)(handler->fd, mask,
538 handler->clientData);
542 WMFreeBag(handlerCopy);
545 W_FlushASAPNotificationQueue();
547 return (count > 0);
548 #else /* not HAVE_SELECT, not HAVE_POLL */
549 Neither select nor poll. You lose.
550 #endif /* HAVE_SELECT */
551 #endif /* HAVE_POLL */
555 void
556 WHandleEvents()
558 /* Check any expired timers */
559 checkTimerHandlers();
561 /* We need to make sure that we have some input handler before calling
562 * checkIdleHandlers() in a while loop, because else the while loop
563 * can run forever (if some idle handler reinitiates itself).
565 if (inputHandler && WMGetBagItemCount(inputHandler)>0) {
566 /* Do idle and timer stuff while there are no input events */
567 /* Check again if there are still input handlers, because some idle
568 * handler could have removed them */
569 while (checkIdleHandlers() && WMGetBagItemCount(inputHandler)>0 &&
570 !handleInputEvents(False)) {
571 /* dispatch timer events */
572 checkTimerHandlers();
574 } else {
575 checkIdleHandlers();
576 /* dispatch timer events */
577 checkTimerHandlers();
580 handleInputEvents(True);
582 /* Check any expired timers */
583 checkTimerHandlers();