Revert "Prevent border drifting."
[wmaker-crm.git] / WINGs / wevent.c
blob8f7a5b6f40ba506a2ec5903ecba9bf08e72ec4d9
2 /*
3 * This event handling stuff was inspired on Tk.
4 */
6 #include "WINGsP.h"
8 /* table to map event types to event masks */
9 static unsigned long eventMasks[] = {
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 */
49 /* hook for other toolkits or wmaker process their events */
50 static WMEventHook *extraEventHandler = NULL;
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.
59 void WMCreateEventHandler(WMView * view, unsigned long mask, WMEventProc * eventProc, void *clientData)
61 W_EventHandler *hPtr;
62 WMArrayIterator iter;
64 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
65 if (hPtr->clientData == clientData && hPtr->proc == eventProc) {
66 hPtr->eventMask |= mask;
67 return;
71 hPtr = wmalloc(sizeof(W_EventHandler));
73 /* select events for window */
74 hPtr->eventMask = mask;
75 hPtr->proc = eventProc;
76 hPtr->clientData = clientData;
78 WMAddToArray(view->eventHandlers, hPtr);
81 static int matchHandler(const void *item, const void *cdata)
83 #define H1 ((W_EventHandler*)item)
84 #define H2 ((W_EventHandler*)cdata)
86 return (H1->eventMask == H2->eventMask && H1->proc == H2->proc && H1->clientData == H2->clientData);
90 * WMDeleteEventHandler--
91 * Delete event handler matching arguments from windows
92 * event handler list.
95 void WMDeleteEventHandler(WMView * view, unsigned long mask, WMEventProc * eventProc, void *clientData)
97 W_EventHandler tmp;
99 tmp.eventMask = mask;
100 tmp.proc = eventProc;
101 tmp.clientData = clientData;
102 WMRemoveFromArrayMatching(view->eventHandlers, matchHandler, (void *)&tmp);
105 static Time getEventTime(WMScreen * screen, XEvent * event)
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;
132 void W_CallDestroyHandlers(W_View * view)
134 XEvent event;
135 WMArrayIterator iter;
136 W_EventHandler *hPtr;
138 event.type = DestroyNotify;
139 event.xdestroywindow.window = view->window;
140 event.xdestroywindow.event = view->window;
142 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
143 if (hPtr->eventMask & StructureNotifyMask) {
144 (*hPtr->proc) (&event, hPtr->clientData);
149 void WMSetViewNextResponder(WMView * view, WMView * responder)
151 /* set the widget to receive keyboard events that aren't handled
152 * by this widget */
154 view->nextResponder = responder;
157 void WMRelayToNextResponder(WMView * view, XEvent * event)
159 unsigned long mask = eventMasks[event->xany.type];
161 if (view->nextResponder) {
162 WMView *next = view->nextResponder;
163 W_EventHandler *hPtr;
164 WMArrayIterator iter;
166 WM_ITERATE_ARRAY(next->eventHandlers, hPtr, iter) {
167 if ((hPtr->eventMask & mask)) {
168 (*hPtr->proc) (event, hPtr->clientData);
174 int WMHandleEvent(XEvent * event)
176 W_EventHandler *hPtr;
177 W_View *view, *toplevel;
178 unsigned long mask;
179 Window window;
180 WMArrayIterator iter;
182 if (event->type == MappingNotify) {
183 XRefreshKeyboardMapping(&event->xmapping);
184 return True;
187 if (XFilterEvent(event, None) == True) {
188 return False;
191 mask = eventMasks[event->xany.type];
193 window = event->xany.window;
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;
202 view = W_GetViewForXWindow(event->xany.display, window);
204 if (!view) {
205 if (extraEventHandler)
206 (extraEventHandler) (event);
208 return False;
211 view->screen->lastEventTime = getEventTime(view->screen, event);
213 toplevel = W_TopLevelOfView(view);
215 if (event->type == SelectionNotify || event->type == SelectionClear || event->type == SelectionRequest) {
216 /* handle selection related events */
217 W_HandleSelectionEvent(event);
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);
225 if (focused) {
226 view = focused;
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;
245 /* compress expose events */
246 if (event->type == Expose && !view->flags.dontCompressExpose) {
247 while (XCheckTypedWindowEvent(event->xexpose.display, view->window, Expose, event)) ;
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;
258 /* do balloon stuffs */
259 if (event->type == EnterNotify)
260 W_BalloonHandleEnterView(view);
261 else if (event->type == LeaveNotify)
262 W_BalloonHandleLeaveView(view);
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);
269 WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
270 if ((hPtr->eventMask & mask)) {
271 (*hPtr->proc) (event, hPtr->clientData);
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;
282 WM_ITERATE_ARRAY(vPtr->eventHandlers, hPtr, iter) {
283 if (hPtr->eventMask & mask) {
284 (*hPtr->proc) (event, hPtr->clientData);
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;
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);
304 W_ReleaseView(toplevel);
306 return True;
309 int WMIsDoubleClick(XEvent * event)
311 W_View *view;
313 if (event->type != ButtonPress)
314 return False;
316 view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
318 if (!view)
319 return False;
321 if (view->screen->lastClickWindow != event->xbutton.window)
322 return False;
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;
334 * Check for X and input events. If X events are present input events will
335 * not be checked.
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).
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.
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.
348 static Bool waitForEvent(Display * dpy, unsigned long xeventmask, Bool waitForInput)
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;
362 return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
365 void WMNextEvent(Display * dpy, XEvent * event)
367 /* Check any expired timers */
368 W_CheckTimerHandlers();
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();
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.
382 /* wait for something to happen or a timer to expire */
383 waitForEvent(dpy, 0, True);
385 /* Check any expired timers */
386 W_CheckTimerHandlers();
389 XNextEvent(dpy, event);
392 void WMMaskEvent(Display * dpy, long mask, XEvent * event)
394 /* Check any expired timers */
395 W_CheckTimerHandlers();
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();
403 if (XCheckMaskEvent(dpy, mask, event))
404 return;
406 /* Wait for input on the X connection socket or another input handler */
407 waitForEvent(dpy, mask, True);
409 /* Check any expired timers */
410 W_CheckTimerHandlers();
414 Bool WMScreenPending(WMScreen * scr)
416 if (XPending(scr->display))
417 return True;
418 else
419 return False;
422 WMEventHook *WMHookEventHandler(WMEventHook * handler)
424 WMEventHook *oldHandler = extraEventHandler;
426 extraEventHandler = handler;
428 return oldHandler;