Updating to version 0.20.2
[wmaker-crm.git] / WINGs / wevent.c
blob6c992e63459bb5aa9b334931cf6835bf8f37938e
3 /*
4 * This event handling stuff was based on Tk.
5 */
7 #include "WINGsP.h"
9 #include "../src/config.h"
11 #include <sys/types.h>
12 #include <unistd.h>
14 #ifdef HAVE_SYS_SELECT_H
15 # include <sys/select.h>
16 #endif
18 #ifdef HAVE_GETTIMEOFDAY
19 # include <sys/time.h>
20 # ifdef TIME_WITH_SYS_TIME
21 # include <time.h>
22 # endif
23 #else /* ! HAVE_GETTIMEOFDAY */
24 # include <time.h>
25 #endif /* ! HAVE_GETTIMEOFDAY */
28 extern _WINGsConfiguration WINGsConfiguration;
32 typedef struct TimerHandler {
33 WMCallback *callback; /* procedure to call */
34 struct timeval when; /* when to call the callback */
35 void *clientData;
36 struct TimerHandler *next;
37 } TimerHandler;
40 typedef struct IdleHandler {
41 WMCallback *callback;
42 void *clientData;
43 struct IdleHandler *next;
44 } IdleHandler;
47 typedef struct InputHandler {
48 WMInputProc *callback;
49 void *clientData;
50 int fd;
51 int mask;
52 struct InputHandler *next;
53 } InputHandler;
56 /* table to map event types to event masks */
57 static unsigned long eventMasks[] = {
60 KeyPressMask, /* KeyPress */
61 KeyReleaseMask, /* KeyRelease */
62 ButtonPressMask, /* ButtonPress */
63 ButtonReleaseMask, /* ButtonRelease */
64 PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
65 |Button1MotionMask|Button2MotionMask|Button3MotionMask
66 |Button4MotionMask|Button5MotionMask,
67 /* MotionNotify */
68 EnterWindowMask, /* EnterNotify */
69 LeaveWindowMask, /* LeaveNotify */
70 FocusChangeMask, /* FocusIn */
71 FocusChangeMask, /* FocusOut */
72 KeymapStateMask, /* KeymapNotify */
73 ExposureMask, /* Expose */
74 ExposureMask, /* GraphicsExpose */
75 ExposureMask, /* NoExpose */
76 VisibilityChangeMask, /* VisibilityNotify */
77 SubstructureNotifyMask, /* CreateNotify */
78 StructureNotifyMask, /* DestroyNotify */
79 StructureNotifyMask, /* UnmapNotify */
80 StructureNotifyMask, /* MapNotify */
81 SubstructureRedirectMask, /* MapRequest */
82 StructureNotifyMask, /* ReparentNotify */
83 StructureNotifyMask, /* ConfigureNotify */
84 SubstructureRedirectMask, /* ConfigureRequest */
85 StructureNotifyMask, /* GravityNotify */
86 ResizeRedirectMask, /* ResizeRequest */
87 StructureNotifyMask, /* CirculateNotify */
88 SubstructureRedirectMask, /* CirculateRequest */
89 PropertyChangeMask, /* PropertyNotify */
90 0, /* SelectionClear */
91 0, /* SelectionRequest */
92 0, /* SelectionNotify */
93 ColormapChangeMask, /* ColormapNotify */
94 ClientMessageMask, /* ClientMessage */
95 0, /* Mapping Notify */
100 /* queue of timer event handlers */
101 static TimerHandler *timerHandler=NULL;
103 static IdleHandler *idleHandler=NULL;
105 static InputHandler *inputHandler=NULL;
107 /* hook for other toolkits or wmaker process their events */
108 static WMEventHook *extraEventHandler=NULL;
112 #define timerPending() (timerHandler)
114 #define idlePending() (idleHandler)
117 #ifdef HAVE_GETTIMEOFDAY
118 static void
119 rightNow(struct timeval *tv) {
120 gettimeofday(tv, NULL);
122 #else /* !HAVE_GETTIMEOFDAY */
123 # define rightNow(tv) (tv)->tv_sec==time(NULL),(tv)->tv_usec=0
124 #endif /* !HAVE_GETTIMEOFDAY */
127 /* is t1 after t2 ? */
128 #define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
129 (((t1).tv_sec == (t2).tv_sec) \
130 && ((t1).tv_usec > (t2).tv_usec)))
133 static void
134 addmillisecs(struct timeval *tv, int milliseconds)
136 tv->tv_usec += milliseconds*1000;
138 tv->tv_sec += tv->tv_usec/1000000;
139 tv->tv_usec = tv->tv_usec%1000000;
143 WMHandlerID
144 WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
146 TimerHandler *handler, *tmp;
148 handler = malloc(sizeof(TimerHandler));
149 if (!handler)
150 return NULL;
152 rightNow(&handler->when);
153 addmillisecs(&handler->when, milliseconds);
154 handler->callback = callback;
155 handler->clientData = cdata;
156 /* insert callback in queue, sorted by time left */
157 if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
158 /* first in the queue */
159 handler->next = timerHandler;
160 timerHandler = handler;
161 } else {
162 tmp = timerHandler;
163 while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
164 tmp = tmp->next;
166 handler->next = tmp->next;
167 tmp->next = handler;
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 timerHandler = tmp->next;
185 free(tmp);
186 } else {
187 while (tmp->next) {
188 if (tmp->next->clientData==cdata) {
189 handler = tmp->next;
190 tmp->next = handler->next;
191 free(handler);
192 break;
194 tmp = tmp->next;
201 void
202 WMDeleteTimerHandler(WMHandlerID handlerID)
204 TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
206 if (!handler || !timerHandler)
207 return;
209 tmp = timerHandler;
210 if (tmp==handler) {
211 timerHandler = handler->next;
212 free(handler);
213 } else {
214 while (tmp->next) {
215 if (tmp->next==handler) {
216 tmp->next=handler->next;
217 free(handler);
218 break;
220 tmp = tmp->next;
227 WMHandlerID
228 WMAddIdleHandler(WMCallback *callback, void *cdata)
230 IdleHandler *handler, *tmp;
232 handler = malloc(sizeof(IdleHandler));
233 if (!handler)
234 return NULL;
236 handler->callback = callback;
237 handler->clientData = cdata;
238 handler->next = NULL;
239 /* add callback at end of queue */
240 if (!idleHandler) {
241 idleHandler = handler;
242 } else {
243 tmp = idleHandler;
244 while (tmp->next) {
245 tmp = tmp->next;
247 tmp->next = handler;
250 return handler;
255 void
256 WMDeleteIdleHandler(WMHandlerID handlerID)
258 IdleHandler *tmp, *handler = (IdleHandler*)handlerID;
260 if (!handler || !idleHandler)
261 return;
263 tmp = idleHandler;
264 if (tmp == handler) {
265 idleHandler = handler->next;
266 free(handler);
267 } else {
268 while (tmp->next) {
269 if (tmp->next == handler) {
270 tmp->next = handler->next;
271 free(handler);
272 break;
274 tmp = tmp->next;
281 WMHandlerID
282 WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
284 InputHandler *handler;
286 handler = wmalloc(sizeof(InputHandler));
288 handler->fd = fd;
289 handler->mask = condition;
290 handler->callback = proc;
291 handler->clientData = clientData;
293 handler->next = inputHandler;
295 inputHandler = handler;
297 return handler;
301 void
302 WMDeleteInputHandler(WMHandlerID handlerID)
304 InputHandler *tmp, *handler = (InputHandler*)handlerID;
306 if (!handler || !inputHandler)
307 return;
309 tmp = inputHandler;
310 if (tmp == handler) {
311 inputHandler = handler->next;
312 free(handler);
313 } else {
314 while (tmp->next) {
315 if (tmp->next == handler) {
316 tmp->next = handler->next;
317 free(handler);
318 break;
320 tmp = tmp->next;
326 static void
327 checkIdleHandlers()
329 IdleHandler *handler, *tmp;
331 if (!idleHandler)
332 return;
334 handler = idleHandler;
336 /* we will process all idleHandlers so, empty the handler list */
337 idleHandler = NULL;
339 while (handler) {
340 tmp = handler->next;
341 (*handler->callback)(handler->clientData);
342 /* remove the handler */
343 free(handler);
345 handler = tmp;
351 static void
352 checkTimerHandlers()
354 TimerHandler *handler;
355 struct timeval now;
357 rightNow(&now);
359 while (timerHandler && IS_AFTER(now, timerHandler->when)) {
360 handler = timerHandler;
361 timerHandler = timerHandler->next;
362 handler->next = NULL;
363 (*handler->callback)(handler->clientData);
364 free(handler);
370 static void
371 delayUntilNextTimerEvent(struct timeval *delay)
373 struct timeval now;
375 if (!timerHandler) {
376 /* The return value of this function is only valid if there _are_
377 timers active. */
378 delay->tv_sec = 0;
379 delay->tv_usec = 0;
380 return;
383 rightNow(&now);
384 if (IS_AFTER(now, timerHandler->when)) {
385 delay->tv_sec = 0;
386 delay->tv_usec = 0;
387 } else {
388 delay->tv_sec = timerHandler->when.tv_sec - now.tv_sec;
389 delay->tv_usec = timerHandler->when.tv_usec - now.tv_usec;
397 * WMCreateEventHandler--
398 * Create an event handler and put it in the event handler list for the
399 * view. If the same callback and clientdata are already used in another
400 * handler, the masks are swapped.
403 void
404 WMCreateEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
405 void *clientData)
407 W_EventHandler *handler;
408 W_EventHandler *ptr = view->handlerList;
409 unsigned long eventMask;
411 if (ptr==NULL) {
412 handler = wmalloc(sizeof(W_EventHandler));
414 handler->nextHandler = NULL;
416 view->handlerList = handler;
418 eventMask = mask;
419 } else {
420 handler = NULL;
421 eventMask = mask;
422 while (ptr != NULL) {
423 if (ptr->clientData == clientData && ptr->proc == eventProc) {
424 handler = ptr;
426 eventMask |= ptr->eventMask;
428 ptr = ptr->nextHandler;
430 if (!handler) {
431 handler = wmalloc(sizeof(W_EventHandler));
432 handler->nextHandler = view->handlerList;
433 view->handlerList = handler;
437 /* select events for window */
438 handler->eventMask = mask;
439 handler->proc = eventProc;
440 handler->clientData = clientData;
445 * WMDeleteEventHandler--
446 * Delete event handler matching arguments from windows
447 * event handler list.
450 void
451 WMDeleteEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
452 void *clientData)
454 W_EventHandler *handler, *ptr, *pptr;
456 ptr = view->handlerList;
458 handler = NULL;
459 pptr = NULL;
461 while (ptr!=NULL) {
462 if (ptr->eventMask == mask && ptr->proc == eventProc
463 && ptr->clientData == clientData) {
464 handler = ptr;
465 break;
467 pptr = ptr;
468 ptr = ptr->nextHandler;
471 if (!handler)
472 return;
474 if (!pptr) {
475 view->handlerList = handler->nextHandler;
476 } else {
477 pptr->nextHandler = handler->nextHandler;
479 free(handler);
484 void
485 W_CleanUpEvents(WMView *view)
487 W_EventHandler *ptr, *nptr;
489 ptr = view->handlerList;
491 while (ptr!=NULL) {
492 nptr = ptr->nextHandler;
493 free(ptr);
494 ptr = nptr;
500 static Time
501 getEventTime(WMScreen *screen, XEvent *event)
503 switch (event->type) {
504 case ButtonPress:
505 case ButtonRelease:
506 return event->xbutton.time;
507 case KeyPress:
508 case KeyRelease:
509 return event->xkey.time;
510 case MotionNotify:
511 return event->xmotion.time;
512 case EnterNotify:
513 case LeaveNotify:
514 return event->xcrossing.time;
515 case PropertyNotify:
516 return event->xproperty.time;
517 case SelectionClear:
518 return event->xselectionclear.time;
519 case SelectionRequest:
520 return event->xselectionrequest.time;
521 case SelectionNotify:
522 return event->xselection.time;
523 default:
524 return screen->lastEventTime;
529 void
530 W_CallDestroyHandlers(W_View *view)
532 XEvent event;
533 W_EventHandler *hPtr;
535 event.type = DestroyNotify;
536 event.xdestroywindow.window = view->window;
537 event.xdestroywindow.event = view->window;
538 hPtr = view->handlerList;
539 while (hPtr!=NULL) {
540 if (hPtr->eventMask & StructureNotifyMask) {
541 (*hPtr->proc)(&event, hPtr->clientData);
544 hPtr = hPtr->nextHandler;
550 WMHandleEvent(XEvent *event)
552 W_EventHandler *hPtr;
553 W_View *view, *vPtr, *toplevel;
554 unsigned long mask;
555 Window window;
557 if (event->type == MappingNotify) {
558 XRefreshKeyboardMapping(&event->xmapping);
559 return True;
562 mask = eventMasks[event->xany.type];
564 window = event->xany.window;
566 /* diferentiate SubstructureNotify with StructureNotify */
567 if (mask == StructureNotifyMask) {
568 if (event->xmap.event != event->xmap.window) {
569 mask = SubstructureNotifyMask;
570 window = event->xmap.event;
573 view = W_GetViewForXWindow(event->xany.display, window);
574 if (!view) {
575 if (extraEventHandler)
576 (extraEventHandler)(event);
578 return False;
581 view->screen->lastEventTime = getEventTime(view->screen, event);
583 toplevel = W_TopLevelOfView(view);
585 /* if it's a key event, redispatch it to the focused control */
586 if (mask & (KeyPressMask|KeyReleaseMask)) {
587 W_View *focused = W_FocusedViewOfToplevel(toplevel);
589 if (focused) {
590 view = focused;
594 /* compress Motion events */
595 if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
596 while (XPending(event->xmotion.display)) {
597 XEvent ev;
598 XPeekEvent(event->xmotion.display, &ev);
599 if (ev.type == MotionNotify
600 && event->xmotion.window == ev.xmotion.window
601 && event->xmotion.subwindow == ev.xmotion.subwindow) {
602 /* replace events */
603 XNextEvent(event->xmotion.display, event);
604 } else break;
608 /* compress expose events */
609 if (event->type == Expose && !view->flags.dontCompressExpose) {
610 while (XCheckTypedWindowEvent(event->xexpose.display, view->window,
611 Expose, event));
615 if (view->screen->modal && toplevel!=view->screen->modalView
616 && !toplevel->flags.worksWhenModal) {
617 if (event->type == KeyPress || event->type == KeyRelease
618 || event->type == MotionNotify || event->type == ButtonPress
619 || event->type == ButtonRelease
620 || event->type == FocusIn || event->type == FocusOut) {
621 return True;
625 /* This is a hack. It will make the panel be secure while
626 * the event handlers are handled, as some event handler
627 * might destroy the widget. */
628 W_RetainView(toplevel);
630 hPtr = view->handlerList;
632 while (hPtr!=NULL) {
633 W_EventHandler *tmp;
635 tmp = hPtr->nextHandler;
637 if ((hPtr->eventMask & mask)) {
638 (*hPtr->proc)(event, hPtr->clientData);
641 hPtr = tmp;
644 /* pass the event to the top level window of the widget */
645 if (view->parent!=NULL) {
646 vPtr = view;
647 while (vPtr->parent!=NULL)
648 vPtr = vPtr->parent;
650 hPtr = vPtr->handlerList;
652 while (hPtr!=NULL) {
654 if (hPtr->eventMask & mask) {
655 (*hPtr->proc)(event, hPtr->clientData);
657 hPtr = hPtr->nextHandler;
661 /* save button click info to track double-clicks */
662 if (view->screen->ignoreNextDoubleClick) {
663 view->screen->ignoreNextDoubleClick = 0;
664 } else {
665 if (event->type == ButtonPress) {
666 view->screen->lastClickWindow = event->xbutton.window;
667 view->screen->lastClickTime = event->xbutton.time;
671 W_ReleaseView(toplevel);
673 return True;
678 WMIsDoubleClick(XEvent *event)
680 W_View *view;
682 if (event->type != ButtonPress)
683 return False;
685 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
687 if (!view)
688 return False;
690 if (view->screen->lastClickWindow != event->xbutton.window)
691 return False;
693 if (event->xbutton.time - view->screen->lastClickTime
694 < WINGsConfiguration.doubleClickDelay) {
695 view->screen->lastClickTime = 0;
696 view->screen->lastClickWindow = None;
697 view->screen->ignoreNextDoubleClick = 1;
698 return True;
699 } else
700 return False;
704 Bool
705 W_WaitForEvent(Display *dpy, unsigned long xeventmask)
707 #ifndef HAVE_SELECT
708 #error This_system_does_not_have_select(2)_and_is_not_supported
709 #endif
710 struct timeval timeout;
711 struct timeval *timeoutPtr;
712 fd_set rset, wset, eset;
713 int maxfd;
714 int count;
715 InputHandler *handler = inputHandler;
717 FD_ZERO(&rset);
718 FD_ZERO(&wset);
719 FD_ZERO(&eset);
721 FD_SET(ConnectionNumber(dpy), &rset);
722 maxfd = ConnectionNumber(dpy);
724 while (handler) {
725 if (handler->mask & WIReadMask)
726 FD_SET(handler->fd, &rset);
728 if (handler->mask & WIWriteMask)
729 FD_SET(handler->fd, &wset);
731 if (handler->mask & WIExceptMask)
732 FD_SET(handler->fd, &eset);
734 if (maxfd < handler->fd)
735 maxfd = handler->fd;
737 handler = handler->next;
742 * Setup the select() timeout to the estimated time until the
743 * next timer expires.
745 if (timerPending()) {
746 delayUntilNextTimerEvent(&timeout);
747 timeoutPtr = &timeout;
748 } else {
749 timeoutPtr = (struct timeval*)0;
752 XSync(dpy, False);
753 if (xeventmask==0) {
754 if (XPending(dpy))
755 return True;
756 } else {
757 XEvent ev;
758 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
759 XPutBackEvent(dpy, &ev);
760 return True;
763 /* TODO: port to poll() */
764 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
766 if (count > 0) {
767 handler = inputHandler;
769 while (handler) {
770 int mask;
772 mask = 0;
774 if (FD_ISSET(handler->fd, &rset))
775 mask |= WIReadMask;
777 if (FD_ISSET(handler->fd, &wset))
778 mask |= WIWriteMask;
780 if (FD_ISSET(handler->fd, &eset))
781 mask |= WIExceptMask;
783 if (mask!=0 && handler->callback) {
784 (*handler->callback)(handler->fd, mask,
785 handler->clientData);
788 handler = handler->next;
792 return FD_ISSET(ConnectionNumber(dpy), &rset);
797 void
798 WMNextEvent(Display *dpy, XEvent *event)
800 /* Check any expired timers */
801 if (timerPending()) {
802 checkTimerHandlers();
805 while (XPending(dpy) == 0) {
806 /* Do idle stuff */
807 /* Do idle and timer stuff while there are no timer or X events */
808 while (!XPending(dpy) && idlePending()) {
809 if (idlePending())
810 checkIdleHandlers();
811 /* dispatch timer events */
812 if (timerPending())
813 checkTimerHandlers();
817 * Make sure that new events did not arrive while we were doing
818 * timer/idle stuff. Or we might block forever waiting for
819 * an event that already arrived.
821 /* wait to something happen */
822 W_WaitForEvent(dpy, 0);
824 /* Check any expired timers */
825 if (timerPending()) {
826 checkTimerHandlers();
830 XNextEvent(dpy, event);
833 #if 0
834 void
835 WMMaskEvent(Display *dpy, long mask, XEvent *event)
837 unsigned long milliseconds;
838 struct timeval timeout;
839 struct timeval *timeoutOrInfty;
840 fd_set readset;
842 while (!XCheckMaskEvent(dpy, mask, event)) {
843 /* Do idle stuff while there are no timer or X events */
844 while (idlePending()) {
845 checkIdleHandlers();
846 if (XCheckMaskEvent(dpy, mask, event))
847 return;
851 * Setup the select() timeout to the estimated time until the
852 * next timer expires.
854 if (timerPending()) {
855 delayUntilNextTimerEvent(&timeout);
856 timeoutOrInfty = &timeout;
857 } else {
858 timeoutOrInfty = (struct timeval*)0;
861 if (XCheckMaskEvent(dpy, mask, event))
862 return;
864 /* Wait for input on the X connection socket */
865 FD_ZERO(&readset);
866 FD_SET(ConnectionNumber(dpy), &readset);
867 select(1 + ConnectionNumber(dpy), &readset, (fd_set*)0, (fd_set*)0,
868 timeoutOrInfty);
870 /* Check any expired timers */
871 if (timerPending()) {
872 checkTimerHandlers();
876 #endif
877 #if 1
879 * Cant use this because XPending() will make W_WaitForEvent
880 * return even if the event in the queue is not what we want,
881 * and if we block until some new event arrives from the
882 * server, other events already in the queue (like Expose)
883 * will be deferred.
885 void
886 WMMaskEvent(Display *dpy, long mask, XEvent *event)
888 while (!XCheckMaskEvent(dpy, mask, event)) {
889 /* Do idle stuff while there are no timer or X events */
890 while (idlePending()) {
891 checkIdleHandlers();
892 if (XCheckMaskEvent(dpy, mask, event))
893 return;
896 /* Wait for input on the X connection socket */
897 W_WaitForEvent(dpy, mask);
899 /* Check any expired timers */
900 if (timerPending()) {
901 checkTimerHandlers();
905 #endif
907 Bool
908 WMScreenPending(WMScreen *scr)
910 if (XPending(scr->display))
911 return True;
912 else
913 return False;
917 WMEventHook*
918 WMHookEventHandler(WMEventHook *handler)
920 WMEventHook *oldHandler = extraEventHandler;
922 extraEventHandler = handler;
924 return oldHandler;