Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / wevent.c
1
2 /*
3  * This event handling stuff was inspired on Tk.
4  */
5
6 #include "WINGsP.h"
7
8 /* table to map event types to event masks */
9 static unsigned long eventMasks[] = {
10         0,
11         0,
12         KeyPressMask,           /* KeyPress */
13         KeyReleaseMask,         /* KeyRelease */
14         ButtonPressMask,        /* ButtonPress */
15         ButtonReleaseMask,      /* ButtonRelease */
16         PointerMotionMask | PointerMotionHintMask | ButtonMotionMask
17             | Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask,
18         /* MotionNotify */
19         EnterWindowMask,        /* EnterNotify */
20         LeaveWindowMask,        /* LeaveNotify */
21         FocusChangeMask,        /* FocusIn */
22         FocusChangeMask,        /* FocusOut */
23         KeymapStateMask,        /* KeymapNotify */
24         ExposureMask,           /* Expose */
25         ExposureMask,           /* GraphicsExpose */
26         ExposureMask,           /* NoExpose */
27         VisibilityChangeMask,   /* VisibilityNotify */
28         SubstructureNotifyMask, /* CreateNotify */
29         StructureNotifyMask,    /* DestroyNotify */
30         StructureNotifyMask,    /* UnmapNotify */
31         StructureNotifyMask,    /* MapNotify */
32         SubstructureRedirectMask,       /* MapRequest */
33         StructureNotifyMask,    /* ReparentNotify */
34         StructureNotifyMask,    /* ConfigureNotify */
35         SubstructureRedirectMask,       /* ConfigureRequest */
36         StructureNotifyMask,    /* GravityNotify */
37         ResizeRedirectMask,     /* ResizeRequest */
38         StructureNotifyMask,    /* CirculateNotify */
39         SubstructureRedirectMask,       /* CirculateRequest */
40         PropertyChangeMask,     /* PropertyNotify */
41         0,                      /* SelectionClear */
42         0,                      /* SelectionRequest */
43         0,                      /* SelectionNotify */
44         ColormapChangeMask,     /* ColormapNotify */
45         ClientMessageMask,      /* ClientMessage */
46         0,                      /* Mapping Notify */
47 };
48
49 /* hook for other toolkits or wmaker process their events */
50 static WMEventHook *extraEventHandler = NULL;
51
52 /*
53  * WMCreateEventHandler--
54  *      Create an event handler and put it in the event handler list for the
55  * view. If the same callback and clientdata are already used in another
56  * handler, the masks are OR'ed.
57  *
58  */
59 void WMCreateEventHandler(WMView * view, unsigned long mask, WMEventProc * eventProc, void *clientData)
60 {
61         W_EventHandler *hPtr;
62         WMArrayIterator iter;
63
64         WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
65                 if (hPtr->clientData == clientData && hPtr->proc == eventProc) {
66                         hPtr->eventMask |= mask;
67                         return;
68                 }
69         }
70
71         hPtr = wmalloc(sizeof(W_EventHandler));
72
73         /* select events for window */
74         hPtr->eventMask = mask;
75         hPtr->proc = eventProc;
76         hPtr->clientData = clientData;
77
78         WMAddToArray(view->eventHandlers, hPtr);
79 }
80
81 static int matchHandler(void *item, void *cdata)
82 {
83 #define H1 ((W_EventHandler*)item)
84 #define H2 ((W_EventHandler*)cdata)
85
86         return (H1->eventMask == H2->eventMask && H1->proc == H2->proc && H1->clientData == H2->clientData);
87 }
88
89 /*
90  * WMDeleteEventHandler--
91  *      Delete event handler matching arguments from windows
92  * event handler list.
93  *
94  */
95 void WMDeleteEventHandler(WMView * view, unsigned long mask, WMEventProc * eventProc, void *clientData)
96 {
97         W_EventHandler tmp;
98
99         tmp.eventMask = mask;
100         tmp.proc = eventProc;
101         tmp.clientData = clientData;
102         WMRemoveFromArrayMatching(view->eventHandlers, matchHandler, (void *)&tmp);
103 }
104
105 static Time getEventTime(WMScreen * screen, XEvent * event)
106 {
107         switch (event->type) {
108         case ButtonPress:
109         case ButtonRelease:
110                 return event->xbutton.time;
111         case KeyPress:
112         case KeyRelease:
113                 return event->xkey.time;
114         case MotionNotify:
115                 return event->xmotion.time;
116         case EnterNotify:
117         case LeaveNotify:
118                 return event->xcrossing.time;
119         case PropertyNotify:
120                 return event->xproperty.time;
121         case SelectionClear:
122                 return event->xselectionclear.time;
123         case SelectionRequest:
124                 return event->xselectionrequest.time;
125         case SelectionNotify:
126                 return event->xselection.time;
127         default:
128                 return screen->lastEventTime;
129         }
130 }
131
132 void W_CallDestroyHandlers(W_View * view)
133 {
134         XEvent event;
135         WMArrayIterator iter;
136         W_EventHandler *hPtr;
137
138         event.type = DestroyNotify;
139         event.xdestroywindow.window = view->window;
140         event.xdestroywindow.event = view->window;
141
142         WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
143                 if (hPtr->eventMask & StructureNotifyMask) {
144                         (*hPtr->proc) (&event, hPtr->clientData);
145                 }
146         }
147 }
148
149 void WMSetViewNextResponder(WMView * view, WMView * responder)
150 {
151         /* set the widget to receive keyboard events that aren't handled
152          * by this widget */
153
154         view->nextResponder = responder;
155 }
156
157 void WMRelayToNextResponder(WMView * view, XEvent * event)
158 {
159         unsigned long mask = eventMasks[event->xany.type];
160
161         if (view->nextResponder) {
162                 WMView *next = view->nextResponder;
163                 W_EventHandler *hPtr;
164                 WMArrayIterator iter;
165
166                 WM_ITERATE_ARRAY(next->eventHandlers, hPtr, iter) {
167                         if ((hPtr->eventMask & mask)) {
168                                 (*hPtr->proc) (event, hPtr->clientData);
169                         }
170                 }
171         }
172 }
173
174 int WMHandleEvent(XEvent * event)
175 {
176         W_EventHandler *hPtr;
177         W_View *view, *toplevel;
178         unsigned long mask;
179         Window window;
180         WMArrayIterator iter;
181
182         if (event->type == MappingNotify) {
183                 XRefreshKeyboardMapping(&event->xmapping);
184                 return True;
185         }
186
187         if (XFilterEvent(event, None) == True) {
188                 return False;
189         }
190
191         mask = eventMasks[event->xany.type];
192
193         window = event->xany.window;
194
195         /* diferentiate SubstructureNotify with StructureNotify */
196         if (mask == StructureNotifyMask) {
197                 if (event->xmap.event != event->xmap.window) {
198                         mask = SubstructureNotifyMask;
199                         window = event->xmap.event;
200                 }
201         }
202         view = W_GetViewForXWindow(event->xany.display, window);
203
204         if (!view) {
205                 if (extraEventHandler)
206                         (extraEventHandler) (event);
207
208                 return False;
209         }
210
211         view->screen->lastEventTime = getEventTime(view->screen, event);
212
213         toplevel = W_TopLevelOfView(view);
214
215         if (event->type == SelectionNotify || event->type == SelectionClear || event->type == SelectionRequest) {
216                 /* handle selection related events */
217                 W_HandleSelectionEvent(event);
218
219         }
220
221         /* if it's a key event, redispatch it to the focused control */
222         if (mask & (KeyPressMask | KeyReleaseMask)) {
223                 W_View *focused = W_FocusedViewOfToplevel(toplevel);
224
225                 if (focused) {
226                         view = focused;
227                 }
228         }
229
230         /* compress Motion events */
231         if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
232                 while (XPending(event->xmotion.display)) {
233                         XEvent ev;
234                         XPeekEvent(event->xmotion.display, &ev);
235                         if (ev.type == MotionNotify
236                             && event->xmotion.window == ev.xmotion.window
237                             && event->xmotion.subwindow == ev.xmotion.subwindow) {
238                                 /* replace events */
239                                 XNextEvent(event->xmotion.display, event);
240                         } else
241                                 break;
242                 }
243         }
244
245         /* compress expose events */
246         if (event->type == Expose && !view->flags.dontCompressExpose) {
247                 while (XCheckTypedWindowEvent(event->xexpose.display, view->window, Expose, event)) ;
248         }
249
250         if (view->screen->modalLoop && toplevel != view->screen->modalView && !toplevel->flags.worksWhenModal) {
251                 if (event->type == KeyPress || event->type == KeyRelease
252                     || event->type == MotionNotify || event->type == ButtonPress
253                     || event->type == ButtonRelease || event->type == FocusIn || event->type == FocusOut) {
254                         return True;
255                 }
256         }
257
258         /* do balloon stuffs */
259         if (event->type == EnterNotify)
260                 W_BalloonHandleEnterView(view);
261         else if (event->type == LeaveNotify)
262                 W_BalloonHandleLeaveView(view);
263
264         /* This is a hack. It will make the panel be secure while
265          * the event handlers are handled, as some event handler
266          * might destroy the widget. */
267         W_RetainView(toplevel);
268
269         WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
270                 if ((hPtr->eventMask & mask)) {
271                         (*hPtr->proc) (event, hPtr->clientData);
272                 }
273         }
274 #if 0
275         /* pass the event to the top level window of the widget */
276         /* TODO: change this to a responder chain */
277         if (view->parent != NULL) {
278                 vPtr = view;
279                 while (vPtr->parent != NULL)
280                         vPtr = vPtr->parent;
281
282                 WM_ITERATE_ARRAY(vPtr->eventHandlers, hPtr, iter) {
283                         if (hPtr->eventMask & mask) {
284                                 (*hPtr->proc) (event, hPtr->clientData);
285                         }
286                 }
287         }
288 #endif
289         /* save button click info to track double-clicks */
290         if (view->screen->ignoreNextDoubleClick) {
291                 view->screen->ignoreNextDoubleClick = 0;
292         } else {
293                 if (event->type == ButtonPress) {
294                         view->screen->lastClickWindow = event->xbutton.window;
295                         view->screen->lastClickTime = event->xbutton.time;
296                 }
297         }
298
299         if (event->type == ClientMessage) {
300                 /* must be handled at the end, for such message can destroy the view */
301                 W_HandleDNDClientMessage(toplevel, &event->xclient);
302         }
303
304         W_ReleaseView(toplevel);
305
306         return True;
307 }
308
309 int WMIsDoubleClick(XEvent * event)
310 {
311         W_View *view;
312
313         if (event->type != ButtonPress)
314                 return False;
315
316         view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
317
318         if (!view)
319                 return False;
320
321         if (view->screen->lastClickWindow != event->xbutton.window)
322                 return False;
323
324         if (event->xbutton.time - view->screen->lastClickTime < WINGsConfiguration.doubleClickDelay) {
325                 view->screen->lastClickTime = 0;
326                 view->screen->lastClickWindow = None;
327                 view->screen->ignoreNextDoubleClick = 1;
328                 return True;
329         } else
330                 return False;
331 }
332
333 /*
334  * Check for X and input events. If X events are present input events will
335  * not be checked.
336  *
337  * Return value: True if a X event is available or any input event was
338  *               processed, false otherwise (including return because of
339  *               some timer handler expired).
340  *
341  *  If waitForInput is False, it will just peek for available input and return
342  * without processing. Return vaue will be True if input is available.
343  *
344  *  If waitForInput is True, it will wait until an input event arrives on the
345  * registered input handlers and ConnectionNumber(dpy), or will return when
346  * a timer handler expires if no input event arrived until then.
347  */
348 static Bool waitForEvent(Display * dpy, unsigned long xeventmask, Bool waitForInput)
349 {
350         XSync(dpy, False);
351         if (xeventmask == 0) {
352                 if (XPending(dpy))
353                         return True;
354         } else {
355                 XEvent ev;
356                 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
357                         XPutBackEvent(dpy, &ev);
358                         return True;
359                 }
360         }
361
362         return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
363 }
364
365 void WMNextEvent(Display * dpy, XEvent * event)
366 {
367         /* Check any expired timers */
368         W_CheckTimerHandlers();
369
370         while (XPending(dpy) == 0) {
371                 /* Do idle and timer stuff while there are no input or X events */
372                 while (!waitForEvent(dpy, 0, False) && W_CheckIdleHandlers()) {
373                         /* dispatch timer events */
374                         W_CheckTimerHandlers();
375                 }
376
377                 /*
378                  * Make sure that new events did not arrive while we were doing
379                  * timer/idle stuff. Or we might block forever waiting for
380                  * an event that already arrived.
381                  */
382                 /* wait for something to happen or a timer to expire */
383                 waitForEvent(dpy, 0, True);
384
385                 /* Check any expired timers */
386                 W_CheckTimerHandlers();
387         }
388
389         XNextEvent(dpy, event);
390 }
391
392 void WMMaskEvent(Display * dpy, long mask, XEvent * event)
393 {
394         /* Check any expired timers */
395         W_CheckTimerHandlers();
396
397         while (!XCheckMaskEvent(dpy, mask, event)) {
398                 /* Do idle and timer stuff while there are no input or X events */
399                 while (!waitForEvent(dpy, mask, False) && W_CheckIdleHandlers()) {
400                         W_CheckTimerHandlers();
401                 }
402
403                 if (XCheckMaskEvent(dpy, mask, event))
404                         return;
405
406                 /* Wait for input on the X connection socket or another input handler */
407                 waitForEvent(dpy, mask, True);
408
409                 /* Check any expired timers */
410                 W_CheckTimerHandlers();
411         }
412 }
413
414 Bool WMScreenPending(WMScreen * scr)
415 {
416         if (XPending(scr->display))
417                 return True;
418         else
419                 return False;
420 }
421
422 WMEventHook *WMHookEventHandler(WMEventHook * handler)
423 {
424         WMEventHook *oldHandler = extraEventHandler;
425
426         extraEventHandler = handler;
427
428         return oldHandler;
429 }