Initial revision
[wmaker-crm.git] / WINGs / wevent.c
blob3c906cd091ff9ab429457612ec8d302e2dfc215e
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 unsigned long msec; /* 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)
118 /* return current time in milliseconds */
119 #ifdef HAVE_GETTIMEOFDAY
120 static unsigned long
121 rightNow(void) {
122 struct timeval tv;
124 gettimeofday(&tv, NULL);
126 return 1000L*(unsigned long)tv.tv_sec + (unsigned long)tv.tv_usec/1000L;
128 #else /* !HAVE_GETTIMEOFDAY */
129 # define rightNow() (1000*(unsigned long)time(NULL))
130 #endif /* !HAVE_GETTIMEOFDAY */
133 WMHandlerID
134 WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
136 TimerHandler *handler, *tmp;
138 handler = malloc(sizeof(TimerHandler));
139 if (!handler)
140 return NULL;
142 handler->msec = rightNow()+milliseconds;
143 handler->callback = callback;
144 handler->clientData = cdata;
145 /* insert callback in queue, sorted by time left */
146 if (!timerHandler || timerHandler->msec >= handler->msec) {
147 /* first in the queue */
148 handler->next = timerHandler;
149 timerHandler = handler;
150 } else {
151 tmp = timerHandler;
152 while (tmp->next && tmp->next->msec < handler->msec) {
153 tmp = tmp->next;
155 handler->next = tmp->next;
156 tmp->next = handler;
158 return handler;
163 void
164 WMDeleteTimerWithClientData(void *cdata)
166 TimerHandler *handler, *tmp;
168 if (!cdata || !timerHandler)
169 return;
171 tmp = timerHandler;
172 if (tmp->clientData==cdata) {
173 timerHandler = tmp->next;
174 free(tmp);
175 } else {
176 while (tmp->next) {
177 if (tmp->next->clientData==cdata) {
178 handler = tmp->next;
179 tmp->next = handler->next;
180 free(handler);
181 break;
183 tmp = tmp->next;
190 void
191 WMDeleteTimerHandler(WMHandlerID handlerID)
193 TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
195 if (!handler || !timerHandler)
196 return;
198 tmp = timerHandler;
199 if (tmp==handler) {
200 timerHandler = handler->next;
201 free(handler);
202 } else {
203 while (tmp->next) {
204 if (tmp->next==handler) {
205 tmp->next=handler->next;
206 free(handler);
207 break;
209 tmp = tmp->next;
216 WMHandlerID
217 WMAddIdleHandler(WMCallback *callback, void *cdata)
219 IdleHandler *handler, *tmp;
221 handler = malloc(sizeof(IdleHandler));
222 if (!handler)
223 return NULL;
225 handler->callback = callback;
226 handler->clientData = cdata;
227 handler->next = NULL;
228 /* add callback at end of queue */
229 if (!idleHandler) {
230 idleHandler = handler;
231 } else {
232 tmp = idleHandler;
233 while (tmp->next) {
234 tmp = tmp->next;
236 tmp->next = handler;
239 return handler;
244 void
245 WMDeleteIdleHandler(WMHandlerID handlerID)
247 IdleHandler *tmp, *handler = (IdleHandler*)handlerID;
249 if (!handler || !idleHandler)
250 return;
252 tmp = idleHandler;
253 if (tmp == handler) {
254 idleHandler = handler->next;
255 free(handler);
256 } else {
257 while (tmp->next) {
258 if (tmp->next == handler) {
259 tmp->next = handler->next;
260 free(handler);
261 break;
263 tmp = tmp->next;
270 WMHandlerID
271 WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
273 InputHandler *handler;
275 handler = wmalloc(sizeof(InputHandler));
277 handler->fd = fd;
278 handler->mask = condition;
279 handler->callback = proc;
280 handler->clientData = clientData;
282 handler->next = inputHandler;
284 inputHandler = handler;
286 return handler;
290 void
291 WMDeleteInputHandler(WMHandlerID handlerID)
293 InputHandler *tmp, *handler = (InputHandler*)handlerID;
295 if (!handler || !inputHandler)
296 return;
298 tmp = inputHandler;
299 if (tmp == handler) {
300 inputHandler = handler->next;
301 free(handler);
302 } else {
303 while (tmp->next) {
304 if (tmp->next == handler) {
305 tmp->next = handler->next;
306 free(handler);
307 break;
309 tmp = tmp->next;
315 static void
316 checkIdleHandlers()
318 IdleHandler *handler, *tmp;
320 if (!idleHandler)
321 return;
323 handler = idleHandler;
325 /* we will process all idleHandlers so, empty the handler list */
326 idleHandler = NULL;
328 while (handler) {
329 tmp = handler->next;
330 (*handler->callback)(handler->clientData);
331 /* remove the handler */
332 free(handler);
334 handler = tmp;
340 static void
341 checkTimerHandlers()
343 TimerHandler *handler;
344 unsigned long now = rightNow();
346 if (!timerHandler || (timerHandler->msec > now))
347 return;
349 while (timerHandler && timerHandler->msec <= now) {
350 handler = timerHandler;
351 timerHandler = timerHandler->next;
352 handler->next = NULL;
353 (*handler->callback)(handler->clientData);
354 free(handler);
360 static unsigned long
361 msToNextTimerEvent()
363 unsigned long now;
365 if (!timerHandler) {
366 /* The return value of this function is only valid if there _are_
367 timers active. */
368 return 0;
371 now = rightNow();
372 if (timerHandler->msec < now) {
373 return 0;
374 } else {
375 return timerHandler->msec - now;
383 * WMCreateEventHandler--
384 * Create an event handler and put it in the event handler list for the
385 * view. If the same callback and clientdata are already used in another
386 * handler, the masks are swapped.
389 void
390 WMCreateEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
391 void *clientData)
393 W_EventHandler *handler;
394 W_EventHandler *ptr = view->handlerList;
395 unsigned long eventMask;
397 if (ptr==NULL) {
398 handler = wmalloc(sizeof(W_EventHandler));
400 handler->nextHandler = NULL;
402 view->handlerList = handler;
404 eventMask = mask;
405 } else {
406 handler = NULL;
407 eventMask = mask;
408 while (ptr != NULL) {
409 if (ptr->clientData == clientData && ptr->proc == eventProc) {
410 handler = ptr;
412 eventMask |= ptr->eventMask;
414 ptr = ptr->nextHandler;
416 if (!handler) {
417 handler = wmalloc(sizeof(W_EventHandler));
418 handler->nextHandler = view->handlerList;
419 view->handlerList = handler;
423 /* select events for window */
424 handler->eventMask = mask;
425 handler->proc = eventProc;
426 handler->clientData = clientData;
431 * WMDeleteEventHandler--
432 * Delete event handler matching arguments from windows
433 * event handler list.
436 void
437 WMDeleteEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
438 void *clientData)
440 W_EventHandler *handler, *ptr, *pptr;
442 ptr = view->handlerList;
444 handler = NULL;
445 pptr = NULL;
447 while (ptr!=NULL) {
448 if (ptr->eventMask == mask && ptr->proc == eventProc
449 && ptr->clientData == clientData) {
450 handler = ptr;
451 break;
453 pptr = ptr;
454 ptr = ptr->nextHandler;
457 if (!handler)
458 return;
460 if (!pptr) {
461 view->handlerList = handler->nextHandler;
462 } else {
463 pptr->nextHandler = handler->nextHandler;
465 free(handler);
470 void
471 W_CleanUpEvents(WMView *view)
473 W_EventHandler *ptr, *nptr;
475 ptr = view->handlerList;
477 while (ptr!=NULL) {
478 nptr = ptr->nextHandler;
479 free(ptr);
480 ptr = nptr;
486 static Time
487 getEventTime(WMScreen *screen, XEvent *event)
489 switch (event->type) {
490 case ButtonPress:
491 case ButtonRelease:
492 return event->xbutton.time;
493 case KeyPress:
494 case KeyRelease:
495 return event->xkey.time;
496 case MotionNotify:
497 return event->xmotion.time;
498 case EnterNotify:
499 case LeaveNotify:
500 return event->xcrossing.time;
501 case PropertyNotify:
502 return event->xproperty.time;
503 case SelectionClear:
504 return event->xselectionclear.time;
505 case SelectionRequest:
506 return event->xselectionrequest.time;
507 case SelectionNotify:
508 return event->xselection.time;
509 default:
510 return screen->lastEventTime;
515 void
516 W_CallDestroyHandlers(W_View *view)
518 XEvent event;
519 W_EventHandler *hPtr;
521 event.type = DestroyNotify;
522 event.xdestroywindow.window = view->window;
523 event.xdestroywindow.event = view->window;
524 hPtr = view->handlerList;
525 while (hPtr!=NULL) {
526 if (hPtr->eventMask & StructureNotifyMask) {
527 (*hPtr->proc)(&event, hPtr->clientData);
530 hPtr = hPtr->nextHandler;
536 WMHandleEvent(XEvent *event)
538 W_EventHandler *hPtr;
539 W_View *view, *vPtr, *toplevel;
540 unsigned long mask;
541 Window window;
543 if (event->type == MappingNotify) {
544 XRefreshKeyboardMapping(&event->xmapping);
545 return True;
548 mask = eventMasks[event->xany.type];
550 window = event->xany.window;
552 /* diferentiate SubstructureNotify with StructureNotify */
553 if (mask == StructureNotifyMask) {
554 if (event->xmap.event != event->xmap.window) {
555 mask = SubstructureNotifyMask;
556 window = event->xmap.event;
559 view = W_GetViewForXWindow(event->xany.display, window);
560 if (!view) {
561 if (extraEventHandler)
562 (extraEventHandler)(event);
564 return False;
567 view->screen->lastEventTime = getEventTime(view->screen, event);
569 toplevel = W_TopLevelOfView(view);
571 /* if it's a key event, redispatch it to the focused control */
572 if (mask & (KeyPressMask|KeyReleaseMask)) {
573 W_View *focused = W_FocusedViewOfToplevel(toplevel);
575 if (focused) {
576 view = focused;
580 /* compress Motion events */
581 if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
582 while (XPending(event->xmotion.display)) {
583 XEvent ev;
584 XPeekEvent(event->xmotion.display, &ev);
585 if (ev.type == MotionNotify
586 && event->xmotion.window == ev.xmotion.window
587 && event->xmotion.subwindow == ev.xmotion.subwindow) {
588 /* replace events */
589 XNextEvent(event->xmotion.display, event);
590 } else break;
594 /* compress expose events */
595 if (event->type == Expose && !view->flags.dontCompressExpose) {
596 while (XCheckTypedWindowEvent(event->xexpose.display, view->window,
597 Expose, event));
601 if (view->screen->modal && toplevel!=view->screen->modalView
602 && !toplevel->flags.worksWhenModal) {
603 if (event->type == KeyPress || event->type == KeyRelease
604 || event->type == MotionNotify || event->type == ButtonPress
605 || event->type == ButtonRelease
606 || event->type == FocusIn || event->type == FocusOut) {
607 return True;
611 /* This is a hack. It will make the panel be secure while
612 * the event handlers are handled, as some event handler
613 * might destroy the widget. */
614 W_RetainView(toplevel);
616 hPtr = view->handlerList;
618 while (hPtr!=NULL) {
619 W_EventHandler *tmp;
621 tmp = hPtr->nextHandler;
623 if ((hPtr->eventMask & mask)) {
624 (*hPtr->proc)(event, hPtr->clientData);
627 hPtr = tmp;
630 /* pass the event to the top level window of the widget */
631 if (view->parent!=NULL) {
632 vPtr = view;
633 while (vPtr->parent!=NULL)
634 vPtr = vPtr->parent;
636 hPtr = vPtr->handlerList;
638 while (hPtr!=NULL) {
640 if (hPtr->eventMask & mask) {
641 (*hPtr->proc)(event, hPtr->clientData);
643 hPtr = hPtr->nextHandler;
647 /* save button click info to track double-clicks */
648 if (view->screen->ignoreNextDoubleClick) {
649 view->screen->ignoreNextDoubleClick = 0;
650 } else {
651 if (event->type == ButtonPress) {
652 view->screen->lastClickWindow = event->xbutton.window;
653 view->screen->lastClickTime = event->xbutton.time;
657 W_ReleaseView(toplevel);
659 return True;
664 WMIsDoubleClick(XEvent *event)
666 W_View *view;
668 if (event->type != ButtonPress)
669 return False;
671 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
673 if (!view)
674 return False;
676 if (view->screen->lastClickWindow != event->xbutton.window)
677 return False;
679 if (event->xbutton.time - view->screen->lastClickTime
680 < WINGsConfiguration.doubleClickDelay) {
681 view->screen->lastClickTime = 0;
682 view->screen->lastClickWindow = None;
683 view->screen->ignoreNextDoubleClick = 1;
684 return True;
685 } else
686 return False;
690 Bool
691 W_WaitForEvent(Display *dpy, unsigned long xeventmask)
693 #ifndef HAVE_SELECT
694 #error This_system_does_not_have_select(2)_and_is_not_supported
695 #endif
696 unsigned long milliseconds;
697 struct timeval timeout;
698 struct timeval *timeoutPtr;
699 fd_set rset, wset, eset;
700 int maxfd;
701 int count;
702 InputHandler *handler = inputHandler;
704 FD_ZERO(&rset);
705 FD_ZERO(&wset);
706 FD_ZERO(&eset);
708 FD_SET(ConnectionNumber(dpy), &rset);
709 maxfd = ConnectionNumber(dpy);
711 while (handler) {
712 if (handler->mask & WIReadMask)
713 FD_SET(handler->fd, &rset);
715 if (handler->mask & WIWriteMask)
716 FD_SET(handler->fd, &wset);
718 if (handler->mask & WIExceptMask)
719 FD_SET(handler->fd, &eset);
721 if (maxfd < handler->fd)
722 maxfd = handler->fd;
724 handler = handler->next;
729 * Setup the select() timeout to the estimated time until the
730 * next timer expires.
732 if (timerPending()) {
733 milliseconds = msToNextTimerEvent();
734 timeout.tv_sec = milliseconds / 1000;
735 timeout.tv_usec = (milliseconds % 1000) * 1000;
736 timeoutPtr = &timeout;
737 } else {
738 timeoutPtr = (struct timeval*)0;
741 XSync(dpy, False);
742 if (xeventmask==0) {
743 if (XPending(dpy))
744 return True;
745 } else {
746 XEvent ev;
747 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
748 XPutBackEvent(dpy, &ev);
749 return True;
752 /* TODO: port to poll() */
753 count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
755 if (count > 0) {
756 handler = inputHandler;
758 while (handler) {
759 int mask;
761 mask = 0;
763 if (FD_ISSET(handler->fd, &rset))
764 mask |= WIReadMask;
766 if (FD_ISSET(handler->fd, &wset))
767 mask |= WIWriteMask;
769 if (FD_ISSET(handler->fd, &eset))
770 mask |= WIExceptMask;
772 if (mask!=0 && handler->callback) {
773 (*handler->callback)(handler->fd, mask,
774 handler->clientData);
777 handler = handler->next;
781 return FD_ISSET(ConnectionNumber(dpy), &rset);
786 void
787 WMNextEvent(Display *dpy, XEvent *event)
789 /* Check any expired timers */
790 if (timerPending()) {
791 checkTimerHandlers();
794 while (XPending(dpy) == 0) {
795 /* Do idle stuff */
796 /* Do idle and timer stuff while there are no timer or X events */
797 while (!XPending(dpy) && idlePending()) {
798 if (idlePending())
799 checkIdleHandlers();
800 /* dispatch timer events */
801 if (timerPending())
802 checkTimerHandlers();
806 * Make sure that new events did not arrive while we were doing
807 * timer/idle stuff. Or we might block forever waiting for
808 * an event that already arrived.
810 /* wait to something happen */
811 W_WaitForEvent(dpy, 0);
813 /* Check any expired timers */
814 if (timerPending()) {
815 checkTimerHandlers();
819 XNextEvent(dpy, event);
822 #if 0
823 void
824 WMMaskEvent(Display *dpy, long mask, XEvent *event)
826 unsigned long milliseconds;
827 struct timeval timeout;
828 struct timeval *timeoutOrInfty;
829 fd_set readset;
831 while (!XCheckMaskEvent(dpy, mask, event)) {
832 /* Do idle stuff while there are no timer or X events */
833 while (idlePending()) {
834 checkIdleHandlers();
835 if (XCheckMaskEvent(dpy, mask, event))
836 return;
840 * Setup the select() timeout to the estimated time until the
841 * next timer expires.
843 if (timerPending()) {
844 milliseconds = msToNextTimerEvent();
845 timeout.tv_sec = milliseconds / 1000;
846 timeout.tv_usec = (milliseconds % 1000) * 1000;
847 timeoutOrInfty = &timeout;
848 } else {
849 timeoutOrInfty = (struct timeval*)0;
852 if (XCheckMaskEvent(dpy, mask, event))
853 return;
855 /* Wait for input on the X connection socket */
856 FD_ZERO(&readset);
857 FD_SET(ConnectionNumber(dpy), &readset);
858 select(1 + ConnectionNumber(dpy), &readset, (fd_set*)0, (fd_set*)0,
859 timeoutOrInfty);
861 /* Check any expired timers */
862 if (timerPending()) {
863 checkTimerHandlers();
867 #endif
868 #if 1
870 * Cant use this because XPending() will make W_WaitForEvent
871 * return even if the event in the queue is not what we want,
872 * and if we block until some new event arrives from the
873 * server, other events already in the queue (like Expose)
874 * will be deferred.
876 void
877 WMMaskEvent(Display *dpy, long mask, XEvent *event)
879 while (!XCheckMaskEvent(dpy, mask, event)) {
880 /* Do idle stuff while there are no timer or X events */
881 while (idlePending()) {
882 checkIdleHandlers();
883 if (XCheckMaskEvent(dpy, mask, event))
884 return;
887 /* Wait for input on the X connection socket */
888 W_WaitForEvent(dpy, mask);
890 /* Check any expired timers */
891 if (timerPending()) {
892 checkTimerHandlers();
896 #endif
898 Bool
899 WMScreenPending(WMScreen *scr)
901 if (XPending(scr->display))
902 return True;
903 else
904 return False;
908 WMEventHook*
909 WMHookEventHandler(WMEventHook *handler)
911 WMEventHook *oldHandler = extraEventHandler;
913 extraEventHandler = handler;
915 return oldHandler;