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 }