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