]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
Ramove unneeded frame_get_window
[mikachu/openbox.git] / openbox / event.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    event.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "event.h"
21 #include "debug.h"
22 #include "window.h"
23 #include "openbox.h"
24 #include "dock.h"
25 #include "actions.h"
26 #include "client.h"
27 #include "config.h"
28 #include "screen.h"
29 #include "engine_interface.h"
30 #include "grab.h"
31 #include "menu.h"
32 #include "prompt.h"
33 #include "menuframe.h"
34 #include "keyboard.h"
35 #include "mouse.h"
36 #include "focus.h"
37 #include "focus_cycle.h"
38 #include "moveresize.h"
39 #include "group.h"
40 #include "stacking.h"
41 #include "ping.h"
42 #include "obt/display.h"
43 #include "obt/prop.h"
44 #include "obt/keyboard.h"
45
46 #include <X11/Xlib.h>
47 #include <X11/Xatom.h>
48 #include <glib.h>
49
50 #ifdef HAVE_SYS_SELECT_H
51 #  include <sys/select.h>
52 #endif
53 #ifdef HAVE_SIGNAL_H
54 #  include <signal.h>
55 #endif
56 #ifdef HAVE_UNISTD_H
57 #  include <unistd.h> /* for usleep() */
58 #endif
59 #ifdef XKB
60 #  include <X11/XKBlib.h>
61 #endif
62
63 #ifdef USE_SM
64 #include <X11/ICE/ICElib.h>
65 #endif
66
67 typedef struct
68 {
69     gboolean ignored;
70 } ObEventData;
71
72 typedef struct
73 {
74     ObClient *client;
75     Time time;
76     gulong serial;
77 } ObFocusDelayData;
78
79 typedef struct
80 {
81     gulong start; /* inclusive */
82     gulong end;   /* inclusive */
83 } ObSerialRange;
84
85 static void event_process(const XEvent *e, gpointer data);
86 static void event_handle_root(XEvent *e);
87 static gboolean event_handle_menu_input(XEvent *e);
88 static void event_handle_menu(ObMenuFrame *frame, XEvent *e);
89 static gboolean event_handle_prompt(ObPrompt *p, XEvent *e);
90 static void event_handle_dock(ObDock *s, XEvent *e);
91 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
92 static void event_handle_client(ObClient *c, XEvent *e);
93 static void event_handle_user_input(ObClient *client, XEvent *e);
94 static gboolean is_enter_focus_event_ignored(gulong serial);
95 static void event_ignore_enter_range(gulong start, gulong end);
96
97 static void focus_delay_dest(gpointer data);
98 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
99 static gboolean focus_delay_func(gpointer data);
100 static void focus_delay_client_dest(ObClient *client, gpointer data);
101
102 Time event_curtime = CurrentTime;
103 Time event_last_user_time = CurrentTime;
104 /*! The serial of the current X event */
105
106 static gulong event_curserial;
107 static gboolean focus_left_screen = FALSE;
108 /*! A list of ObSerialRanges which are to be ignored for mouse enter events */
109 static GSList *ignore_serials = NULL;
110
111 #ifdef USE_SM
112 static void ice_handler(gint fd, gpointer conn)
113 {
114     Bool b;
115     IceProcessMessages(conn, NULL, &b);
116 }
117
118 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
119                       IcePointer *watch_data)
120 {
121     static gint fd = -1;
122
123     if (opening) {
124         fd = IceConnectionNumber(conn);
125         obt_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
126     } else {
127         obt_main_loop_fd_remove(ob_main_loop, fd);
128         fd = -1;
129     }
130 }
131 #endif
132
133 void event_startup(gboolean reconfig)
134 {
135     if (reconfig) return;
136
137     obt_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
138
139 #ifdef USE_SM
140     IceAddConnectionWatch(ice_watch, NULL);
141 #endif
142
143     client_add_destroy_notify(focus_delay_client_dest, NULL);
144 }
145
146 void event_shutdown(gboolean reconfig)
147 {
148     if (reconfig) return;
149
150 #ifdef USE_SM
151     IceRemoveConnectionWatch(ice_watch, NULL);
152 #endif
153
154     client_remove_destroy_notify(focus_delay_client_dest);
155 }
156
157 static Window event_get_window(XEvent *e)
158 {
159     Window window;
160
161     /* pick a window */
162     switch (e->type) {
163     case SelectionClear:
164         window = obt_root(ob_screen);
165         break;
166     case CreateNotify:
167         window = e->xcreatewindow.window;
168         break;
169     case MapRequest:
170         window = e->xmaprequest.window;
171         break;
172     case MapNotify:
173         window = e->xmap.window;
174         break;
175     case UnmapNotify:
176         window = e->xunmap.window;
177         break;
178     case DestroyNotify:
179         window = e->xdestroywindow.window;
180         break;
181     case ConfigureRequest:
182         window = e->xconfigurerequest.window;
183         break;
184     case ConfigureNotify:
185         window = e->xconfigure.window;
186         break;
187     default:
188 #ifdef XKB
189         if (obt_display_extension_xkb &&
190             e->type == obt_display_extension_xkb_basep)
191         {
192             switch (((XkbAnyEvent*)e)->xkb_type) {
193             case XkbBellNotify:
194                 window = ((XkbBellNotifyEvent*)e)->window;
195             default:
196                 window = None;
197             }
198         } else
199 #endif
200 #ifdef SYNC
201         if (obt_display_extension_sync &&
202             e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
203         {
204             window = None;
205         } else
206 #endif
207             window = e->xany.window;
208     }
209     return window;
210 }
211
212 static void event_set_curtime(XEvent *e)
213 {
214     Time t = CurrentTime;
215
216     /* grab the lasttime and hack up the state */
217     switch (e->type) {
218     case ButtonPress:
219     case ButtonRelease:
220         t = e->xbutton.time;
221         break;
222     case KeyPress:
223         t = e->xkey.time;
224         break;
225     case KeyRelease:
226         t = e->xkey.time;
227         break;
228     case MotionNotify:
229         t = e->xmotion.time;
230         break;
231     case PropertyNotify:
232         t = e->xproperty.time;
233         break;
234     case EnterNotify:
235     case LeaveNotify:
236         t = e->xcrossing.time;
237         break;
238     default:
239 #ifdef SYNC
240         if (obt_display_extension_sync &&
241             e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
242         {
243             t = ((XSyncAlarmNotifyEvent*)e)->time;
244         }
245 #endif
246         /* if more event types are anticipated, get their timestamp
247            explicitly */
248         break;
249     }
250
251     /* watch that if we get an event earlier than the last specified user_time,
252        which can happen if the clock goes backwards, we erase the last
253        specified user_time */
254     if (t && event_last_user_time && event_time_after(event_last_user_time, t))
255         event_last_user_time = CurrentTime;
256
257     event_curtime = t;
258 }
259
260 static void event_hack_mods(XEvent *e)
261 {
262 #ifdef XKB
263     XkbStateRec xkb_state;
264 #endif
265
266     switch (e->type) {
267     case ButtonPress:
268     case ButtonRelease:
269         e->xbutton.state = obt_keyboard_only_modmasks(e->xbutton.state);
270         break;
271     case KeyPress:
272         e->xkey.state = obt_keyboard_only_modmasks(e->xkey.state);
273         break;
274     case KeyRelease:
275 #ifdef XKB
276         /* If XKB is present, then the modifiers are all strange from its
277            magic.  Our X core protocol stuff won't work, so we use this to
278            find what the modifier state is instead. */
279         if (XkbGetState(obt_display, XkbUseCoreKbd, &xkb_state) == Success)
280             e->xkey.state =
281                 obt_keyboard_only_modmasks(xkb_state.compat_state);
282         else
283 #endif
284         {
285             e->xkey.state = obt_keyboard_only_modmasks(e->xkey.state);
286             /* remove from the state the mask of the modifier key being
287                released, if it is a modifier key being released that is */
288             e->xkey.state &= ~obt_keyboard_keycode_to_modmask(e->xkey.keycode);
289         }
290         break;
291     case MotionNotify:
292         e->xmotion.state = obt_keyboard_only_modmasks(e->xmotion.state);
293         /* compress events */
294         {
295             XEvent ce;
296             while (XCheckTypedWindowEvent(obt_display, e->xmotion.window,
297                                           e->type, &ce)) {
298                 e->xmotion.x = ce.xmotion.x;
299                 e->xmotion.y = ce.xmotion.y;
300                 e->xmotion.x_root = ce.xmotion.x_root;
301                 e->xmotion.y_root = ce.xmotion.y_root;
302             }
303         }
304         break;
305     }
306 }
307
308 static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
309 {
310     gint mode = e->xfocus.mode;
311     gint detail = e->xfocus.detail;
312     Window win = e->xany.window;
313
314     if (e->type == FocusIn) {
315         /* These are ones we never want.. */
316
317         /* This means focus was given by a keyboard/mouse grab. */
318         if (mode == NotifyGrab)
319             return FALSE;
320         /* This means focus was given back from a keyboard/mouse grab. */
321         if (mode == NotifyUngrab)
322             return FALSE;
323
324         /* These are the ones we want.. */
325
326         if (win == obt_root(ob_screen)) {
327             /* If looking for a focus in on a client, then always return
328                FALSE for focus in's to the root window */
329             if (in_client_only)
330                 return FALSE;
331             /* This means focus reverted off of a client */
332             else if (detail == NotifyPointerRoot ||
333                      detail == NotifyDetailNone ||
334                      detail == NotifyInferior ||
335                      /* This means focus got here from another screen */
336                      detail == NotifyNonlinear)
337                 return TRUE;
338             else
339                 return FALSE;
340         }
341
342         /* It was on a client, was it a valid one?
343            It's possible to get a FocusIn event for a client that was managed
344            but has disappeared.
345         */
346         if (in_client_only) {
347             ObWindow *w = window_find(e->xfocus.window);
348             if (!w || !WINDOW_IS_CLIENT(w))
349                 return FALSE;
350         }
351         else {
352             /* This means focus reverted to parent from the client (this
353                happens often during iconify animation) */
354             if (detail == NotifyInferior)
355                 return TRUE;
356         }
357
358         /* This means focus moved from the root window to a client */
359         if (detail == NotifyVirtual)
360             return TRUE;
361         /* This means focus moved from one client to another */
362         if (detail == NotifyNonlinearVirtual)
363             return TRUE;
364
365         /* Otherwise.. */
366         return FALSE;
367     } else {
368         g_assert(e->type == FocusOut);
369
370         /* These are ones we never want.. */
371
372         /* This means focus was taken by a keyboard/mouse grab. */
373         if (mode == NotifyGrab)
374             return FALSE;
375         /* This means focus was grabbed on a window and it was released. */
376         if (mode == NotifyUngrab)
377             return FALSE;
378
379         /* Focus left the root window revertedto state */
380         if (win == obt_root(ob_screen))
381             return FALSE;
382
383         /* These are the ones we want.. */
384
385         /* This means focus moved from a client to the root window */
386         if (detail == NotifyVirtual)
387             return TRUE;
388         /* This means focus moved from one client to another */
389         if (detail == NotifyNonlinearVirtual)
390             return TRUE;
391
392         /* Otherwise.. */
393         return FALSE;
394     }
395 }
396
397 static Bool event_look_for_focusin(Display *d, XEvent *e, XPointer arg)
398 {
399     return e->type == FocusIn && wanted_focusevent(e, FALSE);
400 }
401
402 static Bool event_look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
403 {
404     return e->type == FocusIn && wanted_focusevent(e, TRUE);
405 }
406
407 static void print_focusevent(XEvent *e)
408 {
409     gint mode = e->xfocus.mode;
410     gint detail = e->xfocus.detail;
411     Window win = e->xany.window;
412     const gchar *modestr, *detailstr;
413
414     switch (mode) {
415     case NotifyNormal:       modestr="NotifyNormal";       break;
416     case NotifyGrab:         modestr="NotifyGrab";         break;
417     case NotifyUngrab:       modestr="NotifyUngrab";       break;
418     case NotifyWhileGrabbed: modestr="NotifyWhileGrabbed"; break;
419     }
420     switch (detail) {
421     case NotifyAncestor:    detailstr="NotifyAncestor";    break;
422     case NotifyVirtual:     detailstr="NotifyVirtual";     break;
423     case NotifyInferior:    detailstr="NotifyInferior";    break;
424     case NotifyNonlinear:   detailstr="NotifyNonlinear";   break;
425     case NotifyNonlinearVirtual: detailstr="NotifyNonlinearVirtual"; break;
426     case NotifyPointer:     detailstr="NotifyPointer";     break;
427     case NotifyPointerRoot: detailstr="NotifyPointerRoot"; break;
428     case NotifyDetailNone:  detailstr="NotifyDetailNone";  break;
429     }
430
431     if (mode == NotifyGrab || mode == NotifyUngrab)
432         return;
433
434     g_assert(modestr);
435     g_assert(detailstr);
436     ob_debug_type(OB_DEBUG_FOCUS, "Focus%s 0x%x mode=%s detail=%s",
437                   (e->xfocus.type == FocusIn ? "In" : "Out"),
438                   win,
439                   modestr, detailstr);
440
441 }
442
443 static gboolean event_ignore(XEvent *e, ObClient *client)
444 {
445     switch(e->type) {
446     case FocusIn:
447         print_focusevent(e);
448         if (!wanted_focusevent(e, FALSE))
449             return TRUE;
450         break;
451     case FocusOut:
452         print_focusevent(e);
453         if (!wanted_focusevent(e, FALSE))
454             return TRUE;
455         break;
456     }
457     return FALSE;
458 }
459
460 static void event_process(const XEvent *ec, gpointer data)
461 {
462     XEvent ee, *e;
463     ObEventData *ed = data;
464
465     Window window;
466     ObClient *client = NULL;
467     ObDock *dock = NULL;
468     ObDockApp *dockapp = NULL;
469     ObWindow *obwin = NULL;
470     ObMenuFrame *menu = NULL;
471     ObPrompt *prompt = NULL;
472
473     /* make a copy we can mangle */
474     ee = *ec;
475     e = &ee;
476
477     window = event_get_window(e);
478     if (window == obt_root(ob_screen))
479         /* don't do any lookups, waste of cpu */;
480     else if ((obwin = window_find(window))) {
481         switch (obwin->type) {
482         case OB_WINDOW_CLASS_DOCK:
483             dock = WINDOW_AS_DOCK(obwin);
484             break;
485         case OB_WINDOW_CLASS_CLIENT:
486             client = WINDOW_AS_CLIENT(obwin);
487             /* events on clients can be events on prompt windows too */
488             prompt = client->prompt;
489             break;
490         case OB_WINDOW_CLASS_MENUFRAME:
491             menu = WINDOW_AS_MENUFRAME(obwin);
492             break;
493         case OB_WINDOW_CLASS_INTERNAL:
494             /* we don't do anything with events directly on these windows */
495             break;
496         case OB_WINDOW_CLASS_PROMPT:
497             prompt = WINDOW_AS_PROMPT(obwin);
498             break;
499         }
500     }
501     else
502         dockapp = dock_find_dockapp(window);
503
504     event_set_curtime(e);
505     event_curserial = e->xany.serial;
506     event_hack_mods(e);
507     if (event_ignore(e, client)) {
508         if (ed)
509             ed->ignored = TRUE;
510         return;
511     } else if (ed)
512             ed->ignored = FALSE;
513
514     /* deal with it in the kernel */
515
516     if (e->type == FocusIn) {
517         if (client &&
518             e->xfocus.detail == NotifyInferior)
519         {
520             ob_debug_type(OB_DEBUG_FOCUS,
521                           "Focus went to the frame window");
522
523             focus_left_screen = FALSE;
524
525             focus_fallback(FALSE, config_focus_under_mouse, TRUE, TRUE);
526
527             /* We don't get a FocusOut for this case, because it's just moving
528                from our Inferior up to us. This happens when iconifying a
529                window with RevertToParent focus */
530             frame_engine->frame_set_is_focus(client->frame, FALSE);
531             frame_engine->frame_update_layout (client->frame, client->area, FALSE, FALSE);
532             frame_engine->frame_update_skin(client->frame);
533             /* focus_set_client(NULL) has already been called */
534         }
535         else if (e->xfocus.detail == NotifyPointerRoot ||
536                  e->xfocus.detail == NotifyDetailNone ||
537                  e->xfocus.detail == NotifyInferior ||
538                  e->xfocus.detail == NotifyNonlinear)
539         {
540             XEvent ce;
541
542             ob_debug_type(OB_DEBUG_FOCUS,
543                           "Focus went to root or pointer root/none");
544
545             if (e->xfocus.detail == NotifyInferior ||
546                 e->xfocus.detail == NotifyNonlinear)
547             {
548                 focus_left_screen = FALSE;
549             }
550
551             /* If another FocusIn is in the queue then don't fallback yet. This
552                fixes the fun case of:
553                window map -> send focusin
554                window unmap -> get focusout
555                window map -> send focusin
556                get first focus out -> fall back to something (new window
557                  hasn't received focus yet, so something else) -> send focusin
558                which means the "something else" is the last thing to get a
559                focusin sent to it, so the new window doesn't end up with focus.
560
561                But if the other focus in is something like PointerRoot then we
562                still want to fall back.
563             */
564             if (XCheckIfEvent(obt_display, &ce, event_look_for_focusin_client,
565                               NULL))
566             {
567                 XPutBackEvent(obt_display, &ce);
568                 ob_debug_type(OB_DEBUG_FOCUS,
569                               "  but another FocusIn is coming");
570             } else {
571                 /* Focus has been reverted.
572
573                    FocusOut events come after UnmapNotify, so we don't need to
574                    worry about focusing an invalid window
575                 */
576
577                 if (!focus_left_screen)
578                     focus_fallback(FALSE, config_focus_under_mouse,
579                                    TRUE, TRUE);
580             }
581         }
582         else if (!client)
583         {
584             ob_debug_type(OB_DEBUG_FOCUS,
585                           "Focus went to a window that is already gone");
586
587             /* If you send focus to a window and then it disappears, you can
588                get the FocusIn for it, after it is unmanaged.
589                Just wait for the next FocusOut/FocusIn pair, but make note that
590                the window that was focused no longer is. */
591             focus_set_client(NULL);
592         }
593         else if (client != focus_client) {
594             focus_left_screen = FALSE;
595             frame_engine->frame_set_is_focus(client->frame, TRUE);
596             frame_engine->frame_update_layout (client->frame, client->area, FALSE, FALSE);
597             frame_engine->frame_update_skin (client->frame);
598             focus_set_client(client);
599             client_calc_layer(client);
600             client_bring_helper_windows(client);
601         }
602     } else if (e->type == FocusOut) {
603         XEvent ce;
604
605         /* Look for the followup FocusIn */
606         if (!XCheckIfEvent(obt_display, &ce, event_look_for_focusin, NULL)) {
607             /* There is no FocusIn, this means focus went to a window that
608                is not being managed, or a window on another screen. */
609             Window win, root;
610             gint i;
611             guint u;
612             obt_display_ignore_errors(TRUE);
613             if (XGetInputFocus(obt_display, &win, &i) &&
614                 XGetGeometry(obt_display, win, &root, &i,&i,&u,&u,&u,&u) &&
615                 root != obt_root(ob_screen))
616             {
617                 ob_debug_type(OB_DEBUG_FOCUS,
618                               "Focus went to another screen !");
619                 focus_left_screen = TRUE;
620             }
621             else
622                 ob_debug_type(OB_DEBUG_FOCUS,
623                               "Focus went to a black hole !");
624             obt_display_ignore_errors(FALSE);
625             /* nothing is focused */
626             focus_set_client(NULL);
627         } else {
628             /* Focus moved, so process the FocusIn event */
629             ObEventData ed = { .ignored = FALSE };
630             event_process(&ce, &ed);
631             if (ed.ignored) {
632                 /* The FocusIn was ignored, this means it was on a window
633                    that isn't a client. */
634                 ob_debug_type(OB_DEBUG_FOCUS,
635                               "Focus went to an unmanaged window 0x%x !",
636                               ce.xfocus.window);
637                 focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
638             }
639         }
640
641         if (client && client != focus_client) {
642           frame_engine->frame_set_is_focus(client->frame, FALSE);
643           frame_engine->frame_update_layout (client->frame, client->area, FALSE, FALSE);
644           frame_engine->frame_update_skin(client->frame);
645             /* focus_set_client(NULL) has already been called in this
646                section or by focus_fallback */
647         }
648     }
649     else if (client)
650         event_handle_client(client, e);
651     else if (dockapp)
652         event_handle_dockapp(dockapp, e);
653     else if (dock)
654         event_handle_dock(dock, e);
655     else if (menu)
656         event_handle_menu(menu, e);
657     else if (window == obt_root(ob_screen))
658         event_handle_root(e);
659     else if (e->type == MapRequest)
660         window_manage(window);
661     else if (e->type == MappingNotify) {
662         /* keyboard layout changes for modifier mapping changes. reload the
663            modifier map, and rebind all the key bindings as appropriate */
664         ob_debug("Kepboard map changed. Reloading keyboard bindings.");
665         obt_keyboard_reload();
666         keyboard_rebind();
667     }
668     else if (e->type == ClientMessage) {
669         /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
670            windows that are not managed yet. */
671         if (e->xclient.message_type ==
672             OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS))
673         {
674             /* Pretend to manage the client, getting information used to
675                determine its decorations */
676             ObClient *c = client_fake_manage(e->xclient.window);
677             gulong vals[4];
678
679             /* set the frame extents on the window */
680             Strut size;
681             frame_engine->frame_get_size(c->frame, &size);
682             vals[0] = size.left;
683             vals[1] = size.right;
684             vals[2] = size.top;
685             vals[3] = size.bottom;
686             OBT_PROP_SETA32(e->xclient.window, NET_FRAME_EXTENTS,
687                             CARDINAL, vals, 4);
688
689             /* Free the pretend client */
690             client_fake_unmanage(c);
691         }
692     }
693     else if (e->type == ConfigureRequest) {
694         /* unhandled configure requests must be used to configure the
695            window directly */
696         XWindowChanges xwc;
697
698         xwc.x = e->xconfigurerequest.x;
699         xwc.y = e->xconfigurerequest.y;
700         xwc.width = e->xconfigurerequest.width;
701         xwc.height = e->xconfigurerequest.height;
702         xwc.border_width = e->xconfigurerequest.border_width;
703         xwc.sibling = e->xconfigurerequest.above;
704         xwc.stack_mode = e->xconfigurerequest.detail;
705
706         /* we are not to be held responsible if someone sends us an
707            invalid request! */
708         obt_display_ignore_errors(TRUE);
709         XConfigureWindow(obt_display, window,
710                          e->xconfigurerequest.value_mask, &xwc);
711         obt_display_ignore_errors(FALSE);
712     }
713 #ifdef SYNC
714     else if (obt_display_extension_sync &&
715              e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
716     {
717         XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
718         if (se->alarm == moveresize_alarm && frame_engine->moveresize_in_progress)
719             moveresize_event(e);
720     }
721 #endif
722
723     if (prompt && event_handle_prompt(prompt, e))
724         ;
725     else if (e->type == ButtonPress || e->type == ButtonRelease) {
726         /* If the button press was on some non-root window, or was physically
727            on the root window, then process it */
728         if (window != obt_root(ob_screen) ||
729             e->xbutton.subwindow == None)
730         {
731             event_handle_user_input(client, e);
732         }
733         /* Otherwise only process it if it was physically on an openbox
734            internal window */
735         else {
736             ObWindow *w;
737
738             if ((w = window_find(e->xbutton.subwindow)) &&
739                 WINDOW_IS_INTERNAL(w))
740             {
741                 event_handle_user_input(client, e);
742             }
743         }
744     }
745     else if (e->type == KeyPress || e->type == KeyRelease ||
746              e->type == MotionNotify)
747         event_handle_user_input(client, e);
748
749     /* if something happens and it's not from an XEvent, then we don't know
750        the time */
751     event_curtime = CurrentTime;
752     event_curserial = 0;
753 }
754
755 static void event_handle_root(XEvent *e)
756 {
757     Atom msgtype;
758
759     switch(e->type) {
760     case SelectionClear:
761         ob_debug("Another WM has requested to replace us. Exiting.");
762         ob_exit_replace();
763         break;
764
765     case ClientMessage:
766         if (e->xclient.format != 32) break;
767
768         msgtype = e->xclient.message_type;
769         if (msgtype == OBT_PROP_ATOM(NET_CURRENT_DESKTOP)) {
770             guint d = e->xclient.data.l[0];
771             if (d < screen_num_desktops) {
772                 event_curtime = e->xclient.data.l[1];
773                 if (event_curtime == 0)
774                     ob_debug_type(OB_DEBUG_APP_BUGS,
775                                   "_NET_CURRENT_DESKTOP message is missing "
776                                   "a timestamp");
777                 screen_set_desktop(d, TRUE);
778             }
779         } else if (msgtype == OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS)) {
780             guint d = e->xclient.data.l[0];
781             if (d > 0 && d <= 1000)
782                 screen_set_num_desktops(d);
783         } else if (msgtype == OBT_PROP_ATOM(NET_SHOWING_DESKTOP)) {
784             screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
785         } else if (msgtype == OBT_PROP_ATOM(OB_CONTROL)) {
786             ob_debug("OB_CONTROL: %d", e->xclient.data.l[0]);
787             if (e->xclient.data.l[0] == 1)
788                 ob_reconfigure();
789             else if (e->xclient.data.l[0] == 2)
790                 ob_restart();
791             else if (e->xclient.data.l[0] == 3)
792                 ob_exit(0);
793         } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
794             if ((Atom)e->xclient.data.l[0] == OBT_PROP_ATOM(NET_WM_PING))
795                 ping_got_pong(e->xclient.data.l[1]);
796         }
797         break;
798     case PropertyNotify:
799         if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_NAMES)) {
800             ob_debug("UPDATE DESKTOP NAMES");
801             screen_update_desktop_names();
802         }
803         else if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_LAYOUT))
804             screen_update_layout();
805         break;
806     case ConfigureNotify:
807 #ifdef XRANDR
808         XRRUpdateConfiguration(e);
809 #endif
810         screen_resize();
811         break;
812     default:
813         ;
814     }
815 }
816
817 void event_enter_client(ObClient *client)
818 {
819     g_assert(config_focus_follow);
820
821     if (is_enter_focus_event_ignored(event_curserial)) {
822         ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu\n"
823                       "on client 0x%x", event_curserial, client->w_client);
824         return;
825     }
826
827     if (client_enter_focusable(client) && client_can_focus(client)) {
828         if (config_focus_delay) {
829             ObFocusDelayData *data;
830
831             obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
832
833             data = g_new(ObFocusDelayData, 1);
834             data->client = client;
835             data->time = event_curtime;
836             data->serial = event_curserial;
837
838             obt_main_loop_timeout_add(ob_main_loop,
839                                       config_focus_delay * 1000,
840                                       focus_delay_func,
841                                       data, focus_delay_cmp, focus_delay_dest);
842         } else {
843             ObFocusDelayData data;
844             data.client = client;
845             data.time = event_curtime;
846             data.serial = event_curserial;
847             focus_delay_func(&data);
848         }
849     }
850 }
851
852 static void event_handle_client(ObClient *client, XEvent *e)
853 {
854     XEvent ce;
855     Atom msgtype;
856     ObFrameContext con;
857     static gint px = -1, py = -1;
858     static guint pb = 0;
859
860     switch (e->type) {
861     case ButtonPress:
862         /* save where the press occured for the first button pressed */
863         if (!pb) {
864             pb = e->xbutton.button;
865             px = e->xbutton.x;
866             py = e->xbutton.y;
867         }
868     case ButtonRelease:
869         /* Wheel buttons don't draw because they are an instant click, so it
870            is a waste of resources to go drawing it.
871            if the user is doing an intereactive thing, or has a menu open then
872            the mouse is grabbed (possibly) and if we get these events we don't
873            want to deal with them
874         */
875         if (!(e->xbutton.button == 4 || e->xbutton.button == 5) &&
876             !grab_on_keyboard())
877         {
878             /* use where the press occured */
879             con = plugin_frame_context(client, e->xbutton.window, px, py);
880             con = mouse_button_frame_context(con, e->xbutton.button,
881                                              e->xbutton.state);
882
883             if (e->type == ButtonRelease && e->xbutton.button == pb)
884                 pb = 0, px = py = -1;
885
886             ObFrameButton current_button = OB_BUTTON_NONE;
887
888             switch (con) {
889             case OB_FRAME_CONTEXT_MAXIMIZE:
890                 current_button = OB_BUTTON_MAX;
891                 break;
892             case OB_FRAME_CONTEXT_CLOSE:
893         current_button = OB_BUTTON_CLOSE;
894                 break;
895             case OB_FRAME_CONTEXT_ICONIFY:
896         current_button = OB_BUTTON_ICONIFY;
897                 break;
898             case OB_FRAME_CONTEXT_ALLDESKTOPS:
899         current_button = OB_BUTTON_DESK;
900                 break;
901             case OB_FRAME_CONTEXT_SHADE:
902         current_button = OB_BUTTON_SHADE;
903                 break;
904             default:
905                 /* nothing changes with clicks for any other contexts */
906                 break;
907             }
908
909             if (current_button)
910         frame_engine->frame_set_hover_flag (client->frame, current_button);
911         }
912         break;
913     case MotionNotify:
914         /* when there is a grab on the pointer, we won't get enter/leave
915            notifies, but we still get motion events */
916         if (grab_on_pointer()) break;
917
918         con = plugin_frame_context(client, e->xmotion.window,
919                             e->xmotion.x, e->xmotion.y);
920         switch (con) {
921         case OB_FRAME_CONTEXT_TITLEBAR:
922         case OB_FRAME_CONTEXT_TLCORNER:
923         case OB_FRAME_CONTEXT_TRCORNER:
924             /* we've left the button area inside the titlebar */
925             frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_NONE);
926             break;
927         case OB_FRAME_CONTEXT_MAXIMIZE:
928         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_MAX);
929             break;
930         case OB_FRAME_CONTEXT_ALLDESKTOPS:
931         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_DESK);
932             break;
933         case OB_FRAME_CONTEXT_SHADE:
934         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_SHADE);
935             break;
936         case OB_FRAME_CONTEXT_ICONIFY:
937         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_ICONIFY);
938             break;
939         case OB_FRAME_CONTEXT_CLOSE:
940         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_CLOSE);
941             break;
942         default:
943             break;
944         }
945         break;
946     case LeaveNotify:
947         con = plugin_frame_context(client, e->xcrossing.window,
948                             e->xcrossing.x, e->xcrossing.y);
949         switch (con) {
950         case OB_FRAME_CONTEXT_TITLEBAR:
951         case OB_FRAME_CONTEXT_TLCORNER:
952         case OB_FRAME_CONTEXT_TRCORNER:
953             /* we've left the button area inside the titlebar */
954         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_NONE);
955             break;
956         case OB_FRAME_CONTEXT_MAXIMIZE:
957         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_NONE);
958             break;
959         case OB_FRAME_CONTEXT_ALLDESKTOPS:
960         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_NONE);
961             break;
962         case OB_FRAME_CONTEXT_SHADE:
963         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_NONE);
964             break;
965         case OB_FRAME_CONTEXT_ICONIFY:
966         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_NONE);
967             break;
968         case OB_FRAME_CONTEXT_CLOSE:
969         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_NONE);
970             break;
971         case OB_FRAME_CONTEXT_FRAME:
972
973             ob_debug_type(OB_DEBUG_FOCUS,
974                           "%sNotify mode %d detail %d on %lx",
975                           (e->type == EnterNotify ? "Enter" : "Leave"),
976                           e->xcrossing.mode,
977                           e->xcrossing.detail, (client?client->w_client:0));
978             if (grab_on_keyboard())
979                 break;
980             if (config_focus_follow && config_focus_delay &&
981                 /* leave inferior events can happen when the mouse goes onto
982                    the window's border and then into the window before the
983                    delay is up */
984                 e->xcrossing.detail != NotifyInferior)
985             {
986                 obt_main_loop_timeout_remove_data(ob_main_loop,
987                                                   focus_delay_func,
988                                                   client, FALSE);
989             }
990             break;
991         default:
992             break;
993         }
994         break;
995     case EnterNotify:
996     {
997         con = plugin_frame_context(client, e->xcrossing.window,
998                             e->xcrossing.x, e->xcrossing.y);
999         switch (con) {
1000         case OB_FRAME_CONTEXT_MAXIMIZE:
1001         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_MAX);
1002             break;
1003         case OB_FRAME_CONTEXT_ALLDESKTOPS:
1004         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_DESK);
1005             break;
1006         case OB_FRAME_CONTEXT_SHADE:
1007         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_SHADE);
1008             break;
1009         case OB_FRAME_CONTEXT_ICONIFY:
1010         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_ICONIFY);
1011             break;
1012         case OB_FRAME_CONTEXT_CLOSE:
1013         frame_engine->frame_set_hover_flag(client->frame, OB_BUTTON_CLOSE);
1014             break;
1015         case OB_FRAME_CONTEXT_FRAME:
1016             if (grab_on_keyboard())
1017                 break;
1018             if (e->xcrossing.mode == NotifyGrab ||
1019                 e->xcrossing.mode == NotifyUngrab ||
1020                 /*ignore enters when we're already in the window */
1021                 e->xcrossing.detail == NotifyInferior)
1022             {
1023                 ob_debug_type(OB_DEBUG_FOCUS,
1024                               "%sNotify mode %d detail %d serial %lu on %lx "
1025                               "IGNORED",
1026                               (e->type == EnterNotify ? "Enter" : "Leave"),
1027                               e->xcrossing.mode,
1028                               e->xcrossing.detail,
1029                               e->xcrossing.serial,
1030                               client?client->w_client:0);
1031             }
1032             else {
1033                 ob_debug_type(OB_DEBUG_FOCUS,
1034                               "%sNotify mode %d detail %d serial %lu on %lx, "
1035                               "focusing window",
1036                               (e->type == EnterNotify ? "Enter" : "Leave"),
1037                               e->xcrossing.mode,
1038                               e->xcrossing.detail,
1039                               e->xcrossing.serial,
1040                               (client?client->w_client:0));
1041                 if (config_focus_follow)
1042                     event_enter_client(client);
1043             }
1044             break;
1045         default:
1046             break;
1047         }
1048         break;
1049     }
1050     case ConfigureRequest:
1051     {
1052         /* dont compress these unless you're going to watch for property
1053            notifies in between (these can change what the configure would
1054            do to the window).
1055            also you can't compress stacking events
1056         */
1057
1058         gint x, y, w, h;
1059         gboolean move = FALSE;
1060         gboolean resize = FALSE;
1061
1062         /* get the current area */
1063         RECT_TO_DIMS(client->area, x, y, w, h);
1064
1065         ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1066                  "visibile %d",
1067                  client->title,
1068                  screen_desktop, client->wmstate, frame_engine->frame_is_visible(client->frame),
1069                  x, y, w, h, client->border_width);
1070
1071         if (e->xconfigurerequest.value_mask & CWBorderWidth)
1072             if (client->border_width != e->xconfigurerequest.border_width) {
1073                 client->border_width = e->xconfigurerequest.border_width;
1074
1075                 /* if the border width is changing then that is the same
1076                    as requesting a resize, but we don't actually change
1077                    the client's border, so it will change their root
1078                    coordiantes (since they include the border width) and
1079                    we need to a notify then */
1080                 move = TRUE;
1081             }
1082
1083
1084         if (e->xconfigurerequest.value_mask & CWStackMode) {
1085             ObClient *sibling = NULL;
1086             gulong ignore_start;
1087             gboolean ok = TRUE;
1088
1089             /* get the sibling */
1090             if (e->xconfigurerequest.value_mask & CWSibling) {
1091                 ObWindow *win;
1092                 win = window_find(e->xconfigurerequest.above);
1093                 if (win && WINDOW_IS_CLIENT(win) &&
1094                     WINDOW_AS_CLIENT(win) != client)
1095                 {
1096                     sibling = WINDOW_AS_CLIENT(win);
1097                 }
1098                 else
1099                     /* an invalid sibling was specified so don't restack at
1100                        all, it won't make sense no matter what we do */
1101                     ok = FALSE;
1102             }
1103
1104             if (ok) {
1105                 if (!config_focus_under_mouse)
1106                     ignore_start = event_start_ignore_all_enters();
1107                 stacking_restack_request(client, sibling,
1108                                          e->xconfigurerequest.detail);
1109                 if (!config_focus_under_mouse)
1110                     event_end_ignore_all_enters(ignore_start);
1111             }
1112
1113             /* a stacking change moves the window without resizing */
1114             move = TRUE;
1115         }
1116
1117         if ((e->xconfigurerequest.value_mask & CWX) ||
1118             (e->xconfigurerequest.value_mask & CWY) ||
1119             (e->xconfigurerequest.value_mask & CWWidth) ||
1120             (e->xconfigurerequest.value_mask & CWHeight))
1121         {
1122             if (e->xconfigurerequest.value_mask & CWX) {
1123                 /* don't allow clients to move shaded windows (fvwm does this)
1124                  */
1125                 if (!client->shaded)
1126                     x = e->xconfigurerequest.x;
1127                 move = TRUE;
1128             }
1129             if (e->xconfigurerequest.value_mask & CWY) {
1130                 /* don't allow clients to move shaded windows (fvwm does this)
1131                  */
1132                 if (!client->shaded)
1133                     y = e->xconfigurerequest.y;
1134                 move = TRUE;
1135             }
1136
1137             if (e->xconfigurerequest.value_mask & CWWidth) {
1138                 w = e->xconfigurerequest.width;
1139                 resize = TRUE;
1140             }
1141             if (e->xconfigurerequest.value_mask & CWHeight) {
1142                 h = e->xconfigurerequest.height;
1143                 resize = TRUE;
1144             }
1145         }
1146
1147         ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1148                  "move %d resize %d",
1149                  e->xconfigurerequest.value_mask & CWX, x,
1150                  e->xconfigurerequest.value_mask & CWY, y,
1151                  e->xconfigurerequest.value_mask & CWWidth, w,
1152                  e->xconfigurerequest.value_mask & CWHeight, h,
1153                  move, resize);
1154
1155         /* check for broken apps moving to their root position
1156
1157            XXX remove this some day...that would be nice. right now all
1158            kde apps do this when they try activate themselves on another
1159            desktop. eg. open amarok window on desktop 1, switch to desktop
1160            2, click amarok tray icon. it will move by its decoration size.
1161         */
1162         Strut size;
1163         frame_engine->frame_get_size(client->frame, &size);
1164         Rect area;
1165         frame_engine->frame_get_window_area(client->frame, &area);
1166         if (x != client->area.x &&
1167             x == (area.x + size.left -
1168                   (gint)client->border_width) &&
1169             y != client->area.y &&
1170             y == (area.y + size.top -
1171                   (gint)client->border_width) &&
1172             w == client->area.width &&
1173             h == client->area.height)
1174         {
1175             ob_debug_type(OB_DEBUG_APP_BUGS,
1176                           "Application %s is trying to move via "
1177                           "ConfigureRequest to it's root window position "
1178                           "but it is not using StaticGravity",
1179                           client->title);
1180             /* don't move it */
1181             x = client->area.x;
1182             y = client->area.y;
1183
1184             /* they still requested a move, so don't change whether a
1185                notify is sent or not */
1186         }
1187
1188         {
1189             gint lw,lh;
1190
1191             client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
1192
1193             /* if x was not given, then use gravity to figure out the new
1194                x.  the reference point should not be moved */
1195             if ((e->xconfigurerequest.value_mask & CWWidth &&
1196                  !(e->xconfigurerequest.value_mask & CWX)))
1197                 client_gravity_resize_w(client, &x, client->area.width, w);
1198             /* if y was not given, then use gravity to figure out the new
1199                y.  the reference point should not be moved */
1200             if ((e->xconfigurerequest.value_mask & CWHeight &&
1201                  !(e->xconfigurerequest.value_mask & CWY)))
1202                 client_gravity_resize_h(client, &y, client->area.height,h);
1203
1204             client_find_onscreen(client, &x, &y, w, h, FALSE);
1205
1206             ob_debug("Granting ConfigureRequest x %d y %d w %d h %d",
1207                      x, y, w, h);
1208             client_configure(client, x, y, w, h, FALSE, TRUE, TRUE);
1209         }
1210         break;
1211     }
1212     case UnmapNotify:
1213         ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1214                  "ignores left %d",
1215                  client->w_client, e->xunmap.event, e->xunmap.from_configure,
1216                  client->ignore_unmaps);
1217         if (client->ignore_unmaps) {
1218             client->ignore_unmaps--;
1219             break;
1220         }
1221         client_unmanage(client);
1222         break;
1223     case DestroyNotify:
1224         ob_debug("DestroyNotify for window 0x%x", client->w_client);
1225         client_unmanage(client);
1226         break;
1227     case ReparentNotify:
1228         /* this is when the client is first taken captive in the frame */
1229         if (e->xreparent.parent == client->w_frame) break;
1230
1231         /*
1232           This event is quite rare and is usually handled in unmapHandler.
1233           However, if the window is unmapped when the reparent event occurs,
1234           the window manager never sees it because an unmap event is not sent
1235           to an already unmapped window.
1236         */
1237
1238         /* we don't want the reparent event, put it back on the stack for the
1239            X server to deal with after we unmanage the window */
1240         XPutBackEvent(obt_display, e);
1241
1242         ob_debug("ReparentNotify for window 0x%x", client->w_client);
1243         client_unmanage(client);
1244         break;
1245     case MapRequest:
1246         ob_debug("MapRequest for 0x%lx", client->w_client);
1247         if (!client->iconic) break; /* this normally doesn't happen, but if it
1248                                        does, we don't want it!
1249                                        it can happen now when the window is on
1250                                        another desktop, but we still don't
1251                                        want it! */
1252         client_activate(client, FALSE, TRUE, TRUE, TRUE);
1253         break;
1254     case ClientMessage:
1255         /* validate cuz we query stuff off the client here */
1256         if (!client_validate(client)) break;
1257
1258         if (e->xclient.format != 32) return;
1259
1260         msgtype = e->xclient.message_type;
1261         if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) {
1262             /* compress changes into a single change */
1263             while (XCheckTypedWindowEvent(obt_display, client->w_client,
1264                                           e->type, &ce)) {
1265                 /* XXX: it would be nice to compress ALL messages of a
1266                    type, not just messages in a row without other
1267                    message types between. */
1268                 if (ce.xclient.message_type != msgtype) {
1269                     XPutBackEvent(obt_display, &ce);
1270                     break;
1271                 }
1272                 e->xclient = ce.xclient;
1273             }
1274             client_set_wm_state(client, e->xclient.data.l[0]);
1275         } else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) {
1276             /* compress changes into a single change */
1277             while (XCheckTypedWindowEvent(obt_display, client->w_client,
1278                                           e->type, &ce)) {
1279                 /* XXX: it would be nice to compress ALL messages of a
1280                    type, not just messages in a row without other
1281                    message types between. */
1282                 if (ce.xclient.message_type != msgtype) {
1283                     XPutBackEvent(obt_display, &ce);
1284                     break;
1285                 }
1286                 e->xclient = ce.xclient;
1287             }
1288             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1289                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1290                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1291                                    FALSE, FALSE);
1292         } else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) {
1293             gulong ignore_start;
1294
1295             /* can't compress these */
1296             ob_debug("net_wm_state %s %ld %ld for 0x%lx",
1297                      (e->xclient.data.l[0] == 0 ? "Remove" :
1298                       e->xclient.data.l[0] == 1 ? "Add" :
1299                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1300                      e->xclient.data.l[1], e->xclient.data.l[2],
1301                      client->w_client);
1302
1303             /* ignore enter events caused by these like ob actions do */
1304             if (!config_focus_under_mouse)
1305                 ignore_start = event_start_ignore_all_enters();
1306             client_set_state(client, e->xclient.data.l[0],
1307                              e->xclient.data.l[1], e->xclient.data.l[2]);
1308             if (!config_focus_under_mouse)
1309                 event_end_ignore_all_enters(ignore_start);
1310         } else if (msgtype == OBT_PROP_ATOM(NET_CLOSE_WINDOW)) {
1311             ob_debug("net_close_window for 0x%lx", client->w_client);
1312             client_close(client);
1313         } else if (msgtype == OBT_PROP_ATOM(NET_ACTIVE_WINDOW)) {
1314             ob_debug("net_active_window for 0x%lx source=%s",
1315                      client->w_client,
1316                      (e->xclient.data.l[0] == 0 ? "unknown" :
1317                       (e->xclient.data.l[0] == 1 ? "application" :
1318                        (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1319             /* XXX make use of data.l[2] !? */
1320             if (e->xclient.data.l[0] == 1 || e->xclient.data.l[0] == 2) {
1321                 /* don't use the user's timestamp for client_focus, cuz if it's
1322                    an old broken timestamp (happens all the time) then focus
1323                    won't move even though we're trying to move it
1324                   event_curtime = e->xclient.data.l[1];*/
1325                 if (e->xclient.data.l[1] == 0)
1326                     ob_debug_type(OB_DEBUG_APP_BUGS,
1327                                   "_NET_ACTIVE_WINDOW message for window %s is"
1328                                   " missing a timestamp", client->title);
1329             } else
1330                 ob_debug_type(OB_DEBUG_APP_BUGS,
1331                               "_NET_ACTIVE_WINDOW message for window %s is "
1332                               "missing source indication");
1333             client_activate(client, FALSE, TRUE, TRUE,
1334                             (e->xclient.data.l[0] == 0 ||
1335                              e->xclient.data.l[0] == 2));
1336         } else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) {
1337             ob_debug("net_wm_moveresize for 0x%lx direction %d",
1338                      client->w_client, e->xclient.data.l[2]);
1339             if ((Atom)e->xclient.data.l[2] ==
1340                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1341                 (Atom)e->xclient.data.l[2] ==
1342                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
1343                 (Atom)e->xclient.data.l[2] ==
1344                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT) ||
1345                 (Atom)e->xclient.data.l[2] ==
1346                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
1347                 (Atom)e->xclient.data.l[2] ==
1348                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
1349                 (Atom)e->xclient.data.l[2] ==
1350                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) ||
1351                 (Atom)e->xclient.data.l[2] ==
1352                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM) ||
1353                 (Atom)e->xclient.data.l[2] ==
1354                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) ||
1355                 (Atom)e->xclient.data.l[2] ==
1356                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
1357                 (Atom)e->xclient.data.l[2] ==
1358                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
1359                 (Atom)e->xclient.data.l[2] ==
1360                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD) ||
1361                 (Atom)e->xclient.data.l[2] ==
1362                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
1363             {
1364                 moveresize_start(client, e->xclient.data.l[0],
1365                                  e->xclient.data.l[1], e->xclient.data.l[3],
1366                                  e->xclient.data.l[2]);
1367             }
1368             else if ((Atom)e->xclient.data.l[2] ==
1369                      OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
1370                 moveresize_end(TRUE);
1371         } else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
1372             gint ograv, x, y, w, h;
1373
1374             ograv = client->gravity;
1375
1376             if (e->xclient.data.l[0] & 0xff)
1377                 client->gravity = e->xclient.data.l[0] & 0xff;
1378
1379             if (e->xclient.data.l[0] & 1 << 8)
1380                 x = e->xclient.data.l[1];
1381             else
1382                 x = client->area.x;
1383             if (e->xclient.data.l[0] & 1 << 9)
1384                 y = e->xclient.data.l[2];
1385             else
1386                 y = client->area.y;
1387
1388             if (e->xclient.data.l[0] & 1 << 10) {
1389                 w = e->xclient.data.l[3];
1390
1391                 /* if x was not given, then use gravity to figure out the new
1392                    x.  the reference point should not be moved */
1393                 if (!(e->xclient.data.l[0] & 1 << 8))
1394                     client_gravity_resize_w(client, &x, client->area.width, w);
1395             }
1396             else
1397                 w = client->area.width;
1398
1399             if (e->xclient.data.l[0] & 1 << 11) {
1400                 h = e->xclient.data.l[4];
1401
1402                 /* if y was not given, then use gravity to figure out the new
1403                    y.  the reference point should not be moved */
1404                 if (!(e->xclient.data.l[0] & 1 << 9))
1405                     client_gravity_resize_h(client, &y, client->area.height,h);
1406             }
1407             else
1408                 h = client->area.height;
1409
1410             ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)",
1411                      e->xclient.data.l[0] & 1 << 8, x,
1412                      e->xclient.data.l[0] & 1 << 9, y,
1413                      client->gravity);
1414
1415             client_find_onscreen(client, &x, &y, w, h, FALSE);
1416
1417             client_configure(client, x, y, w, h, FALSE, TRUE, FALSE);
1418
1419             client->gravity = ograv;
1420         } else if (msgtype == OBT_PROP_ATOM(NET_RESTACK_WINDOW)) {
1421             if (e->xclient.data.l[0] != 2) {
1422                 ob_debug_type(OB_DEBUG_APP_BUGS,
1423                               "_NET_RESTACK_WINDOW sent for window %s with "
1424                               "invalid source indication %ld",
1425                               client->title, e->xclient.data.l[0]);
1426             } else {
1427                 ObClient *sibling = NULL;
1428                 if (e->xclient.data.l[1]) {
1429                     ObWindow *win = window_find(e->xclient.data.l[1]);
1430                     if (WINDOW_IS_CLIENT(win) &&
1431                         WINDOW_AS_CLIENT(win) != client)
1432                     {
1433                         sibling = WINDOW_AS_CLIENT(win);
1434                     }
1435                     if (sibling == NULL)
1436                         ob_debug_type(OB_DEBUG_APP_BUGS,
1437                                       "_NET_RESTACK_WINDOW sent for window %s "
1438                                       "with invalid sibling 0x%x",
1439                                  client->title, e->xclient.data.l[1]);
1440                 }
1441                 if (e->xclient.data.l[2] == Below ||
1442                     e->xclient.data.l[2] == BottomIf ||
1443                     e->xclient.data.l[2] == Above ||
1444                     e->xclient.data.l[2] == TopIf ||
1445                     e->xclient.data.l[2] == Opposite)
1446                 {
1447                     gulong ignore_start;
1448
1449                     if (!config_focus_under_mouse)
1450                         ignore_start = event_start_ignore_all_enters();
1451                     /* just raise, don't activate */
1452                     stacking_restack_request(client, sibling,
1453                                              e->xclient.data.l[2]);
1454                     if (!config_focus_under_mouse)
1455                         event_end_ignore_all_enters(ignore_start);
1456
1457                     /* send a synthetic ConfigureNotify, cuz this is supposed
1458                        to be like a ConfigureRequest. */
1459                     client_reconfigure(client, TRUE);
1460                 } else
1461                     ob_debug_type(OB_DEBUG_APP_BUGS,
1462                                   "_NET_RESTACK_WINDOW sent for window %s "
1463                                   "with invalid detail %d",
1464                                   client->title, e->xclient.data.l[2]);
1465             }
1466         }
1467         break;
1468     case PropertyNotify:
1469         /* validate cuz we query stuff off the client here */
1470         if (!client_validate(client)) break;
1471
1472         /* compress changes to a single property into a single change */
1473         while (XCheckTypedWindowEvent(obt_display, client->w_client,
1474                                       e->type, &ce)) {
1475             Atom a, b;
1476
1477             /* XXX: it would be nice to compress ALL changes to a property,
1478                not just changes in a row without other props between. */
1479
1480             a = ce.xproperty.atom;
1481             b = e->xproperty.atom;
1482
1483             if (a == b)
1484                 continue;
1485             if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
1486                  a == OBT_PROP_ATOM(WM_NAME) ||
1487                  a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1488                  a == OBT_PROP_ATOM(WM_ICON_NAME))
1489                 &&
1490                 (b == OBT_PROP_ATOM(NET_WM_NAME) ||
1491                  b == OBT_PROP_ATOM(WM_NAME) ||
1492                  b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1493                  b == OBT_PROP_ATOM(WM_ICON_NAME))) {
1494                 continue;
1495             }
1496             if (a == OBT_PROP_ATOM(NET_WM_ICON) &&
1497                 b == OBT_PROP_ATOM(NET_WM_ICON))
1498                 continue;
1499
1500             XPutBackEvent(obt_display, &ce);
1501             break;
1502         }
1503
1504         msgtype = e->xproperty.atom;
1505         if (msgtype == XA_WM_NORMAL_HINTS) {
1506             ob_debug("Update NORMAL hints");
1507             client_update_normal_hints(client);
1508             /* normal hints can make a window non-resizable */
1509             client_setup_decor_and_functions(client, FALSE);
1510
1511             /* make sure the client's sizes are within its bounds, but only
1512                reconfigure the window if it needs to. emacs will update its
1513                normal hints every time it receives a conigurenotify */
1514             client_reconfigure(client, FALSE);
1515         } else if (msgtype == XA_WM_HINTS) {
1516             client_update_wmhints(client);
1517         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1518             client_update_transient_for(client);
1519             client_get_type_and_transientness(client);
1520             /* type may have changed, so update the layer */
1521             client_calc_layer(client);
1522             client_setup_decor_and_functions(client, TRUE);
1523         } else if (msgtype == OBT_PROP_ATOM(NET_WM_NAME) ||
1524                    msgtype == OBT_PROP_ATOM(WM_NAME) ||
1525                    msgtype == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1526                    msgtype == OBT_PROP_ATOM(WM_ICON_NAME)) {
1527             client_update_title(client);
1528         } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
1529             client_update_protocols(client);
1530             client_setup_decor_and_functions(client, TRUE);
1531         }
1532         else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT)) {
1533             client_update_strut(client);
1534         }
1535         else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL)) {
1536             client_update_strut(client);
1537         }
1538         else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON)) {
1539             client_update_icons(client);
1540         }
1541         else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY)) {
1542             client_update_icon_geometry(client);
1543         }
1544         else if (msgtype == OBT_PROP_ATOM(NET_WM_USER_TIME)) {
1545             guint32 t;
1546             if (client == focus_client &&
1547                 OBT_PROP_GET32(client->w_client, NET_WM_USER_TIME, CARDINAL, &t)
1548                 && t && !event_time_after(t, e->xproperty.time) &&
1549                 (!event_last_user_time ||
1550                  event_time_after(t, event_last_user_time)))
1551             {
1552                 event_last_user_time = t;
1553             }
1554         }
1555 #ifdef SYNC
1556         else if (msgtype == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER)) {
1557             client_update_sync_request_counter(client);
1558         }
1559 #endif
1560         break;
1561     case ColormapNotify:
1562         client_update_colormap(client, e->xcolormap.colormap);
1563         break;
1564     default:
1565         ;
1566 #ifdef SHAPE
1567         if (obt_display_extension_shape &&
1568             e->type == obt_display_extension_shape_basep)
1569         {
1570             client->shaped = ((XShapeEvent*)e)->shaped;
1571             frame_engine->frame_adjust_shape(client->frame);
1572         }
1573 #endif
1574     }
1575 }
1576
1577 static void event_handle_dock(ObDock *s, XEvent *e)
1578 {
1579     switch (e->type) {
1580     case ButtonPress:
1581         if (e->xbutton.button == 1)
1582             stacking_raise(DOCK_AS_WINDOW(s));
1583         else if (e->xbutton.button == 2)
1584             stacking_lower(DOCK_AS_WINDOW(s));
1585         break;
1586     case EnterNotify:
1587         dock_hide(FALSE);
1588         break;
1589     case LeaveNotify:
1590         /* don't hide when moving into a dock app */
1591         if (e->xcrossing.detail != NotifyInferior)
1592             dock_hide(TRUE);
1593         break;
1594     }
1595 }
1596
1597 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1598 {
1599     switch (e->type) {
1600     case MotionNotify:
1601         dock_app_drag(app, &e->xmotion);
1602         break;
1603     case UnmapNotify:
1604         if (app->ignore_unmaps) {
1605             app->ignore_unmaps--;
1606             break;
1607         }
1608         dock_unmanage(app, TRUE);
1609         break;
1610     case DestroyNotify:
1611         dock_unmanage(app, FALSE);
1612         break;
1613     case ReparentNotify:
1614         dock_unmanage(app, FALSE);
1615         break;
1616     case ConfigureNotify:
1617         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1618         break;
1619     }
1620 }
1621
1622 static ObMenuFrame* find_active_menu(void)
1623 {
1624     GList *it;
1625     ObMenuFrame *ret = NULL;
1626
1627     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1628         ret = it->data;
1629         if (ret->selected)
1630             break;
1631         ret = NULL;
1632     }
1633     return ret;
1634 }
1635
1636 static ObMenuFrame* find_active_or_last_menu(void)
1637 {
1638     ObMenuFrame *ret = NULL;
1639
1640     ret = find_active_menu();
1641     if (!ret && menu_frame_visible)
1642         ret = menu_frame_visible->data;
1643     return ret;
1644 }
1645
1646 static gboolean event_handle_prompt(ObPrompt *p, XEvent *e)
1647 {
1648     switch (e->type) {
1649     case ButtonPress:
1650     case ButtonRelease:
1651     case MotionNotify:
1652         return prompt_mouse_event(p, e);
1653         break;
1654     case KeyPress:
1655         return prompt_key_event(p, e);
1656         break;
1657     }
1658     return FALSE;
1659 }
1660
1661 static gboolean event_handle_menu_input(XEvent *ev)
1662 {
1663     gboolean ret = FALSE;
1664
1665     if (ev->type == ButtonRelease || ev->type == ButtonPress) {
1666         ObMenuEntryFrame *e;
1667
1668         if (menu_hide_delay_reached() &&
1669             (ev->xbutton.button < 4 || ev->xbutton.button > 5))
1670         {
1671             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1672                                             ev->xbutton.y_root)))
1673             {
1674                 if (ev->type == ButtonPress && e->frame->child)
1675                     menu_frame_select(e->frame->child, NULL, TRUE);
1676                 menu_frame_select(e->frame, e, TRUE);
1677                 if (ev->type == ButtonRelease)
1678                     menu_entry_frame_execute(e, ev->xbutton.state);
1679             }
1680             else if (ev->type == ButtonRelease)
1681                 menu_frame_hide_all();
1682         }
1683         ret = TRUE;
1684     }
1685     else if (ev->type == MotionNotify) {
1686         ObMenuFrame *f;
1687         ObMenuEntryFrame *e;
1688
1689         if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1690                                         ev->xmotion.y_root)))
1691             if (!(f = find_active_menu()) ||
1692                 f == e->frame ||
1693                 f->parent == e->frame ||
1694                 f->child == e->frame)
1695                 menu_frame_select(e->frame, e, FALSE);
1696     }
1697     else if (ev->type == KeyPress || ev->type == KeyRelease) {
1698         guint keycode, state;
1699         gunichar unikey;
1700         ObMenuFrame *frame;
1701
1702         keycode = ev->xkey.keycode;
1703         state = ev->xkey.state;
1704         unikey = obt_keyboard_keycode_to_unichar(keycode);
1705
1706         frame = find_active_or_last_menu();
1707         if (frame == NULL)
1708             g_assert_not_reached(); /* there is no active menu */
1709
1710         /* Allow control while going thru the menu */
1711         else if (ev->type == KeyPress && (state & ~ControlMask) == 0) {
1712             frame->got_press = TRUE;
1713
1714             if (keycode == ob_keycode(OB_KEY_ESCAPE)) {
1715                 menu_frame_hide_all();
1716                 ret = TRUE;
1717             }
1718
1719             else if (keycode == ob_keycode(OB_KEY_LEFT)) {
1720                 /* Left goes to the parent menu */
1721                 menu_frame_select(frame, NULL, TRUE);
1722                 ret = TRUE;
1723             }
1724
1725             else if (keycode == ob_keycode(OB_KEY_RIGHT)) {
1726                 /* Right goes to the selected submenu */
1727                 if (frame->child) menu_frame_select_next(frame->child);
1728                 ret = TRUE;
1729             }
1730
1731             else if (keycode == ob_keycode(OB_KEY_UP)) {
1732                 menu_frame_select_previous(frame);
1733                 ret = TRUE;
1734             }
1735
1736             else if (keycode == ob_keycode(OB_KEY_DOWN)) {
1737                 menu_frame_select_next(frame);
1738                 ret = TRUE;
1739             }
1740         }
1741
1742         /* Use KeyRelease events for running things so that the key release
1743            doesn't get sent to the focused application.
1744
1745            Allow ControlMask only, and don't bother if the menu is empty */
1746         else if (ev->type == KeyRelease && (state & ~ControlMask) == 0 &&
1747                  frame->entries && frame->got_press)
1748         {
1749             if (keycode == ob_keycode(OB_KEY_RETURN)) {
1750                 /* Enter runs the active item or goes into the submenu.
1751                    Control-Enter runs it without closing the menu. */
1752                 if (frame->child)
1753                     menu_frame_select_next(frame->child);
1754                 else if (frame->selected)
1755                     menu_entry_frame_execute(frame->selected, state);
1756
1757                 ret = TRUE;
1758             }
1759
1760             /* keyboard accelerator shortcuts. (if it was a valid key) */
1761             else if (unikey != 0) {
1762                 GList *start;
1763                 GList *it;
1764                 ObMenuEntryFrame *found = NULL;
1765                 guint num_found = 0;
1766
1767                 /* start after the selected one */
1768                 start = frame->entries;
1769                 if (frame->selected) {
1770                     for (it = start; frame->selected != it->data;
1771                          it = g_list_next(it))
1772                         g_assert(it != NULL); /* nothing was selected? */
1773                     /* next with wraparound */
1774                     start = g_list_next(it);
1775                     if (start == NULL) start = frame->entries;
1776                 }
1777
1778                 it = start;
1779                 do {
1780                     ObMenuEntryFrame *e = it->data;
1781                     gunichar entrykey = 0;
1782
1783                     if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
1784                         entrykey = e->entry->data.normal.shortcut;
1785                     else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1786                         entrykey = e->entry->data.submenu.submenu->shortcut;
1787
1788                     if (unikey == entrykey) {
1789                         if (found == NULL) found = e;
1790                         ++num_found;
1791                     }
1792
1793                     /* next with wraparound */
1794                     it = g_list_next(it);
1795                     if (it == NULL) it = frame->entries;
1796                 } while (it != start);
1797
1798                 if (found) {
1799                     if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1800                         num_found == 1)
1801                     {
1802                         menu_frame_select(frame, found, TRUE);
1803                         usleep(50000); /* highlight the item for a short bit so
1804                                           the user can see what happened */
1805                         menu_entry_frame_execute(found, state);
1806                     } else {
1807                         menu_frame_select(frame, found, TRUE);
1808                         if (num_found == 1)
1809                             menu_frame_select_next(frame->child);
1810                     }
1811
1812                     ret = TRUE;
1813                 }
1814             }
1815         }
1816     }
1817
1818     return ret;
1819 }
1820
1821 static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
1822 {
1823     ObMenuFrame *f;
1824     ObMenuEntryFrame *e;
1825
1826     switch (ev->type) {
1827     case EnterNotify:
1828         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1829             if (e->ignore_enters)
1830                 --e->ignore_enters;
1831             else if (!(f = find_active_menu()) ||
1832                      f == e->frame ||
1833                      f->parent == e->frame ||
1834                      f->child == e->frame)
1835                 menu_frame_select(e->frame, e, FALSE);
1836         }
1837         break;
1838     case LeaveNotify:
1839         /*ignore leaves when we're already in the window */
1840         if (ev->xcrossing.detail == NotifyInferior)
1841             break;
1842
1843         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1844             (f = find_active_menu()) && f->selected == e &&
1845             e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1846         {
1847             menu_frame_select(e->frame, NULL, FALSE);
1848         }
1849         break;
1850     }
1851 }
1852
1853 static void event_handle_user_input(ObClient *client, XEvent *e)
1854 {
1855     g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1856              e->type == MotionNotify || e->type == KeyPress ||
1857              e->type == KeyRelease);
1858
1859     if (menu_frame_visible) {
1860         if (event_handle_menu_input(e))
1861             /* don't use the event if the menu used it, but if the menu
1862                didn't use it and it's a keypress that is bound, it will
1863                close the menu and be used */
1864             return;
1865     }
1866
1867     /* if the keyboard interactive action uses the event then dont
1868        use it for bindings. likewise is moveresize uses the event. */
1869     if (!actions_interactive_input_event(e) && !moveresize_event(e)) {
1870         if (frame_engine->moveresize_in_progress)
1871             /* make further actions work on the client being
1872                moved/resized */
1873             client = moveresize_client;
1874
1875         if (e->type == ButtonPress ||
1876             e->type == ButtonRelease ||
1877             e->type == MotionNotify)
1878         {
1879             /* the frame may not be "visible" but they can still click on it
1880                in the case where it is animating before disappearing */
1881             mouse_event(client, e);
1882         } else
1883             keyboard_event((frame_engine->focus_cycle_target ? frame_engine->focus_cycle_target :
1884                             (client ? client : focus_client)), e);
1885     }
1886 }
1887
1888 static void focus_delay_dest(gpointer data)
1889 {
1890     g_free(data);
1891 }
1892
1893 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1894 {
1895     const ObFocusDelayData *f1 = d1;
1896     return f1->client == d2;
1897 }
1898
1899 static gboolean focus_delay_func(gpointer data)
1900 {
1901     ObFocusDelayData *d = data;
1902     Time old = event_curtime;
1903
1904     /* don't move focus and kill the menu or the move/resize */
1905     if (menu_frame_visible || frame_engine->moveresize_in_progress) return FALSE;
1906
1907     event_curtime = d->time;
1908     event_curserial = d->serial;
1909     if (client_focus(d->client) && config_focus_raise)
1910         stacking_raise(CLIENT_AS_WINDOW(d->client));
1911     event_curtime = old;
1912     return FALSE; /* no repeat */
1913 }
1914
1915 static void focus_delay_client_dest(ObClient *client, gpointer data)
1916 {
1917     obt_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1918                                       client, FALSE);
1919 }
1920
1921 void event_halt_focus_delay(void)
1922 {
1923     /* ignore all enter events up till the event which caused this to occur */
1924     if (event_curserial) event_ignore_enter_range(1, event_curserial);
1925     obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1926 }
1927
1928 gulong event_start_ignore_all_enters(void)
1929 {
1930     return NextRequest(obt_display);
1931 }
1932
1933 static void event_ignore_enter_range(gulong start, gulong end)
1934 {
1935     ObSerialRange *r;
1936
1937     g_assert(start != 0);
1938     g_assert(end != 0);
1939
1940     r = g_new(ObSerialRange, 1);
1941     r->start = start;
1942     r->end = end;
1943     ignore_serials = g_slist_prepend(ignore_serials, r);
1944
1945     ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu",
1946                   r->start, r->end);
1947
1948     /* increment the serial so we don't ignore events we weren't meant to */
1949     OBT_PROP_ERASE(screen_support_win, MOTIF_WM_HINTS);
1950 }
1951
1952 void event_end_ignore_all_enters(gulong start)
1953 {
1954     /* Use (NextRequest-1) so that we ignore up to the current serial only.
1955        Inside event_ignore_enter_range, we increment the serial by one, but if
1956        we ignore that serial too, then any enter events generated by mouse
1957        movement will be ignored until we create some further network traffic.
1958        Instead ignore up to NextRequest-1, then when we increment the serial,
1959        we will be *past* the range of ignored serials */
1960     event_ignore_enter_range(start, NextRequest(obt_display)-1);
1961 }
1962
1963 static gboolean is_enter_focus_event_ignored(gulong serial)
1964 {
1965     GSList *it, *next;
1966
1967     for (it = ignore_serials; it; it = next) {
1968         ObSerialRange *r = it->data;
1969
1970         next = g_slist_next(it);
1971
1972         if ((glong)(serial - r->end) > 0) {
1973             /* past the end */
1974             ignore_serials = g_slist_delete_link(ignore_serials, it);
1975             g_free(r);
1976         }
1977         else if ((glong)(serial - r->start) >= 0)
1978             return TRUE;
1979     }
1980     return FALSE;
1981 }
1982
1983 void event_cancel_all_key_grabs(void)
1984 {
1985     if (actions_interactive_act_running()) {
1986         actions_interactive_cancel_act();
1987         ob_debug("KILLED interactive action");
1988     }
1989     else if (menu_frame_visible) {
1990         menu_frame_hide_all();
1991         ob_debug("KILLED open menus");
1992     }
1993     else if (frame_engine->moveresize_in_progress) {
1994         moveresize_end(TRUE);
1995         ob_debug("KILLED interactive moveresize");
1996     }
1997     else if (grab_on_keyboard()) {
1998         ungrab_keyboard();
1999         ob_debug("KILLED active grab on keyboard");
2000     }
2001     else
2002         ungrab_passive_key();
2003
2004     XSync(obt_display, FALSE);
2005 }
2006
2007 gboolean event_time_after(Time t1, Time t2)
2008 {
2009     g_assert(t1 != CurrentTime);
2010     g_assert(t2 != CurrentTime);
2011
2012     /*
2013       Timestamp values wrap around (after about 49.7 days). The server, given
2014       its current time is represented by timestamp T, always interprets
2015       timestamps from clients by treating half of the timestamp space as being
2016       later in time than T.
2017       - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
2018     */
2019
2020     /* TIME_HALF is half of the number space of a Time type variable */
2021 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
2022
2023     if (t2 >= TIME_HALF)
2024         /* t2 is in the second half so t1 might wrap around and be smaller than
2025            t2 */
2026         return t1 >= t2 || t1 < (t2 + TIME_HALF);
2027     else
2028         /* t2 is in the first half so t1 has to come after it */
2029         return t1 >= t2 && t1 < (t2 + TIME_HALF);
2030 }
2031
2032 Time event_get_server_time(void)
2033 {
2034     /* Generate a timestamp */
2035     XEvent event;
2036
2037     XChangeProperty(obt_display, screen_support_win,
2038                     OBT_PROP_ATOM(WM_CLASS), OBT_PROP_ATOM(STRING),
2039                     8, PropModeAppend, NULL, 0);
2040     XWindowEvent(obt_display, screen_support_win, PropertyChangeMask, &event);
2041     return event.xproperty.time;
2042 }