dummy child of all mikabox branches at 3.4.7
[mikachu/openbox.git] /
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 "frame.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_adjust_focus(client->frame, FALSE);
531             /* focus_set_client(NULL) has already been called */
532         }
533         else if (e->xfocus.detail == NotifyPointerRoot ||
534                  e->xfocus.detail == NotifyDetailNone ||
535                  e->xfocus.detail == NotifyInferior ||
536                  e->xfocus.detail == NotifyNonlinear)
537         {
538             XEvent ce;
539
540             ob_debug_type(OB_DEBUG_FOCUS,
541                           "Focus went to root or pointer root/none");
542
543             if (e->xfocus.detail == NotifyInferior ||
544                 e->xfocus.detail == NotifyNonlinear)
545             {
546                 focus_left_screen = FALSE;
547             }
548
549             /* If another FocusIn is in the queue then don't fallback yet. This
550                fixes the fun case of:
551                window map -> send focusin
552                window unmap -> get focusout
553                window map -> send focusin
554                get first focus out -> fall back to something (new window
555                  hasn't received focus yet, so something else) -> send focusin
556                which means the "something else" is the last thing to get a
557                focusin sent to it, so the new window doesn't end up with focus.
558
559                But if the other focus in is something like PointerRoot then we
560                still want to fall back.
561             */
562             if (XCheckIfEvent(obt_display, &ce, event_look_for_focusin_client,
563                               NULL))
564             {
565                 XPutBackEvent(obt_display, &ce);
566                 ob_debug_type(OB_DEBUG_FOCUS,
567                               "  but another FocusIn is coming");
568             } else {
569                 /* Focus has been reverted.
570
571                    FocusOut events come after UnmapNotify, so we don't need to
572                    worry about focusing an invalid window
573                 */
574
575                 if (!focus_left_screen)
576                     focus_fallback(FALSE, config_focus_under_mouse,
577                                    TRUE, TRUE);
578             }
579         }
580         else if (!client)
581         {
582             ob_debug_type(OB_DEBUG_FOCUS,
583                           "Focus went to a window that is already gone");
584
585             /* If you send focus to a window and then it disappears, you can
586                get the FocusIn for it, after it is unmanaged.
587                Just wait for the next FocusOut/FocusIn pair, but make note that
588                the window that was focused no longer is. */
589             focus_set_client(NULL);
590         }
591         else if (client != focus_client) {
592             focus_left_screen = FALSE;
593             frame_adjust_focus(client->frame, TRUE);
594             focus_set_client(client);
595             client_calc_layer(client);
596             client_bring_helper_windows(client);
597         }
598     } else if (e->type == FocusOut) {
599         XEvent ce;
600
601         /* Look for the followup FocusIn */
602         if (!XCheckIfEvent(obt_display, &ce, event_look_for_focusin, NULL)) {
603             /* There is no FocusIn, this means focus went to a window that
604                is not being managed, or a window on another screen. */
605             Window win, root;
606             gint i;
607             guint u;
608             obt_display_ignore_errors(TRUE);
609             if (XGetInputFocus(obt_display, &win, &i) &&
610                 XGetGeometry(obt_display, win, &root, &i,&i,&u,&u,&u,&u) &&
611                 root != obt_root(ob_screen))
612             {
613                 ob_debug_type(OB_DEBUG_FOCUS,
614                               "Focus went to another screen !");
615                 focus_left_screen = TRUE;
616             }
617             else
618                 ob_debug_type(OB_DEBUG_FOCUS,
619                               "Focus went to a black hole !");
620             obt_display_ignore_errors(FALSE);
621             /* nothing is focused */
622             focus_set_client(NULL);
623         } else {
624             /* Focus moved, so process the FocusIn event */
625             ObEventData ed = { .ignored = FALSE };
626             event_process(&ce, &ed);
627             if (ed.ignored) {
628                 /* The FocusIn was ignored, this means it was on a window
629                    that isn't a client. */
630                 ob_debug_type(OB_DEBUG_FOCUS,
631                               "Focus went to an unmanaged window 0x%x !",
632                               ce.xfocus.window);
633                 focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
634             }
635         }
636
637         if (client && client != focus_client) {
638             frame_adjust_focus(client->frame, FALSE);
639             /* focus_set_client(NULL) has already been called in this
640                section or by focus_fallback */
641         }
642     }
643     else if (client)
644         event_handle_client(client, e);
645     else if (dockapp)
646         event_handle_dockapp(dockapp, e);
647     else if (dock)
648         event_handle_dock(dock, e);
649     else if (menu)
650         event_handle_menu(menu, e);
651     else if (window == obt_root(ob_screen))
652         event_handle_root(e);
653     else if (e->type == MapRequest)
654         window_manage(window);
655     else if (e->type == MappingNotify) {
656         /* keyboard layout changes for modifier mapping changes. reload the
657            modifier map, and rebind all the key bindings as appropriate */
658         ob_debug("Kepboard map changed. Reloading keyboard bindings.");
659         obt_keyboard_reload();
660         keyboard_rebind();
661     }
662     else if (e->type == ClientMessage) {
663         /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
664            windows that are not managed yet. */
665         if (e->xclient.message_type ==
666             OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS))
667         {
668             /* Pretend to manage the client, getting information used to
669                determine its decorations */
670             ObClient *c = client_fake_manage(e->xclient.window);
671             gulong vals[4];
672
673             /* set the frame extents on the window */
674             vals[0] = c->frame->size.left;
675             vals[1] = c->frame->size.right;
676             vals[2] = c->frame->size.top;
677             vals[3] = c->frame->size.bottom;
678             OBT_PROP_SETA32(e->xclient.window, NET_FRAME_EXTENTS,
679                             CARDINAL, vals, 4);
680
681             /* Free the pretend client */
682             client_fake_unmanage(c);
683         }
684     }
685     else if (e->type == ConfigureRequest) {
686         /* unhandled configure requests must be used to configure the
687            window directly */
688         XWindowChanges xwc;
689
690         xwc.x = e->xconfigurerequest.x;
691         xwc.y = e->xconfigurerequest.y;
692         xwc.width = e->xconfigurerequest.width;
693         xwc.height = e->xconfigurerequest.height;
694         xwc.border_width = e->xconfigurerequest.border_width;
695         xwc.sibling = e->xconfigurerequest.above;
696         xwc.stack_mode = e->xconfigurerequest.detail;
697
698         /* we are not to be held responsible if someone sends us an
699            invalid request! */
700         obt_display_ignore_errors(TRUE);
701         XConfigureWindow(obt_display, window,
702                          e->xconfigurerequest.value_mask, &xwc);
703         obt_display_ignore_errors(FALSE);
704     }
705 #ifdef SYNC
706     else if (obt_display_extension_sync &&
707              e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
708     {
709         XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
710         if (se->alarm == moveresize_alarm && moveresize_in_progress)
711             moveresize_event(e);
712     }
713 #endif
714
715     if (prompt && event_handle_prompt(prompt, e))
716         ;
717     else if (e->type == ButtonPress || e->type == ButtonRelease) {
718         /* If the button press was on some non-root window, or was physically
719            on the root window, then process it */
720         if (window != obt_root(ob_screen) ||
721             e->xbutton.subwindow == None)
722         {
723             event_handle_user_input(client, e);
724         }
725         /* Otherwise only process it if it was physically on an openbox
726            internal window */
727         else {
728             ObWindow *w;
729
730             if ((w = window_find(e->xbutton.subwindow)) &&
731                 WINDOW_IS_INTERNAL(w))
732             {
733                 event_handle_user_input(client, e);
734             }
735         }
736     }
737     else if (e->type == KeyPress || e->type == KeyRelease ||
738              e->type == MotionNotify)
739         event_handle_user_input(client, e);
740
741     /* if something happens and it's not from an XEvent, then we don't know
742        the time */
743     event_curtime = CurrentTime;
744     event_curserial = 0;
745 }
746
747 static void event_handle_root(XEvent *e)
748 {
749     Atom msgtype;
750
751     switch(e->type) {
752     case SelectionClear:
753         ob_debug("Another WM has requested to replace us. Exiting.");
754         ob_exit_replace();
755         break;
756
757     case ClientMessage:
758         if (e->xclient.format != 32) break;
759
760         msgtype = e->xclient.message_type;
761         if (msgtype == OBT_PROP_ATOM(NET_CURRENT_DESKTOP)) {
762             guint d = e->xclient.data.l[0];
763             if (d < screen_num_desktops) {
764                 event_curtime = e->xclient.data.l[1];
765                 if (event_curtime == 0)
766                     ob_debug_type(OB_DEBUG_APP_BUGS,
767                                   "_NET_CURRENT_DESKTOP message is missing "
768                                   "a timestamp");
769                 screen_set_desktop(d, TRUE);
770             }
771         } else if (msgtype == OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS)) {
772             guint d = e->xclient.data.l[0];
773             if (d > 0 && d <= 1000)
774                 screen_set_num_desktops(d);
775         } else if (msgtype == OBT_PROP_ATOM(NET_SHOWING_DESKTOP)) {
776             screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
777         } else if (msgtype == OBT_PROP_ATOM(OB_CONTROL)) {
778             ob_debug("OB_CONTROL: %d", e->xclient.data.l[0]);
779             if (e->xclient.data.l[0] == 1)
780                 ob_reconfigure();
781             else if (e->xclient.data.l[0] == 2)
782                 ob_restart();
783             else if (e->xclient.data.l[0] == 3)
784                 ob_exit(0);
785         } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
786             if ((Atom)e->xclient.data.l[0] == OBT_PROP_ATOM(NET_WM_PING))
787                 ping_got_pong(e->xclient.data.l[1]);
788         }
789         break;
790     case PropertyNotify:
791         if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_NAMES)) {
792             ob_debug("UPDATE DESKTOP NAMES");
793             screen_update_desktop_names();
794         }
795         else if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_LAYOUT))
796             screen_update_layout();
797         break;
798     case ConfigureNotify:
799 #ifdef XRANDR
800         XRRUpdateConfiguration(e);
801 #endif
802         screen_resize();
803         break;
804     default:
805         ;
806     }
807 }
808
809 void event_enter_client(ObClient *client)
810 {
811     g_assert(config_focus_follow);
812
813     if (is_enter_focus_event_ignored(event_curserial)) {
814         ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu\n"
815                       "on client 0x%x", event_curserial, client->window);
816         return;
817     }
818
819     if (client_enter_focusable(client) && client_can_focus(client)) {
820         if (config_focus_delay) {
821             ObFocusDelayData *data;
822
823             obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
824
825             data = g_new(ObFocusDelayData, 1);
826             data->client = client;
827             data->time = event_curtime;
828             data->serial = event_curserial;
829
830             obt_main_loop_timeout_add(ob_main_loop,
831                                       config_focus_delay * 1000,
832                                       focus_delay_func,
833                                       data, focus_delay_cmp, focus_delay_dest);
834         } else {
835             ObFocusDelayData data;
836             data.client = client;
837             data.time = event_curtime;
838             data.serial = event_curserial;
839             focus_delay_func(&data);
840         }
841     }
842 }
843
844 static gboolean *context_to_button(ObFrame *f, ObFrameContext con, gboolean press)
845 {
846     if (press) {
847         switch (con) {
848         case OB_FRAME_CONTEXT_MAXIMIZE:
849             return &f->max_press;
850         case OB_FRAME_CONTEXT_CLOSE:
851             return &f->close_press;
852         case OB_FRAME_CONTEXT_ICONIFY:
853             return &f->iconify_press;
854         case OB_FRAME_CONTEXT_ALLDESKTOPS:
855             return &f->desk_press;
856         case OB_FRAME_CONTEXT_SHADE:
857             return &f->shade_press;
858         default:
859             return NULL;
860         }
861     } else {
862         switch (con) {
863         case OB_FRAME_CONTEXT_MAXIMIZE:
864             return &f->max_hover;
865         case OB_FRAME_CONTEXT_CLOSE:
866             return &f->close_hover;
867         case OB_FRAME_CONTEXT_ICONIFY:
868             return &f->iconify_hover;
869         case OB_FRAME_CONTEXT_ALLDESKTOPS:
870             return &f->desk_hover;
871         case OB_FRAME_CONTEXT_SHADE:
872             return &f->shade_hover;
873         default:
874             return NULL;
875         }
876     }
877 }
878
879 static void compress_client_message_event(XEvent *e, XEvent *ce, Window window,
880                                           Atom msgtype)
881 {
882     /* compress changes into a single change */
883     while (XCheckTypedWindowEvent(obt_display, window, e->type, ce)) {
884         /* XXX: it would be nice to compress ALL messages of a
885            type, not just messages in a row without other
886            message types between. */
887         if (ce->xclient.message_type != msgtype) {
888             XPutBackEvent(obt_display, ce);
889             break;
890         }
891         e->xclient = ce->xclient;
892     }
893 }
894
895 static void event_handle_client(ObClient *client, XEvent *e)
896 {
897     XEvent ce;
898     Atom msgtype;
899     ObFrameContext con;
900     gboolean *but;
901     static gint px = -1, py = -1;
902     static guint pb = 0;
903     static ObFrameContext pcon = OB_FRAME_CONTEXT_NONE;
904
905     switch (e->type) {
906     case ButtonPress:
907         /* save where the press occured for the first button pressed */
908         if (!pb) {
909             pb = e->xbutton.button;
910             px = e->xbutton.x;
911             py = e->xbutton.y;
912
913             pcon = frame_context(client, e->xbutton.window, px, py);
914             pcon = mouse_button_frame_context(pcon, e->xbutton.button,
915                                               e->xbutton.state);
916         }
917     case ButtonRelease:
918         /* Wheel buttons don't draw because they are an instant click, so it
919            is a waste of resources to go drawing it.
920            if the user is doing an interactive thing, or has a menu open then
921            the mouse is grabbed (possibly) and if we get these events we don't
922            want to deal with them
923         */
924         if (!(e->xbutton.button == 4 || e->xbutton.button == 5) &&
925             !grab_on_keyboard())
926         {
927             /* use where the press occured */
928             con = frame_context(client, e->xbutton.window, px, py);
929             con = mouse_button_frame_context(con, e->xbutton.button,
930                                              e->xbutton.state);
931
932             /* button presses on CLIENT_CONTEXTs are not accompanied by a
933                release because they are Replayed to the client */
934             if ((e->type == ButtonRelease || CLIENT_CONTEXT(con, client)) &&
935                 e->xbutton.button == pb)
936                 pb = 0, px = py = -1, pcon = OB_FRAME_CONTEXT_NONE;
937
938             but = context_to_button(client->frame, con, TRUE);
939             if (but) {
940                 *but = (e->type == ButtonPress);
941                 frame_adjust_state(client->frame);
942             }
943         }
944         break;
945     case MotionNotify:
946         /* when there is a grab on the pointer, we won't get enter/leave
947            notifies, but we still get motion events */
948         if (grab_on_pointer()) break;
949
950         con = frame_context(client, e->xmotion.window,
951                             e->xmotion.x, e->xmotion.y);
952         switch (con) {
953         case OB_FRAME_CONTEXT_TITLEBAR:
954         case OB_FRAME_CONTEXT_TLCORNER:
955         case OB_FRAME_CONTEXT_TRCORNER:
956             /* we've left the button area inside the titlebar */
957             if (client->frame->max_hover || client->frame->desk_hover ||
958                 client->frame->shade_hover || client->frame->iconify_hover ||
959                 client->frame->close_hover)
960             {
961                 client->frame->max_hover =
962                     client->frame->desk_hover =
963                     client->frame->shade_hover =
964                     client->frame->iconify_hover =
965                     client->frame->close_hover = FALSE;
966                 frame_adjust_state(client->frame);
967             }
968             break;
969         default:
970             but = context_to_button(client->frame, con, FALSE);
971             if (but && !*but && !pb) {
972                 *but = TRUE;
973                 frame_adjust_state(client->frame);
974             }
975             break;
976         }
977         break;
978     case LeaveNotify:
979         con = frame_context(client, e->xcrossing.window,
980                             e->xcrossing.x, e->xcrossing.y);
981         switch (con) {
982         case OB_FRAME_CONTEXT_TITLEBAR:
983         case OB_FRAME_CONTEXT_TLCORNER:
984         case OB_FRAME_CONTEXT_TRCORNER:
985             /* we've left the button area inside the titlebar */
986             client->frame->max_hover =
987                 client->frame->desk_hover =
988                 client->frame->shade_hover =
989                 client->frame->iconify_hover =
990                 client->frame->close_hover = FALSE;
991             if (e->xcrossing.mode == NotifyGrab) {
992                 client->frame->max_press =
993                     client->frame->desk_press =
994                     client->frame->shade_press =
995                     client->frame->iconify_press =
996                     client->frame->close_press = FALSE;
997             }
998             break;
999         case OB_FRAME_CONTEXT_FRAME:
1000             /* When the mouse leaves an animating window, don't use the
1001                corresponding enter events. Pretend like the animating window
1002                doesn't even exist..! */
1003             if (frame_iconify_animating(client->frame))
1004                 event_end_ignore_all_enters(event_start_ignore_all_enters());
1005
1006             ob_debug_type(OB_DEBUG_FOCUS,
1007                           "%sNotify mode %d detail %d on %lx",
1008                           (e->type == EnterNotify ? "Enter" : "Leave"),
1009                           e->xcrossing.mode,
1010                           e->xcrossing.detail, (client?client->window:0));
1011             if (grab_on_keyboard())
1012                 break;
1013             if (config_focus_follow && config_focus_delay &&
1014                 /* leave inferior events can happen when the mouse goes onto
1015                    the window's border and then into the window before the
1016                    delay is up */
1017                 e->xcrossing.detail != NotifyInferior)
1018             {
1019                 obt_main_loop_timeout_remove_data(ob_main_loop,
1020                                                   focus_delay_func,
1021                                                   client, FALSE);
1022             }
1023             break;
1024         default:
1025             but = context_to_button(client->frame, con, FALSE);
1026             if (but) {
1027                 *but = FALSE;
1028                 if (e->xcrossing.mode == NotifyGrab) {
1029                     but = context_to_button(client->frame, con, TRUE);
1030                     *but = FALSE;
1031                 }
1032                 frame_adjust_state(client->frame);
1033             }
1034             break;
1035         }
1036         break;
1037     case EnterNotify:
1038     {
1039         con = frame_context(client, e->xcrossing.window,
1040                             e->xcrossing.x, e->xcrossing.y);
1041         switch (con) {
1042         case OB_FRAME_CONTEXT_FRAME:
1043             if (grab_on_keyboard())
1044                 break;
1045             if (e->xcrossing.mode == NotifyGrab ||
1046                 e->xcrossing.mode == NotifyUngrab ||
1047                 /*ignore enters when we're already in the window */
1048                 e->xcrossing.detail == NotifyInferior)
1049             {
1050                 ob_debug_type(OB_DEBUG_FOCUS,
1051                               "%sNotify mode %d detail %d serial %lu on %lx "
1052                               "IGNORED",
1053                               (e->type == EnterNotify ? "Enter" : "Leave"),
1054                               e->xcrossing.mode,
1055                               e->xcrossing.detail,
1056                               e->xcrossing.serial,
1057                               client?client->window:0);
1058             }
1059             else {
1060                 ob_debug_type(OB_DEBUG_FOCUS,
1061                               "%sNotify mode %d detail %d serial %lu on %lx, "
1062                               "focusing window",
1063                               (e->type == EnterNotify ? "Enter" : "Leave"),
1064                               e->xcrossing.mode,
1065                               e->xcrossing.detail,
1066                               e->xcrossing.serial,
1067                               (client?client->window:0));
1068                 if (config_focus_follow)
1069                     event_enter_client(client);
1070             }
1071             break;
1072         default:
1073             but = context_to_button(client->frame, con, FALSE);
1074             if (but) {
1075                 *but = TRUE;
1076                 if (e->xcrossing.mode == NotifyUngrab) {
1077                     but = context_to_button(client->frame, con, TRUE);
1078                     *but = (con == pcon);
1079                 }
1080                 frame_adjust_state(client->frame);
1081             }
1082             break;
1083         }
1084         break;
1085     }
1086     case ConfigureRequest:
1087     {
1088         if (client->locked)
1089             break;
1090
1091         /* dont compress these unless you're going to watch for property
1092            notifies in between (these can change what the configure would
1093            do to the window).
1094            also you can't compress stacking events
1095         */
1096
1097         gint x, y, w, h;
1098         gboolean move = FALSE;
1099         gboolean resize = FALSE;
1100
1101         /* get the current area */
1102         RECT_TO_DIMS(client->area, x, y, w, h);
1103
1104         ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1105                  "visible %d",
1106                  client->title,
1107                  screen_desktop, client->wmstate, client->frame->visible);
1108         ob_debug("                     x %d y %d w %d h %d b %d",
1109                  x, y, w, h, client->border_width);
1110
1111         if (e->xconfigurerequest.value_mask & CWBorderWidth)
1112             if (client->border_width != e->xconfigurerequest.border_width) {
1113                 client->border_width = e->xconfigurerequest.border_width;
1114
1115                 /* if the border width is changing then that is the same
1116                    as requesting a resize, but we don't actually change
1117                    the client's border, so it will change their root
1118                    coordinates (since they include the border width) and
1119                    we need to a notify then */
1120                 move = TRUE;
1121             }
1122
1123
1124         if (e->xconfigurerequest.value_mask & CWStackMode) {
1125             ObClient *sibling = NULL;
1126             gulong ignore_start;
1127             gboolean ok = TRUE;
1128
1129             /* get the sibling */
1130             if (e->xconfigurerequest.value_mask & CWSibling) {
1131                 ObWindow *win;
1132                 win = window_find(e->xconfigurerequest.above);
1133                 if (win && WINDOW_IS_CLIENT(win) &&
1134                     WINDOW_AS_CLIENT(win) != client)
1135                 {
1136                     sibling = WINDOW_AS_CLIENT(win);
1137                 }
1138                 else
1139                     /* an invalid sibling was specified so don't restack at
1140                        all, it won't make sense no matter what we do */
1141                     ok = FALSE;
1142             }
1143
1144             if (ok) {
1145                 if (!config_focus_under_mouse)
1146                     ignore_start = event_start_ignore_all_enters();
1147                 stacking_restack_request(client, sibling,
1148                                          e->xconfigurerequest.detail);
1149                 if (!config_focus_under_mouse)
1150                     event_end_ignore_all_enters(ignore_start);
1151             }
1152
1153             /* a stacking change moves the window without resizing */
1154             move = TRUE;
1155         }
1156
1157         if ((e->xconfigurerequest.value_mask & CWX) ||
1158             (e->xconfigurerequest.value_mask & CWY) ||
1159             (e->xconfigurerequest.value_mask & CWWidth) ||
1160             (e->xconfigurerequest.value_mask & CWHeight))
1161         {
1162             /* don't allow clients to move shaded windows (fvwm does this)
1163             */
1164             if (e->xconfigurerequest.value_mask & CWX) {
1165                 if (!client->shaded)
1166                     x = e->xconfigurerequest.x;
1167                 move = TRUE;
1168             }
1169             if (e->xconfigurerequest.value_mask & CWY) {
1170                 if (!client->shaded)
1171                     y = e->xconfigurerequest.y;
1172                 move = TRUE;
1173             }
1174
1175             if (e->xconfigurerequest.value_mask & CWWidth) {
1176                 w = e->xconfigurerequest.width;
1177                 resize = TRUE;
1178             }
1179             if (e->xconfigurerequest.value_mask & CWHeight) {
1180                 h = e->xconfigurerequest.height;
1181                 resize = TRUE;
1182             }
1183         }
1184
1185         ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1186                  "move %d resize %d",
1187                  e->xconfigurerequest.value_mask & CWX, x,
1188                  e->xconfigurerequest.value_mask & CWY, y,
1189                  e->xconfigurerequest.value_mask & CWWidth, w,
1190                  e->xconfigurerequest.value_mask & CWHeight, h,
1191                  move, resize);
1192
1193         /* check for broken apps moving to their root position
1194
1195            XXX remove this some day...that would be nice. right now all
1196            kde apps do this when they try activate themselves on another
1197            desktop. eg. open amarok window on desktop 1, switch to desktop
1198            2, click amarok tray icon. it will move by its decoration size.
1199         */
1200         if (x != client->area.x &&
1201             x == (client->frame->area.x + client->frame->size.left -
1202                   (gint)client->border_width) &&
1203             y != client->area.y &&
1204             y == (client->frame->area.y + client->frame->size.top -
1205                   (gint)client->border_width) &&
1206             w == client->area.width &&
1207             h == client->area.height)
1208         {
1209             ob_debug_type(OB_DEBUG_APP_BUGS,
1210                           "Application %s is trying to move via "
1211                           "ConfigureRequest to it's root window position "
1212                           "but it is not using StaticGravity",
1213                           client->title);
1214             /* don't move it */
1215             x = client->area.x;
1216             y = client->area.y;
1217
1218             /* they still requested a move, so don't change whether a
1219                notify is sent or not */
1220         }
1221
1222         {
1223             gint lw, lh;
1224
1225             client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
1226
1227             /* if x was not given, then use gravity to figure out the new
1228                x.  the reference point should not be moved */
1229             if ((e->xconfigurerequest.value_mask & CWWidth &&
1230                  !(e->xconfigurerequest.value_mask & CWX)))
1231                 client_gravity_resize_w(client, &x, client->area.width, w);
1232             /* same for y */
1233             if ((e->xconfigurerequest.value_mask & CWHeight &&
1234                  !(e->xconfigurerequest.value_mask & CWY)))
1235                 client_gravity_resize_h(client, &y, client->area.height,h);
1236
1237             client_find_onscreen(client, &x, &y, w, h, FALSE);
1238
1239             ob_debug("Granting ConfigureRequest x %d y %d w %d h %d",
1240                      x, y, w, h);
1241             client_configure(client, x, y, w, h, FALSE, TRUE, TRUE);
1242         }
1243         break;
1244     }
1245     case UnmapNotify:
1246         ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1247                  "ignores left %d",
1248                  client->window, e->xunmap.event, e->xunmap.from_configure,
1249                  client->ignore_unmaps);
1250         if (client->ignore_unmaps) {
1251             client->ignore_unmaps--;
1252             break;
1253         }
1254         client_unmanage(client);
1255         break;
1256     case DestroyNotify:
1257         ob_debug("DestroyNotify for window 0x%x", client->window);
1258         client_unmanage(client);
1259         break;
1260     case ReparentNotify:
1261         /* this is when the client is first taken captive in the frame */
1262         if (e->xreparent.parent == client->frame->window) break;
1263
1264         /*
1265           This event is quite rare and is usually handled in unmapHandler.
1266           However, if the window is unmapped when the reparent event occurs,
1267           the window manager never sees it because an unmap event is not sent
1268           to an already unmapped window.
1269         */
1270
1271         /* we don't want the reparent event, put it back on the stack for the
1272            X server to deal with after we unmanage the window */
1273         XPutBackEvent(obt_display, e);
1274
1275         ob_debug("ReparentNotify for window 0x%x", client->window);
1276         client_unmanage(client);
1277         break;
1278     case MapRequest:
1279         ob_debug("MapRequest for 0x%lx", client->window);
1280         if (!client->iconic) break; /* this normally doesn't happen, but if it
1281                                        does, we don't want it!
1282                                        it can happen now when the window is on
1283                                        another desktop, but we still don't
1284                                        want it! */
1285         client_activate(client, FALSE, TRUE, TRUE, TRUE);
1286         break;
1287     case ClientMessage:
1288         /* validate cuz we query stuff off the client here */
1289         if (!client_validate(client)) break;
1290
1291         if (e->xclient.format != 32) return;
1292
1293         msgtype = e->xclient.message_type;
1294         if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) {
1295             compress_client_message_event(e, &ce, client->window, msgtype);
1296             if (!client->locked)
1297                 client_set_wm_state(client, e->xclient.data.l[0]);
1298         } else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) {
1299             compress_client_message_event(e, &ce, client->window, msgtype);
1300             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1301                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1302                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1303                                    FALSE, FALSE);
1304         } else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) {
1305             gulong ignore_start;
1306
1307             /* can't compress these */
1308             ob_debug("net_wm_state %s %ld %ld for 0x%lx",
1309                      (e->xclient.data.l[0] == 0 ? "Remove" :
1310                       e->xclient.data.l[0] == 1 ? "Add" :
1311                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1312                      e->xclient.data.l[1], e->xclient.data.l[2],
1313                      client->window);
1314
1315             if (!client->locked) {
1316                 /* ignore enter events caused by these like ob actions do */
1317                 if (!config_focus_under_mouse)
1318                     ignore_start = event_start_ignore_all_enters();
1319                 client_set_state(client, e->xclient.data.l[0],
1320                                  e->xclient.data.l[1], e->xclient.data.l[2]);
1321                 if (!config_focus_under_mouse)
1322                     event_end_ignore_all_enters(ignore_start);
1323             }
1324         } else if (msgtype == OBT_PROP_ATOM(NET_CLOSE_WINDOW)) {
1325             ob_debug("net_close_window for 0x%lx", client->window);
1326             if (!client->locked)
1327                 client_close(client);
1328         } else if (msgtype == OBT_PROP_ATOM(NET_ACTIVE_WINDOW)) {
1329             ob_debug("net_active_window for 0x%lx source=%s",
1330                      client->window,
1331                      (e->xclient.data.l[0] == 0 ? "unknown" :
1332                       (e->xclient.data.l[0] == 1 ? "application" :
1333                        (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1334             /* XXX make use of data.l[2] !? */
1335             if (e->xclient.data.l[0] == 1 || e->xclient.data.l[0] == 2) {
1336                 /* don't use the user's timestamp for client_focus, cuz if it's
1337                    an old broken timestamp (happens all the time) then focus
1338                    won't move even though we're trying to move it
1339                   event_curtime = e->xclient.data.l[1];*/
1340                 if (e->xclient.data.l[1] == 0)
1341                     ob_debug_type(OB_DEBUG_APP_BUGS,
1342                                   "_NET_ACTIVE_WINDOW message for window %s is"
1343                                   " missing a timestamp", client->title);
1344             } else
1345                 ob_debug_type(OB_DEBUG_APP_BUGS,
1346                               "_NET_ACTIVE_WINDOW message for window %s is "
1347                               "missing source indication");
1348             client_activate(client, TRUE, TRUE, TRUE,
1349                             (e->xclient.data.l[0] == 0 ||
1350                              e->xclient.data.l[0] == 2));
1351         } else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) {
1352             ob_debug("net_wm_moveresize for 0x%lx direction %d",
1353                      client->window, e->xclient.data.l[2]);
1354             if ((Atom)e->xclient.data.l[2] ==
1355                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1356                 (Atom)e->xclient.data.l[2] ==
1357                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
1358                 (Atom)e->xclient.data.l[2] ==
1359                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT) ||
1360                 (Atom)e->xclient.data.l[2] ==
1361                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
1362                 (Atom)e->xclient.data.l[2] ==
1363                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
1364                 (Atom)e->xclient.data.l[2] ==
1365                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) ||
1366                 (Atom)e->xclient.data.l[2] ==
1367                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM) ||
1368                 (Atom)e->xclient.data.l[2] ==
1369                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) ||
1370                 (Atom)e->xclient.data.l[2] ==
1371                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
1372                 (Atom)e->xclient.data.l[2] ==
1373                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
1374                 (Atom)e->xclient.data.l[2] ==
1375                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD) ||
1376                 (Atom)e->xclient.data.l[2] ==
1377                 OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
1378             {
1379                 moveresize_start(client, e->xclient.data.l[0],
1380                                  e->xclient.data.l[1], e->xclient.data.l[3],
1381                                  e->xclient.data.l[2]);
1382             }
1383             else if ((Atom)e->xclient.data.l[2] ==
1384                      OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
1385                 moveresize_end(TRUE);
1386         } else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
1387             if (client->locked)
1388                 break;
1389
1390             gint ograv, x, y, w, h;
1391
1392             ograv = client->gravity;
1393
1394             if (e->xclient.data.l[0] & 0xff)
1395                 client->gravity = e->xclient.data.l[0] & 0xff;
1396
1397             if (e->xclient.data.l[0] & 1 << 8)
1398                 x = e->xclient.data.l[1];
1399             else
1400                 x = client->area.x;
1401             if (e->xclient.data.l[0] & 1 << 9)
1402                 y = e->xclient.data.l[2];
1403             else
1404                 y = client->area.y;
1405
1406             if (e->xclient.data.l[0] & 1 << 10) {
1407                 w = e->xclient.data.l[3];
1408
1409                 /* if x was not given, then use gravity to figure out the new
1410                    x.  the reference point should not be moved */
1411                 if (!(e->xclient.data.l[0] & 1 << 8))
1412                     client_gravity_resize_w(client, &x, client->area.width, w);
1413             }
1414             else
1415                 w = client->area.width;
1416
1417             if (e->xclient.data.l[0] & 1 << 11) {
1418                 h = e->xclient.data.l[4];
1419
1420                 /* same for y */
1421                 if (!(e->xclient.data.l[0] & 1 << 9))
1422                     client_gravity_resize_h(client, &y, client->area.height,h);
1423             }
1424             else
1425                 h = client->area.height;
1426
1427             ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)",
1428                      e->xclient.data.l[0] & 1 << 8, x,
1429                      e->xclient.data.l[0] & 1 << 9, y,
1430                      client->gravity);
1431
1432             client_find_onscreen(client, &x, &y, w, h, FALSE);
1433
1434             client_configure(client, x, y, w, h, FALSE, TRUE, FALSE);
1435
1436             client->gravity = ograv;
1437         } else if (msgtype == OBT_PROP_ATOM(NET_RESTACK_WINDOW)) {
1438             if (e->xclient.data.l[0] != 2) {
1439                 ob_debug_type(OB_DEBUG_APP_BUGS,
1440                               "_NET_RESTACK_WINDOW sent for window %s with "
1441                               "invalid source indication %ld",
1442                               client->title, e->xclient.data.l[0]);
1443             } else {
1444                 ObClient *sibling = NULL;
1445                 if (e->xclient.data.l[1]) {
1446                     ObWindow *win = window_find(e->xclient.data.l[1]);
1447                     if (WINDOW_IS_CLIENT(win) &&
1448                         WINDOW_AS_CLIENT(win) != client)
1449                     {
1450                         sibling = WINDOW_AS_CLIENT(win);
1451                     }
1452                     if (sibling == NULL)
1453                         ob_debug_type(OB_DEBUG_APP_BUGS,
1454                                       "_NET_RESTACK_WINDOW sent for window %s "
1455                                       "with invalid sibling 0x%x",
1456                                  client->title, e->xclient.data.l[1]);
1457                 }
1458                 if (e->xclient.data.l[2] == Below ||
1459                     e->xclient.data.l[2] == BottomIf ||
1460                     e->xclient.data.l[2] == Above ||
1461                     e->xclient.data.l[2] == TopIf ||
1462                     e->xclient.data.l[2] == Opposite)
1463                 {
1464                     gulong ignore_start;
1465
1466                     if (!config_focus_under_mouse)
1467                         ignore_start = event_start_ignore_all_enters();
1468                     /* just raise, don't activate */
1469                     stacking_restack_request(client, sibling,
1470                                              e->xclient.data.l[2]);
1471                     if (!config_focus_under_mouse)
1472                         event_end_ignore_all_enters(ignore_start);
1473
1474                     /* send a synthetic ConfigureNotify, cuz this is supposed
1475                        to be like a ConfigureRequest. */
1476                     client_reconfigure(client, TRUE);
1477                 } else
1478                     ob_debug_type(OB_DEBUG_APP_BUGS,
1479                                   "_NET_RESTACK_WINDOW sent for window %s "
1480                                   "with invalid detail %d",
1481                                   client->title, e->xclient.data.l[2]);
1482             }
1483         }
1484         break;
1485     case PropertyNotify:
1486         /* validate cuz we query stuff off the client here */
1487         if (!client_validate(client)) break;
1488
1489         /* compress changes to a single property into a single change */
1490         while (XCheckTypedWindowEvent(obt_display, client->window,
1491                                       e->type, &ce)) {
1492             Atom a, b;
1493
1494             /* XXX: it would be nice to compress ALL changes to a property,
1495                not just changes in a row without other props between. */
1496
1497             a = ce.xproperty.atom;
1498             b = e->xproperty.atom;
1499
1500             if (a == b)
1501                 continue;
1502             if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
1503                  a == OBT_PROP_ATOM(WM_NAME) ||
1504                  a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1505                  a == OBT_PROP_ATOM(WM_ICON_NAME))
1506                 &&
1507                 (b == OBT_PROP_ATOM(NET_WM_NAME) ||
1508                  b == OBT_PROP_ATOM(WM_NAME) ||
1509                  b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1510                  b == OBT_PROP_ATOM(WM_ICON_NAME))) {
1511                 continue;
1512             }
1513             if (a == OBT_PROP_ATOM(NET_WM_ICON) &&
1514                 b == OBT_PROP_ATOM(NET_WM_ICON))
1515                 continue;
1516
1517             XPutBackEvent(obt_display, &ce);
1518             break;
1519         }
1520
1521         msgtype = e->xproperty.atom;
1522         if (msgtype == XA_WM_NORMAL_HINTS) {
1523             ob_debug("Update NORMAL hints");
1524             client_update_normal_hints(client);
1525             /* normal hints can make a window non-resizable */
1526             client_setup_decor_and_functions(client, FALSE);
1527
1528             /* make sure the client's sizes are within its bounds, but only
1529                reconfigure the window if it needs to. emacs will update its
1530                normal hints every time it receives a conigurenotify */
1531             client_reconfigure(client, FALSE);
1532         } else if (msgtype == XA_WM_HINTS) {
1533             client_update_wmhints(client);
1534         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1535             client_update_transient_for(client);
1536             client_get_type_and_transientness(client);
1537             /* type may have changed, so update the layer */
1538             client_calc_layer(client);
1539             client_setup_decor_and_functions(client, TRUE);
1540         } else if (msgtype == OBT_PROP_ATOM(NET_WM_NAME) ||
1541                    msgtype == OBT_PROP_ATOM(WM_NAME) ||
1542                    msgtype == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1543                    msgtype == OBT_PROP_ATOM(WM_ICON_NAME)) {
1544             client_update_title(client);
1545         } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
1546             client_update_protocols(client);
1547             client_setup_decor_and_functions(client, TRUE);
1548         }
1549         else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT) ||
1550                  msgtype == OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL)) {
1551             client_update_strut(client);
1552         }
1553         else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON)) {
1554             client_update_icons(client);
1555         }
1556         else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY)) {
1557             client_update_icon_geometry(client);
1558         }
1559         else if (msgtype == OBT_PROP_ATOM(NET_WM_USER_TIME)) {
1560             guint32 t;
1561             if (client == focus_client &&
1562                 OBT_PROP_GET32(client->window, NET_WM_USER_TIME, CARDINAL, &t)
1563                 && t && !event_time_after(t, e->xproperty.time) &&
1564                 (!event_last_user_time ||
1565                  event_time_after(t, event_last_user_time)))
1566             {
1567                 event_last_user_time = t;
1568             }
1569         }
1570 #ifdef SYNC
1571         else if (msgtype == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER)) {
1572             client_update_sync_request_counter(client);
1573         }
1574 #endif
1575         break;
1576     case ColormapNotify:
1577         client_update_colormap(client, e->xcolormap.colormap);
1578         break;
1579     default:
1580         ;
1581 #ifdef SHAPE
1582         if (obt_display_extension_shape &&
1583             e->type == obt_display_extension_shape_basep)
1584         {
1585             client->shaped = ((XShapeEvent*)e)->shaped;
1586             frame_adjust_shape(client->frame);
1587         }
1588 #endif
1589     }
1590 }
1591
1592 static void event_handle_dock(ObDock *s, XEvent *e)
1593 {
1594     switch (e->type) {
1595     case ButtonPress:
1596         if (e->xbutton.button == 1)
1597             stacking_raise(DOCK_AS_WINDOW(s));
1598         else if (e->xbutton.button == 2)
1599             stacking_lower(DOCK_AS_WINDOW(s));
1600         break;
1601     case EnterNotify:
1602         dock_hide(FALSE);
1603         break;
1604     case LeaveNotify:
1605         /* don't hide when moving into a dock app */
1606         if (e->xcrossing.detail != NotifyInferior)
1607             dock_hide(TRUE);
1608         break;
1609     }
1610 }
1611
1612 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1613 {
1614     switch (e->type) {
1615     case MotionNotify:
1616         dock_app_drag(app, &e->xmotion);
1617         break;
1618     case UnmapNotify:
1619         if (app->ignore_unmaps) {
1620             app->ignore_unmaps--;
1621             break;
1622         }
1623         dock_unmanage(app, TRUE);
1624         break;
1625     case DestroyNotify:
1626     case ReparentNotify:
1627         dock_unmanage(app, FALSE);
1628         break;
1629     case ConfigureNotify:
1630         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1631         break;
1632     }
1633 }
1634
1635 static ObMenuFrame* find_active_menu(void)
1636 {
1637     GList *it;
1638     ObMenuFrame *ret = NULL;
1639
1640     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1641         ret = it->data;
1642         if (ret->selected)
1643             break;
1644         ret = NULL;
1645     }
1646     return ret;
1647 }
1648
1649 static ObMenuFrame* find_active_or_last_menu(void)
1650 {
1651     ObMenuFrame *ret = NULL;
1652
1653     ret = find_active_menu();
1654     if (!ret && menu_frame_visible)
1655         ret = menu_frame_visible->data;
1656     return ret;
1657 }
1658
1659 static gboolean event_handle_prompt(ObPrompt *p, XEvent *e)
1660 {
1661     switch (e->type) {
1662     case ButtonPress:
1663     case ButtonRelease:
1664     case MotionNotify:
1665         return prompt_mouse_event(p, e);
1666         break;
1667     case KeyPress:
1668         return prompt_key_event(p, e);
1669         break;
1670     }
1671     return FALSE;
1672 }
1673
1674 static gboolean event_handle_menu_input(XEvent *ev)
1675 {
1676     gboolean ret = FALSE;
1677
1678     if (ev->type == ButtonRelease || ev->type == ButtonPress) {
1679         ObMenuEntryFrame *e;
1680
1681         if (menu_hide_delay_reached() &&
1682             (ev->xbutton.button < 4 || ev->xbutton.button > 5))
1683         {
1684             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1685                                             ev->xbutton.y_root)))
1686             {
1687                 if (ev->type == ButtonPress && e->frame->child)
1688                     menu_frame_select(e->frame->child, NULL, TRUE);
1689                 menu_frame_select(e->frame, e, TRUE);
1690                 if (ev->type == ButtonRelease)
1691                     menu_entry_frame_execute(e, ev->xbutton.state);
1692             }
1693             else if (ev->type == ButtonRelease)
1694                 menu_frame_hide_all();
1695         }
1696         ret = TRUE;
1697     }
1698     else if (ev->type == MotionNotify) {
1699         ObMenuFrame *f;
1700         ObMenuEntryFrame *e;
1701
1702         if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1703                                         ev->xmotion.y_root)))
1704             if (!(f = find_active_menu()) ||
1705                 f == e->frame ||
1706                 f->parent == e->frame ||
1707                 f->child == e->frame)
1708                 menu_frame_select(e->frame, e, FALSE);
1709     }
1710     else if (ev->type == KeyPress || ev->type == KeyRelease) {
1711         guint keycode, state;
1712         gunichar unikey;
1713         ObMenuFrame *frame;
1714
1715         keycode = ev->xkey.keycode;
1716         state = ev->xkey.state;
1717         unikey = obt_keyboard_keycode_to_unichar(keycode);
1718
1719         frame = find_active_or_last_menu();
1720         if (frame == NULL)
1721             g_assert_not_reached(); /* there is no active menu */
1722
1723         /* Allow control while going thru the menu */
1724         else if (ev->type == KeyPress && (state & ~ControlMask) == 0) {
1725             frame->got_press = TRUE;
1726
1727             if (keycode == ob_keycode(OB_KEY_ESCAPE)) {
1728                 menu_frame_hide_all();
1729                 ret = TRUE;
1730             }
1731
1732             else if (keycode == ob_keycode(OB_KEY_LEFT)) {
1733                 /* Left goes to the parent menu */
1734                 menu_frame_select(frame, NULL, TRUE);
1735                 ret = TRUE;
1736             }
1737
1738             else if (keycode == ob_keycode(OB_KEY_RIGHT)) {
1739                 /* Right goes to the selected submenu */
1740                 if (frame->child) menu_frame_select_next(frame->child);
1741                 ret = TRUE;
1742             }
1743
1744             else if (keycode == ob_keycode(OB_KEY_UP)) {
1745                 menu_frame_select_previous(frame);
1746                 ret = TRUE;
1747             }
1748
1749             else if (keycode == ob_keycode(OB_KEY_DOWN)) {
1750                 menu_frame_select_next(frame);
1751                 ret = TRUE;
1752             }
1753         }
1754
1755         /* Use KeyRelease events for running things so that the key release
1756            doesn't get sent to the focused application.
1757
1758            Allow ControlMask only, and don't bother if the menu is empty */
1759         else if (ev->type == KeyRelease && (state & ~ControlMask) == 0 &&
1760                  frame->entries && frame->got_press)
1761         {
1762             if (keycode == ob_keycode(OB_KEY_RETURN)) {
1763                 /* Enter runs the active item or goes into the submenu.
1764                    Control-Enter runs it without closing the menu. */
1765                 if (frame->child)
1766                     menu_frame_select_next(frame->child);
1767                 else if (frame->selected)
1768                     menu_entry_frame_execute(frame->selected, state);
1769
1770                 ret = TRUE;
1771             }
1772
1773             /* keyboard accelerator shortcuts. (if it was a valid key) */
1774             else if (unikey != 0) {
1775                 GList *start;
1776                 GList *it;
1777                 ObMenuEntryFrame *found = NULL;
1778                 guint num_found = 0;
1779
1780                 /* start after the selected one */
1781                 start = frame->entries;
1782                 if (frame->selected) {
1783                     for (it = start; frame->selected != it->data;
1784                          it = g_list_next(it))
1785                         g_assert(it != NULL); /* nothing was selected? */
1786                     /* next with wraparound */
1787                     start = g_list_next(it);
1788                     if (start == NULL) start = frame->entries;
1789                 }
1790
1791                 it = start;
1792                 do {
1793                     ObMenuEntryFrame *e = it->data;
1794                     gunichar entrykey = 0;
1795
1796                     if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
1797                         entrykey = e->entry->data.normal.shortcut;
1798                     else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1799                         entrykey = e->entry->data.submenu.submenu->shortcut;
1800
1801                     if (unikey == entrykey) {
1802                         if (found == NULL) found = e;
1803                         ++num_found;
1804                     }
1805
1806                     /* next with wraparound */
1807                     it = g_list_next(it);
1808                     if (it == NULL) it = frame->entries;
1809                 } while (it != start);
1810
1811                 if (found) {
1812                     if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1813                         num_found == 1)
1814                     {
1815                         menu_frame_select(frame, found, TRUE);
1816                         usleep(50000); /* highlight the item for a short bit so
1817                                           the user can see what happened */
1818                         menu_entry_frame_execute(found, state);
1819                     } else {
1820                         menu_frame_select(frame, found, TRUE);
1821                         if (num_found == 1)
1822                             menu_frame_select_next(frame->child);
1823                     }
1824
1825                     ret = TRUE;
1826                 }
1827             }
1828         }
1829     }
1830
1831     return ret;
1832 }
1833
1834 static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
1835 {
1836     ObMenuFrame *f;
1837     ObMenuEntryFrame *e;
1838
1839     switch (ev->type) {
1840     case EnterNotify:
1841         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1842             if (e->ignore_enters)
1843                 --e->ignore_enters;
1844             else if (!(f = find_active_menu()) ||
1845                      f == e->frame ||
1846                      f->parent == e->frame ||
1847                      f->child == e->frame)
1848                 menu_frame_select(e->frame, e, FALSE);
1849         }
1850         break;
1851     case LeaveNotify:
1852         /*ignore leaves when we're already in the window */
1853         if (ev->xcrossing.detail == NotifyInferior)
1854             break;
1855
1856         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1857             (f = find_active_menu()) && f->selected == e &&
1858             e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1859         {
1860             menu_frame_select(e->frame, NULL, FALSE);
1861         }
1862         break;
1863     }
1864 }
1865
1866 static void event_handle_user_input(ObClient *client, XEvent *e)
1867 {
1868     g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1869              e->type == MotionNotify || e->type == KeyPress ||
1870              e->type == KeyRelease);
1871
1872     if (menu_frame_visible) {
1873         if (event_handle_menu_input(e))
1874             /* don't use the event if the menu used it, but if the menu
1875                didn't use it and it's a keypress that is bound, it will
1876                close the menu and be used */
1877             return;
1878     }
1879
1880     /* if the keyboard interactive action uses the event then dont
1881        use it for bindings. likewise is moveresize uses the event. */
1882     if (!actions_interactive_input_event(e) && !moveresize_event(e)) {
1883         if (moveresize_in_progress)
1884             /* make further actions work on the client being
1885                moved/resized */
1886             client = moveresize_client;
1887
1888         if (e->type == ButtonPress ||
1889             e->type == ButtonRelease ||
1890             e->type == MotionNotify)
1891         {
1892             /* the frame may not be "visible" but they can still click on it
1893                in the case where it is animating before disappearing */
1894             if (!client || !frame_iconify_animating(client->frame))
1895                 mouse_event(client, e);
1896         } else
1897             keyboard_event((focus_cycle_target ? focus_cycle_target :
1898                             (client ? client : focus_client)), e);
1899     }
1900 }
1901
1902 static void focus_delay_dest(gpointer data)
1903 {
1904     g_free(data);
1905 }
1906
1907 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1908 {
1909     const ObFocusDelayData *f1 = d1;
1910     return f1->client == d2;
1911 }
1912
1913 static gboolean focus_delay_func(gpointer data)
1914 {
1915     ObFocusDelayData *d = data;
1916     Time old = event_curtime;
1917
1918     /* don't move focus and kill the menu or the move/resize */
1919     if (menu_frame_visible || moveresize_in_progress) return FALSE;
1920
1921     event_curtime = d->time;
1922     event_curserial = d->serial;
1923     if (client_focus(d->client) && config_focus_raise)
1924         stacking_raise(CLIENT_AS_WINDOW(d->client));
1925     event_curtime = old;
1926     return FALSE; /* no repeat */
1927 }
1928
1929 static void focus_delay_client_dest(ObClient *client, gpointer data)
1930 {
1931     obt_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1932                                       client, FALSE);
1933 }
1934
1935 void event_halt_focus_delay(void)
1936 {
1937     /* ignore all enter events up till the event which caused this to occur */
1938     if (event_curserial) event_ignore_enter_range(1, event_curserial);
1939     obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1940 }
1941
1942 gulong event_start_ignore_all_enters(void)
1943 {
1944     return NextRequest(obt_display);
1945 }
1946
1947 static void event_ignore_enter_range(gulong start, gulong end)
1948 {
1949     ObSerialRange *r;
1950
1951     g_assert(start != 0);
1952     g_assert(end != 0);
1953
1954     r = g_new(ObSerialRange, 1);
1955     r->start = start;
1956     r->end = end;
1957     ignore_serials = g_slist_prepend(ignore_serials, r);
1958
1959     ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu",
1960                   r->start, r->end);
1961
1962     /* increment the serial so we don't ignore events we weren't meant to */
1963     OBT_PROP_ERASE(screen_support_win, MOTIF_WM_HINTS);
1964 }
1965
1966 void event_end_ignore_all_enters(gulong start)
1967 {
1968     /* Use (NextRequest-1) so that we ignore up to the current serial only.
1969        Inside event_ignore_enter_range, we increment the serial by one, but if
1970        we ignore that serial too, then any enter events generated by mouse
1971        movement will be ignored until we create some further network traffic.
1972        Instead ignore up to NextRequest-1, then when we increment the serial,
1973        we will be *past* the range of ignored serials */
1974     event_ignore_enter_range(start, NextRequest(obt_display)-1);
1975 }
1976
1977 static gboolean is_enter_focus_event_ignored(gulong serial)
1978 {
1979     GSList *it, *next;
1980
1981     for (it = ignore_serials; it; it = next) {
1982         ObSerialRange *r = it->data;
1983
1984         next = g_slist_next(it);
1985
1986         if ((glong)(serial - r->end) > 0) {
1987             /* past the end */
1988             ignore_serials = g_slist_delete_link(ignore_serials, it);
1989             g_free(r);
1990         }
1991         else if ((glong)(serial - r->start) >= 0)
1992             return TRUE;
1993     }
1994     return FALSE;
1995 }
1996
1997 void event_cancel_all_key_grabs(void)
1998 {
1999     if (actions_interactive_act_running()) {
2000         actions_interactive_cancel_act();
2001         ob_debug("KILLED interactive action");
2002     }
2003     else if (menu_frame_visible) {
2004         menu_frame_hide_all();
2005         ob_debug("KILLED open menus");
2006     }
2007     else if (moveresize_in_progress) {
2008         moveresize_end(TRUE);
2009         ob_debug("KILLED interactive moveresize");
2010     }
2011     else if (grab_on_keyboard()) {
2012         ungrab_keyboard();
2013         ob_debug("KILLED active grab on keyboard");
2014     }
2015     else
2016         ungrab_passive_key();
2017
2018     XSync(obt_display, FALSE);
2019 }
2020
2021 gboolean event_time_after(Time t1, Time t2)
2022 {
2023     g_assert(t1 != CurrentTime);
2024     g_assert(t2 != CurrentTime);
2025
2026     /*
2027       Timestamp values wrap around (after about 49.7 days). The server, given
2028       its current time is represented by timestamp T, always interprets
2029       timestamps from clients by treating half of the timestamp space as being
2030       later in time than T.
2031       - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
2032     */
2033
2034     /* TIME_HALF is half of the number space of a Time type variable */
2035 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
2036
2037     if (t2 >= TIME_HALF)
2038         /* t2 is in the second half so t1 might wrap around and be smaller than
2039            t2 */
2040         return t1 >= t2 || t1 < (t2 + TIME_HALF);
2041     else
2042         /* t2 is in the first half so t1 has to come after it */
2043         return t1 >= t2 && t1 < (t2 + TIME_HALF);
2044 }
2045
2046 Time event_get_server_time(void)
2047 {
2048     /* Generate a timestamp */
2049     XEvent event;
2050
2051     XChangeProperty(obt_display, screen_support_win,
2052                     OBT_PROP_ATOM(WM_CLASS), OBT_PROP_ATOM(STRING),
2053                     8, PropModeAppend, NULL, 0);
2054     XWindowEvent(obt_display, screen_support_win, PropertyChangeMask, &event);
2055     return event.xproperty.time;
2056 }