/* * WINGs internal handlers: timer, idle and input handlers */ #include "WINGsP.h" #include "../src/config.h" #include #include #include #ifdef HAVE_SYS_SELECT_H # include #endif #include #ifndef X_GETTIMEOFDAY #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0) #endif typedef struct TimerHandler { WMCallback *callback; /* procedure to call */ struct timeval when; /* when to call the callback */ void *clientData; struct TimerHandler *next; int nextDelay; /* 0 if it's one-shot */ } TimerHandler; typedef struct IdleHandler { WMCallback *callback; void *clientData; } IdleHandler; typedef struct InputHandler { WMInputProc *callback; void *clientData; int fd; int mask; } InputHandler; /* queue of timer event handlers */ static TimerHandler *timerHandler=NULL; static WMArray *idleHandler=NULL; static WMArray *inputHandler=NULL; #define timerPending() (timerHandler) static void rightNow(struct timeval *tv) { X_GETTIMEOFDAY(tv); } /* is t1 after t2 ? */ #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \ (((t1).tv_sec == (t2).tv_sec) \ && ((t1).tv_usec > (t2).tv_usec))) #define IS_ZERO(tv) (tv.tv_sec == 0 && tv.tv_usec == 0) #define SET_ZERO(tv) tv.tv_sec = 0, tv.tv_usec = 0 static void addmillisecs(struct timeval *tv, int milliseconds) { tv->tv_usec += milliseconds*1000; tv->tv_sec += tv->tv_usec/1000000; tv->tv_usec = tv->tv_usec%1000000; } static void enqueueTimerHandler(TimerHandler *handler) { TimerHandler *tmp; /* insert callback in queue, sorted by time left */ if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) { /* first in the queue */ handler->next = timerHandler; timerHandler = handler; } else { tmp = timerHandler; while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) { tmp = tmp->next; } handler->next = tmp->next; tmp->next = handler; } } static void delayUntilNextTimerEvent(struct timeval *delay) { struct timeval now; TimerHandler *handler; handler = timerHandler; while (handler && IS_ZERO(handler->when)) handler = handler->next; if (!handler) { /* The return value of this function is only valid if there _are_ timers active. */ delay->tv_sec = 0; delay->tv_usec = 0; return; } rightNow(&now); if (IS_AFTER(now, handler->when)) { delay->tv_sec = 0; delay->tv_usec = 0; } else { delay->tv_sec = handler->when.tv_sec - now.tv_sec; delay->tv_usec = handler->when.tv_usec - now.tv_usec; if (delay->tv_usec < 0) { delay->tv_usec += 1000000; delay->tv_sec--; } } } WMHandlerID WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata) { TimerHandler *handler; handler = malloc(sizeof(TimerHandler)); if (!handler) return NULL; rightNow(&handler->when); addmillisecs(&handler->when, milliseconds); handler->callback = callback; handler->clientData = cdata; handler->nextDelay = 0; enqueueTimerHandler(handler); return handler; } WMHandlerID WMAddPersistentTimerHandler(int milliseconds, WMCallback *callback, void *cdata) { TimerHandler *handler = WMAddTimerHandler(milliseconds, callback, cdata); if (handler != NULL) handler->nextDelay = milliseconds; return handler; } void WMDeleteTimerWithClientData(void *cdata) { TimerHandler *handler, *tmp; if (!cdata || !timerHandler) return; tmp = timerHandler; if (tmp->clientData==cdata) { tmp->nextDelay = 0; if (!IS_ZERO(tmp->when)) { timerHandler = tmp->next; wfree(tmp); } } else { while (tmp->next) { if (tmp->next->clientData==cdata) { handler = tmp->next; handler->nextDelay = 0; if (IS_ZERO(handler->when)) break; tmp->next = handler->next; wfree(handler); break; } tmp = tmp->next; } } } void WMDeleteTimerHandler(WMHandlerID handlerID) { TimerHandler *tmp, *handler=(TimerHandler*)handlerID; if (!handler || !timerHandler) return; tmp = timerHandler; handler->nextDelay = 0; if (IS_ZERO(handler->when)) return; if (tmp==handler) { timerHandler = handler->next; wfree(handler); } else { while (tmp->next) { if (tmp->next==handler) { tmp->next=handler->next; wfree(handler); break; } tmp = tmp->next; } } } WMHandlerID WMAddIdleHandler(WMCallback *callback, void *cdata) { IdleHandler *handler; handler = malloc(sizeof(IdleHandler)); if (!handler) return NULL; handler->callback = callback; handler->clientData = cdata; /* add handler at end of queue */ if (!idleHandler) { idleHandler = WMCreateArrayWithDestructor(16, wfree); } WMAddToArray(idleHandler, handler); return handler; } void WMDeleteIdleHandler(WMHandlerID handlerID) { IdleHandler *handler = (IdleHandler*)handlerID; int pos; if (!handler || !idleHandler) return; WMRemoveFromArray(idleHandler, handler); } WMHandlerID WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData) { InputHandler *handler; handler = wmalloc(sizeof(InputHandler)); handler->fd = fd; handler->mask = condition; handler->callback = proc; handler->clientData = clientData; if (!inputHandler) inputHandler = WMCreateArrayWithDestructor(16, wfree); WMAddToArray(inputHandler, handler); return handler; } void WMDeleteInputHandler(WMHandlerID handlerID) { InputHandler *handler = (InputHandler*)handlerID; int pos; if (!handler || !inputHandler) return; WMRemoveFromArray(inputHandler, handler); } Bool W_CheckIdleHandlers(void) { IdleHandler *handler; WMArray *handlerCopy; WMArrayIterator iter; if (!idleHandler || WMGetArrayItemCount(idleHandler)==0) { W_FlushIdleNotificationQueue(); /* make sure an observer in queue didn't added an idle handler */ return (idleHandler!=NULL && WMGetArrayItemCount(idleHandler)>0); } handlerCopy = WMDuplicateArray(idleHandler); WM_ITERATE_ARRAY(handlerCopy, handler, iter) { /* check if the handler still exist or was removed by a callback */ if (WMGetFirstInArray(idleHandler, handler) == WANotFound) continue; (*handler->callback)(handler->clientData); WMDeleteIdleHandler(handler); } WMFreeArray(handlerCopy); W_FlushIdleNotificationQueue(); /* this is not necesarrily False, because one handler can re-add itself */ return (WMGetArrayItemCount(idleHandler)>0); } void W_CheckTimerHandlers(void) { TimerHandler *handler; struct timeval now; if (!timerHandler) { W_FlushASAPNotificationQueue(); return; } rightNow(&now); handler = timerHandler; while (handler && IS_AFTER(now, handler->when)) { if (!IS_ZERO(handler->when)) { SET_ZERO(handler->when); (*handler->callback)(handler->clientData); } handler = handler->next; } while (timerHandler && IS_ZERO(timerHandler->when)) { handler = timerHandler; timerHandler = timerHandler->next; if (handler->nextDelay > 0) { handler->when = now; addmillisecs(&handler->when, handler->nextDelay); enqueueTimerHandler(handler); } else { wfree(handler); } } W_FlushASAPNotificationQueue(); } /* * This functions will handle input events on all registered file descriptors. * Input: * - waitForInput - True if we want the function to wait until an event * appears on a file descriptor we watch, False if we * want the function to immediately return if there is * no data available on the file descriptors we watch. * - inputfd - Extra input file descriptor to watch for input. * This is only used when called from wevent.c to watch * on ConnectionNumber(dpy) to avoid blocking of X events * if we wait for input from other file handlers. * Output: * if waitForInput is False, the function will return False if there are no * input handlers registered, or if there is no data * available on the registered ones, and will return True * if there is at least one input handler that has data * available. * if waitForInput is True, the function will return False if there are no * input handlers registered, else it will block until an * event appears on one of the file descriptors it watches * and then it will return True. * * If the retured value is True, the input handlers for the corresponding file * descriptors are also called. * * Parametersshould be passed like this: * - from wevent.c: * waitForInput - apropriate value passed by the function who called us * inputfd = ConnectionNumber(dpy) * - from wutil.c: * waitForInput - apropriate value passed by the function who called us * inputfd = -1 * */ Bool W_HandleInputEvents(Bool waitForInput, int inputfd) { #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT) struct poll fd *fds; InputHandler *handler; int count, timeout, nfds, i, extrafd; extrafd = (inputfd < 0) ? 0 : 1; if (inputHandler) nfds = WMGetArrayItemCount(inputHandler); else nfds = 0; if (!extrafd && nfds==0) { W_FlushASAPNotificationQueue(); return False; } fds = wmalloc((nfds+extrafd) * sizeof(struct pollfd)); if (extrafd) { /* put this to the end of array to avoid using ranges from 1 to nfds+1 */ fds[nfds].fd = inputfd; fds[nfds].events = POLLIN; } /* use WM_ITERATE_ARRAY() here */ for (i = 0; ifd; fds[i].events = 0; if (handler->mask & WIReadMask) fds[i].events |= POLLIN; if (handler->mask & WIWriteMask) fds[i].events |= POLLOUT; #if 0 /* FIXME */ if (handler->mask & WIExceptMask) FD_SET(handler->fd, &eset); #endif } /* * Setup the timeout to the estimated time until the * next timer expires. */ if (!waitForInput) { timeout = 0; } else if (timerPending()) { struct timeval tv; delayUntilNextTimerEvent(&tv); timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000; } else { timeout = -1; } count = poll(fds, nfds+extrafd, timeout); if (count>0 && nfds>0) { WMArray *handlerCopy = WMDuplicateArray(inputHandler); int mask; /* use WM_ITERATE_ARRAY() here */ for (i=0; imask & WIReadMask) && (fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI))) mask |= WIReadMask; if ((handler->mask & WIWriteMask) && (fds[i].revents & (POLLOUT | POLLWRBAND))) mask |= WIWriteMask; if ((handler->mask & WIExceptMask) && (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR))) mask |= WIExceptMask; if (mask!=0 && handler->callback) { (*handler->callback)(handler->fd, mask, handler->clientData); } } WMFreeArray(handlerCopy); } wfree(fds); W_FlushASAPNotificationQueue(); return (count > 0); #else #ifdef HAVE_SELECT struct timeval timeout; struct timeval *timeoutPtr; fd_set rset, wset, eset; int maxfd, nfds, i; int count; InputHandler *handler; if (inputHandler) nfds = WMGetArrayItemCount(inputHandler); else nfds = 0; if (inputfd<0 && nfds==0) { W_FlushASAPNotificationQueue(); return False; } FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); if (inputfd < 0) { maxfd = 0; } else { FD_SET(inputfd, &rset); maxfd = inputfd; } /* use WM_ITERATE_ARRAY() here */ for (i=0; imask & WIReadMask) FD_SET(handler->fd, &rset); if (handler->mask & WIWriteMask) FD_SET(handler->fd, &wset); if (handler->mask & WIExceptMask) FD_SET(handler->fd, &eset); if (maxfd < handler->fd) maxfd = handler->fd; } /* * Setup the timeout to the estimated time until the * next timer expires. */ if (!waitForInput) { SET_ZERO(timeout); timeoutPtr = &timeout; } else if (timerPending()) { delayUntilNextTimerEvent(&timeout); timeoutPtr = &timeout; } else { timeoutPtr = (struct timeval*)0; } count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr); if (count>0 && nfds>0) { WMArray *handlerCopy = WMDuplicateArray(inputHandler); int mask; /* use WM_ITERATE_ARRAY() here */ for (i=0; imask & WIReadMask) && FD_ISSET(handler->fd, &rset)) mask |= WIReadMask; if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset)) mask |= WIWriteMask; if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset)) mask |= WIExceptMask; if (mask!=0 && handler->callback) { (*handler->callback)(handler->fd, mask, handler->clientData); } } WMFreeArray(handlerCopy); } W_FlushASAPNotificationQueue(); /* --oldway-- return ((inputfd < 0) ? (count > 0) : FD_ISSET(inputfd, &rset));*/ return (count > 0); #else /* not HAVE_SELECT, not HAVE_POLL */ Neither select nor poll. You lose. #endif /* HAVE_SELECT */ #endif /* HAVE_POLL */ }