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