Makefile: Beautify compilation messages
[wmaker-crm.git] / WINGs / wevent.c
blob87a6b9fb95786cf67076ed6f0ab04ca793fd998c
3 /*
4 * This event handling stuff was inspired on Tk.
5 */
7 #include "WINGsP.h"
10 /* table to map event types to event masks */
11 static unsigned long eventMasks[] = {
14 KeyPressMask, /* KeyPress */
15 KeyReleaseMask, /* KeyRelease */
16 ButtonPressMask, /* ButtonPress */
17 ButtonReleaseMask, /* ButtonRelease */
18 PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
19 |Button1MotionMask|Button2MotionMask|Button3MotionMask
20 |Button4MotionMask|Button5MotionMask,
21 /* MotionNotify */
22 EnterWindowMask, /* EnterNotify */
23 LeaveWindowMask, /* LeaveNotify */
24 FocusChangeMask, /* FocusIn */
25 FocusChangeMask, /* FocusOut */
26 KeymapStateMask, /* KeymapNotify */
27 ExposureMask, /* Expose */
28 ExposureMask, /* GraphicsExpose */
29 ExposureMask, /* NoExpose */
30 VisibilityChangeMask, /* VisibilityNotify */
31 SubstructureNotifyMask, /* CreateNotify */
32 StructureNotifyMask, /* DestroyNotify */
33 StructureNotifyMask, /* UnmapNotify */
34 StructureNotifyMask, /* MapNotify */
35 SubstructureRedirectMask, /* MapRequest */
36 StructureNotifyMask, /* ReparentNotify */
37 StructureNotifyMask, /* ConfigureNotify */
38 SubstructureRedirectMask, /* ConfigureRequest */
39 StructureNotifyMask, /* GravityNotify */
40 ResizeRedirectMask, /* ResizeRequest */
41 StructureNotifyMask, /* CirculateNotify */
42 SubstructureRedirectMask, /* CirculateRequest */
43 PropertyChangeMask, /* PropertyNotify */
44 0, /* SelectionClear */
45 0, /* SelectionRequest */
46 0, /* SelectionNotify */
47 ColormapChangeMask, /* ColormapNotify */
48 ClientMessageMask, /* ClientMessage */
49 0, /* Mapping Notify */
54 /* hook for other toolkits or wmaker process their events */
55 static WMEventHook *extraEventHandler=NULL;
62 * WMCreateEventHandler--
63 * Create an event handler and put it in the event handler list for the
64 * view. If the same callback and clientdata are already used in another
65 * handler, the masks are OR'ed.
68 void
69 WMCreateEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
70 void *clientData)
72 W_EventHandler *hPtr;
73 WMArrayIterator iter;
75 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
76 if (hPtr->clientData==clientData && hPtr->proc==eventProc) {
77 hPtr->eventMask |= mask;
78 return;
82 hPtr = wmalloc(sizeof(W_EventHandler));
84 /* select events for window */
85 hPtr->eventMask = mask;
86 hPtr->proc = eventProc;
87 hPtr->clientData = clientData;
89 WMAddToArray(view->eventHandlers, hPtr);
93 static int
94 matchHandler(void *item, void *cdata)
96 #define H1 ((W_EventHandler*)item)
97 #define H2 ((W_EventHandler*)cdata)
99 return (H1->eventMask==H2->eventMask && H1->proc==H2->proc &&
100 H1->clientData==H2->clientData);
105 * WMDeleteEventHandler--
106 * Delete event handler matching arguments from windows
107 * event handler list.
110 void
111 WMDeleteEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
112 void *clientData)
114 W_EventHandler tmp;
116 tmp.eventMask = mask;
117 tmp.proc = eventProc;
118 tmp.clientData = clientData;
119 WMRemoveFromArrayMatching(view->eventHandlers, matchHandler, (void*)&tmp);
123 static Time
124 getEventTime(WMScreen *screen, XEvent *event)
126 switch (event->type) {
127 case ButtonPress:
128 case ButtonRelease:
129 return event->xbutton.time;
130 case KeyPress:
131 case KeyRelease:
132 return event->xkey.time;
133 case MotionNotify:
134 return event->xmotion.time;
135 case EnterNotify:
136 case LeaveNotify:
137 return event->xcrossing.time;
138 case PropertyNotify:
139 return event->xproperty.time;
140 case SelectionClear:
141 return event->xselectionclear.time;
142 case SelectionRequest:
143 return event->xselectionrequest.time;
144 case SelectionNotify:
145 return event->xselection.time;
146 default:
147 return screen->lastEventTime;
152 void
153 W_CallDestroyHandlers(W_View *view)
155 XEvent event;
156 WMArrayIterator iter;
157 W_EventHandler *hPtr;
160 event.type = DestroyNotify;
161 event.xdestroywindow.window = view->window;
162 event.xdestroywindow.event = view->window;
164 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
165 if (hPtr->eventMask & StructureNotifyMask) {
166 (*hPtr->proc)(&event, hPtr->clientData);
173 void
174 WMSetViewNextResponder(WMView *view, WMView *responder)
176 /* set the widget to receive keyboard events that aren't handled
177 * by this widget */
179 view->nextResponder = responder;
183 void
184 WMRelayToNextResponder(WMView *view, XEvent *event)
186 unsigned long mask = eventMasks[event->xany.type];
188 if (view->nextResponder) {
189 WMView *next = view->nextResponder;
190 W_EventHandler *hPtr;
191 WMArrayIterator iter;
193 WM_ITERATE_ARRAY(next->eventHandlers, hPtr, iter) {
194 if ((hPtr->eventMask & mask)) {
195 (*hPtr->proc)(event, hPtr->clientData);
203 WMHandleEvent(XEvent *event)
205 W_EventHandler *hPtr;
206 W_View *view, *toplevel;
207 unsigned long mask;
208 Window window;
209 WMArrayIterator iter;
211 if (event->type == MappingNotify) {
212 XRefreshKeyboardMapping(&event->xmapping);
213 return True;
216 if (XFilterEvent(event, None) == True) {
217 return False;
220 mask = eventMasks[event->xany.type];
222 window = event->xany.window;
224 /* diferentiate SubstructureNotify with StructureNotify */
225 if (mask == StructureNotifyMask) {
226 if (event->xmap.event != event->xmap.window) {
227 mask = SubstructureNotifyMask;
228 window = event->xmap.event;
231 view = W_GetViewForXWindow(event->xany.display, window);
233 if (!view) {
234 if (extraEventHandler)
235 (extraEventHandler)(event);
237 return False;
240 view->screen->lastEventTime = getEventTime(view->screen, event);
242 toplevel = W_TopLevelOfView(view);
244 if (event->type == SelectionNotify || event->type == SelectionClear
245 || event->type == SelectionRequest) {
246 /* handle selection related events */
247 W_HandleSelectionEvent(event);
251 /* if it's a key event, redispatch it to the focused control */
252 if (mask & (KeyPressMask|KeyReleaseMask)) {
253 W_View *focused = W_FocusedViewOfToplevel(toplevel);
255 if (focused) {
256 view = focused;
260 /* compress Motion events */
261 if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
262 while (XPending(event->xmotion.display)) {
263 XEvent ev;
264 XPeekEvent(event->xmotion.display, &ev);
265 if (ev.type == MotionNotify
266 && event->xmotion.window == ev.xmotion.window
267 && event->xmotion.subwindow == ev.xmotion.subwindow) {
268 /* replace events */
269 XNextEvent(event->xmotion.display, event);
270 } else break;
274 /* compress expose events */
275 if (event->type == Expose && !view->flags.dontCompressExpose) {
276 while (XCheckTypedWindowEvent(event->xexpose.display, view->window,
277 Expose, event));
281 if (view->screen->modalLoop && toplevel!=view->screen->modalView
282 && !toplevel->flags.worksWhenModal) {
283 if (event->type == KeyPress || event->type == KeyRelease
284 || event->type == MotionNotify || event->type == ButtonPress
285 || event->type == ButtonRelease
286 || event->type == FocusIn || event->type == FocusOut) {
287 return True;
291 /* do balloon stuffs */
292 if (event->type == EnterNotify)
293 W_BalloonHandleEnterView(view);
294 else if (event->type == LeaveNotify)
295 W_BalloonHandleLeaveView(view);
297 /* This is a hack. It will make the panel be secure while
298 * the event handlers are handled, as some event handler
299 * might destroy the widget. */
300 W_RetainView(toplevel);
302 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
303 if ((hPtr->eventMask & mask)) {
304 (*hPtr->proc)(event, hPtr->clientData);
307 #if 0
308 /* pass the event to the top level window of the widget */
309 /* TODO: change this to a responder chain */
310 if (view->parent != NULL) {
311 vPtr = view;
312 while (vPtr->parent != NULL)
313 vPtr = vPtr->parent;
315 WM_ITERATE_ARRAY(vPtr->eventHandlers, hPtr, iter) {
316 if (hPtr->eventMask & mask) {
317 (*hPtr->proc)(event, hPtr->clientData);
321 #endif
322 /* save button click info to track double-clicks */
323 if (view->screen->ignoreNextDoubleClick) {
324 view->screen->ignoreNextDoubleClick = 0;
325 } else {
326 if (event->type == ButtonPress) {
327 view->screen->lastClickWindow = event->xbutton.window;
328 view->screen->lastClickTime = event->xbutton.time;
332 if (event->type == ClientMessage) {
333 /* must be handled at the end, for such message can destroy the view */
334 W_HandleDNDClientMessage(toplevel, &event->xclient);
337 W_ReleaseView(toplevel);
339 return True;
344 WMIsDoubleClick(XEvent *event)
346 W_View *view;
348 if (event->type != ButtonPress)
349 return False;
351 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
353 if (!view)
354 return False;
356 if (view->screen->lastClickWindow != event->xbutton.window)
357 return False;
359 if (event->xbutton.time - view->screen->lastClickTime
360 < WINGsConfiguration.doubleClickDelay) {
361 view->screen->lastClickTime = 0;
362 view->screen->lastClickWindow = None;
363 view->screen->ignoreNextDoubleClick = 1;
364 return True;
365 } else
366 return False;
371 * Check for X and input events. If X events are present input events will
372 * not be checked.
374 * Return value: True if a X event is available or any input event was
375 * processed, false otherwise (including return because of
376 * some timer handler expired).
378 * If waitForInput is False, it will just peek for available input and return
379 * without processing. Return vaue will be True if input is available.
381 * If waitForInput is True, it will wait until an input event arrives on the
382 * registered input handlers and ConnectionNumber(dpy), or will return when
383 * a timer handler expires if no input event arrived until then.
385 static Bool
386 waitForEvent(Display *dpy, unsigned long xeventmask, Bool waitForInput)
388 XSync(dpy, False);
389 if (xeventmask==0) {
390 if (XPending(dpy))
391 return True;
392 } else {
393 XEvent ev;
394 if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
395 XPutBackEvent(dpy, &ev);
396 return True;
400 return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
404 void
405 WMNextEvent(Display *dpy, XEvent *event)
407 /* Check any expired timers */
408 W_CheckTimerHandlers();
410 while (XPending(dpy) == 0) {
411 /* Do idle and timer stuff while there are no input or X events */
412 while (!waitForEvent(dpy, 0, False) && W_CheckIdleHandlers()) {
413 /* dispatch timer events */
414 W_CheckTimerHandlers();
418 * Make sure that new events did not arrive while we were doing
419 * timer/idle stuff. Or we might block forever waiting for
420 * an event that already arrived.
422 /* wait for something to happen or a timer to expire */
423 waitForEvent(dpy, 0, True);
425 /* Check any expired timers */
426 W_CheckTimerHandlers();
429 XNextEvent(dpy, event);
433 void
434 WMMaskEvent(Display *dpy, long mask, XEvent *event)
436 /* Check any expired timers */
437 W_CheckTimerHandlers();
439 while (!XCheckMaskEvent(dpy, mask, event)) {
440 /* Do idle and timer stuff while there are no input or X events */
441 while (!waitForEvent(dpy, mask, False) && W_CheckIdleHandlers()) {
442 W_CheckTimerHandlers();
445 if (XCheckMaskEvent(dpy, mask, event))
446 return;
448 /* Wait for input on the X connection socket or another input handler */
449 waitForEvent(dpy, mask, True);
451 /* Check any expired timers */
452 W_CheckTimerHandlers();
457 Bool
458 WMScreenPending(WMScreen *scr)
460 if (XPending(scr->display))
461 return True;
462 else
463 return False;
467 WMEventHook*
468 WMHookEventHandler(WMEventHook *handler)
470 WMEventHook *oldHandler = extraEventHandler;
472 extraEventHandler = handler;
474 return oldHandler;