]> Dogcows Code - chaz/openbox/blob - openbox/event.c
eat enter events after unmanaging a window
[chaz/openbox] / 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 Ben 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 "client.h"
26 #include "xerror.h"
27 #include "prop.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "frame.h"
31 #include "menu.h"
32 #include "menuframe.h"
33 #include "keyboard.h"
34 #include "mouse.h"
35 #include "mainloop.h"
36 #include "framerender.h"
37 #include "focus.h"
38 #include "moveresize.h"
39 #include "group.h"
40 #include "stacking.h"
41 #include "extensions.h"
42
43 #include <X11/Xlib.h>
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
46 #include <glib.h>
47
48 #ifdef HAVE_SYS_SELECT_H
49 # include <sys/select.h>
50 #endif
51 #ifdef HAVE_SIGNAL_H
52 # include <signal.h>
53 #endif
54 #ifdef XKB
55 # include <X11/XKBlib.h>
56 #endif
57
58 #ifdef USE_SM
59 #include <X11/ICE/ICElib.h>
60 #endif
61
62 typedef struct
63 {
64 gboolean ignored;
65 } ObEventData;
66
67 typedef struct
68 {
69 ObClient *client;
70 Time time;
71 } ObFocusDelayData;
72
73 static void event_process(const XEvent *e, gpointer data);
74 static void event_client_dest(ObClient *client, gpointer data);
75 static void event_handle_root(XEvent *e);
76 static void event_handle_menu(XEvent *e);
77 static void event_handle_dock(ObDock *s, XEvent *e);
78 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
79 static void event_handle_client(ObClient *c, XEvent *e);
80 static void event_handle_group(ObGroup *g, XEvent *e);
81
82 static void focus_delay_dest(gpointer data);
83 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
84 static gboolean focus_delay_func(gpointer data);
85 static void focus_delay_client_dest(ObClient *client, gpointer data);
86
87 static gboolean menu_hide_delay_func(gpointer data);
88
89 /* The time for the current event being processed */
90 Time event_curtime = CurrentTime;
91
92 /*! The value of the mask for the NumLock modifier */
93 guint NumLockMask;
94 /*! The value of the mask for the ScrollLock modifier */
95 guint ScrollLockMask;
96 /*! The key codes for the modifier keys */
97 static XModifierKeymap *modmap;
98 /*! Table of the constant modifier masks */
99 static const gint mask_table[] = {
100 ShiftMask, LockMask, ControlMask, Mod1Mask,
101 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
102 };
103 static gint mask_table_size;
104
105 static guint ignore_enter_focus = 0;
106
107 static gboolean menu_can_hide;
108
109 #ifdef USE_SM
110 static void ice_handler(gint fd, gpointer conn)
111 {
112 Bool b;
113 IceProcessMessages(conn, NULL, &b);
114 }
115
116 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
117 IcePointer *watch_data)
118 {
119 static gint fd = -1;
120
121 if (opening) {
122 fd = IceConnectionNumber(conn);
123 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
124 } else {
125 ob_main_loop_fd_remove(ob_main_loop, fd);
126 fd = -1;
127 }
128 }
129 #endif
130
131 void event_startup(gboolean reconfig)
132 {
133 if (reconfig) return;
134
135 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
136
137 /* get lock masks that are defined by the display (not constant) */
138 modmap = XGetModifierMapping(ob_display);
139 g_assert(modmap);
140 if (modmap && modmap->max_keypermod > 0) {
141 size_t cnt;
142 const size_t size = mask_table_size * modmap->max_keypermod;
143 /* get the values of the keyboard lock modifiers
144 Note: Caps lock is not retrieved the same way as Scroll and Num
145 lock since it doesn't need to be. */
146 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
147 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
148 XK_Scroll_Lock);
149
150 for (cnt = 0; cnt < size; ++cnt) {
151 if (! modmap->modifiermap[cnt]) continue;
152
153 if (num_lock == modmap->modifiermap[cnt])
154 NumLockMask = mask_table[cnt / modmap->max_keypermod];
155 if (scroll_lock == modmap->modifiermap[cnt])
156 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
157 }
158 }
159
160 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
161
162 #ifdef USE_SM
163 IceAddConnectionWatch(ice_watch, NULL);
164 #endif
165
166 client_add_destructor(focus_delay_client_dest, NULL);
167 client_add_destructor(event_client_dest, NULL);
168 }
169
170 void event_shutdown(gboolean reconfig)
171 {
172 if (reconfig) return;
173
174 #ifdef USE_SM
175 IceRemoveConnectionWatch(ice_watch, NULL);
176 #endif
177
178 client_remove_destructor(focus_delay_client_dest);
179 client_remove_destructor(event_client_dest);
180 XFreeModifiermap(modmap);
181 }
182
183 static Window event_get_window(XEvent *e)
184 {
185 Window window;
186
187 /* pick a window */
188 switch (e->type) {
189 case SelectionClear:
190 window = RootWindow(ob_display, ob_screen);
191 break;
192 case MapRequest:
193 window = e->xmap.window;
194 break;
195 case UnmapNotify:
196 window = e->xunmap.window;
197 break;
198 case DestroyNotify:
199 window = e->xdestroywindow.window;
200 break;
201 case ConfigureRequest:
202 window = e->xconfigurerequest.window;
203 break;
204 case ConfigureNotify:
205 window = e->xconfigure.window;
206 break;
207 default:
208 #ifdef XKB
209 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
210 switch (((XkbAnyEvent*)e)->xkb_type) {
211 case XkbBellNotify:
212 window = ((XkbBellNotifyEvent*)e)->window;
213 default:
214 window = None;
215 }
216 } else
217 #endif
218 window = e->xany.window;
219 }
220 return window;
221 }
222
223 static void event_set_curtime(XEvent *e)
224 {
225 Time t = CurrentTime;
226
227 /* grab the lasttime and hack up the state */
228 switch (e->type) {
229 case ButtonPress:
230 case ButtonRelease:
231 t = e->xbutton.time;
232 break;
233 case KeyPress:
234 t = e->xkey.time;
235 break;
236 case KeyRelease:
237 t = e->xkey.time;
238 break;
239 case MotionNotify:
240 t = e->xmotion.time;
241 break;
242 case PropertyNotify:
243 t = e->xproperty.time;
244 break;
245 case EnterNotify:
246 case LeaveNotify:
247 t = e->xcrossing.time;
248 break;
249 default:
250 /* if more event types are anticipated, get their timestamp
251 explicitly */
252 break;
253 }
254
255 event_curtime = t;
256 }
257
258 #define STRIP_MODS(s) \
259 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
260 /* kill off the Button1Mask etc, only want the modifiers */ \
261 s &= (ControlMask | ShiftMask | Mod1Mask | \
262 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
263
264 static void event_hack_mods(XEvent *e)
265 {
266 #ifdef XKB
267 XkbStateRec xkb_state;
268 #endif
269 KeyCode *kp;
270 gint i, k;
271
272 switch (e->type) {
273 case ButtonPress:
274 case ButtonRelease:
275 STRIP_MODS(e->xbutton.state);
276 break;
277 case KeyPress:
278 STRIP_MODS(e->xkey.state);
279 break;
280 case KeyRelease:
281 STRIP_MODS(e->xkey.state);
282 /* remove from the state the mask of the modifier being released, if
283 it is a modifier key being released (this is a little ugly..) */
284 #ifdef XKB
285 if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
286 e->xkey.state = xkb_state.compat_state;
287 break;
288 }
289 #endif
290 kp = modmap->modifiermap;
291 for (i = 0; i < mask_table_size; ++i) {
292 for (k = 0; k < modmap->max_keypermod; ++k) {
293 if (*kp == e->xkey.keycode) { /* found the keycode */
294 /* remove the mask for it */
295 e->xkey.state &= ~mask_table[i];
296 /* cause the first loop to break; */
297 i = mask_table_size;
298 break; /* get outta here! */
299 }
300 ++kp;
301 }
302 }
303 break;
304 case MotionNotify:
305 STRIP_MODS(e->xmotion.state);
306 /* compress events */
307 {
308 XEvent ce;
309 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
310 e->type, &ce)) {
311 e->xmotion.x_root = ce.xmotion.x_root;
312 e->xmotion.y_root = ce.xmotion.y_root;
313 }
314 }
315 break;
316 }
317 }
318
319 static gboolean wanted_focusevent(XEvent *e)
320 {
321 gint mode = e->xfocus.mode;
322 gint detail = e->xfocus.detail;
323 Window win = e->xany.window;
324
325 if (e->type == FocusIn) {
326
327 /* These are ones we never want.. */
328
329 /* This means focus was given by a keyboard/mouse grab. */
330 if (mode == NotifyGrab)
331 return FALSE;
332 /* This means focus was given back from a keyboard/mouse grab. */
333 if (mode == NotifyUngrab)
334 return FALSE;
335
336 /* These are the ones we want.. */
337
338 if (win == RootWindow(ob_display, ob_screen)) {
339 /* This means focus reverted off of a client */
340 if (detail == NotifyPointerRoot || detail == NotifyDetailNone ||
341 detail == NotifyInferior)
342 return TRUE;
343 else
344 return FALSE;
345 }
346
347 /* This means focus moved from the root window to a client */
348 if (detail == NotifyVirtual)
349 return TRUE;
350 /* This means focus moved from one client to another */
351 if (detail == NotifyNonlinearVirtual)
352 return TRUE;
353
354 /* This means focus reverted off of a client */
355 if (detail == NotifyInferior)
356 return TRUE;
357
358 /* Otherwise.. */
359 return FALSE;
360 } else {
361 g_assert(e->type == FocusOut);
362
363
364 /* These are ones we never want.. */
365
366 /* This means focus was taken by a keyboard/mouse grab. */
367 if (mode == NotifyGrab)
368 return FALSE;
369
370 /* Focus left the root window revertedto state */
371 if (win == RootWindow(ob_display, ob_screen))
372 return FALSE;
373
374 /* These are the ones we want.. */
375
376 /* This means focus moved from a client to the root window */
377 if (detail == NotifyVirtual)
378 return TRUE;
379 /* This means focus moved from one client to another */
380 if (detail == NotifyNonlinearVirtual)
381 return TRUE;
382
383 /* Otherwise.. */
384 return FALSE;
385 }
386 }
387
388 static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
389 {
390 return e->type == FocusIn && wanted_focusevent(e);
391 }
392
393 static gboolean event_ignore(XEvent *e, ObClient *client)
394 {
395 switch(e->type) {
396 case EnterNotify:
397 case LeaveNotify:
398 return keyboard_interactively_grabbed();
399 case FocusIn:
400 case FocusOut:
401 if (!wanted_focusevent(e)) {
402 ob_debug_type(OB_DEBUG_FOCUS, "focus event ignored\n");
403 return TRUE;
404 }
405 ob_debug_type(OB_DEBUG_FOCUS, "focus event used;\n");
406 break;
407 }
408 return FALSE;
409 }
410
411 static void event_process(const XEvent *ec, gpointer data)
412 {
413 Window window;
414 ObGroup *group = NULL;
415 ObClient *client = NULL;
416 ObDock *dock = NULL;
417 ObDockApp *dockapp = NULL;
418 ObWindow *obwin = NULL;
419 XEvent ee, *e;
420 ObEventData *ed = data;
421
422 /* make a copy we can mangle */
423 ee = *ec;
424 e = &ee;
425
426 window = event_get_window(e);
427 if (!(e->type == PropertyNotify &&
428 (group = g_hash_table_lookup(group_map, &window))))
429 if ((obwin = g_hash_table_lookup(window_map, &window))) {
430 switch (obwin->type) {
431 case Window_Dock:
432 dock = WINDOW_AS_DOCK(obwin);
433 break;
434 case Window_DockApp:
435 dockapp = WINDOW_AS_DOCKAPP(obwin);
436 break;
437 case Window_Client:
438 client = WINDOW_AS_CLIENT(obwin);
439 break;
440 case Window_Menu:
441 case Window_Internal:
442 /* not to be used for events */
443 g_assert_not_reached();
444 break;
445 }
446 }
447
448 if (e->type == FocusIn || e->type == FocusOut) {
449 gint mode = e->xfocus.mode;
450 gint detail = e->xfocus.detail;
451 Window window = e->xfocus.window;
452 if (detail == NotifyVirtual) {
453 ob_debug_type(OB_DEBUG_FOCUS,
454 "FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
455 (e->type == FocusIn ? "IN" : "OUT"), window);
456 }
457
458 else if (detail == NotifyNonlinearVirtual) {
459 ob_debug_type(OB_DEBUG_FOCUS,
460 "FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
461 (e->type == FocusIn ? "IN" : "OUT"), window);
462 }
463
464 else
465 ob_debug_type(OB_DEBUG_FOCUS,
466 "UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
467 (e->type == FocusIn ? "IN" : "OUT"),
468 detail, mode, window);
469 }
470
471 event_set_curtime(e);
472 event_hack_mods(e);
473 if (event_ignore(e, client)) {
474 if (ed)
475 ed->ignored = TRUE;
476 return;
477 } else if (ed)
478 ed->ignored = FALSE;
479
480 /* deal with it in the kernel */
481
482 if (menu_frame_visible &&
483 (e->type == EnterNotify || e->type == LeaveNotify))
484 {
485 /* crossing events for menu */
486 event_handle_menu(e);
487 } else if (e->type == FocusIn) {
488 if (e->xfocus.detail == NotifyPointerRoot ||
489 e->xfocus.detail == NotifyDetailNone) {
490 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root\n");
491 /* Focus has been reverted to the root window or nothing, so fall
492 back to something other than the window which just had it. */
493 focus_fallback(FALSE);
494 } else if (e->xfocus.detail == NotifyInferior) {
495 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to parent\n");
496 /* Focus has been reverted to parent, which is our frame window,
497 or the root window, so fall back to something other than the
498 window which had it. */
499 focus_fallback(FALSE);
500 } else if (client && client != focus_client) {
501 focus_set_client(client);
502 frame_adjust_focus(client->frame, TRUE);
503 client_calc_layer(client);
504 }
505 } else if (e->type == FocusOut) {
506 gboolean nomove = FALSE;
507 XEvent ce;
508
509 ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n");
510
511 /* Look for the followup FocusIn */
512 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
513 /* There is no FocusIn, this means focus went to a window that
514 is not being managed, or a window on another screen. */
515 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to a black hole !\n");
516 } else if (ce.xany.window == e->xany.window) {
517 /* If focus didn't actually move anywhere, there is nothing to do*/
518 nomove = TRUE;
519 } else {
520 /* Focus did move, so process the FocusIn event */
521 ObEventData ed = { .ignored = FALSE };
522 event_process(&ce, &ed);
523 if (ed.ignored) {
524 /* The FocusIn was ignored, this means it was on a window
525 that isn't a client. */
526 ob_debug_type(OB_DEBUG_FOCUS,
527 "Focus went to an unmanaged window 0x%x !\n",
528 ce.xfocus.window);
529 focus_fallback(TRUE);
530 }
531 }
532
533 if (client && !nomove) {
534 /* This client is no longer focused, so show that */
535 focus_hilite = NULL;
536 frame_adjust_focus(client->frame, FALSE);
537 client_calc_layer(client);
538 }
539 } else if (group)
540 event_handle_group(group, e);
541 else if (client)
542 event_handle_client(client, e);
543 else if (dockapp)
544 event_handle_dockapp(dockapp, e);
545 else if (dock)
546 event_handle_dock(dock, e);
547 else if (window == RootWindow(ob_display, ob_screen))
548 event_handle_root(e);
549 else if (e->type == MapRequest)
550 client_manage(window);
551 else if (e->type == ConfigureRequest) {
552 /* unhandled configure requests must be used to configure the
553 window directly */
554 XWindowChanges xwc;
555
556 xwc.x = e->xconfigurerequest.x;
557 xwc.y = e->xconfigurerequest.y;
558 xwc.width = e->xconfigurerequest.width;
559 xwc.height = e->xconfigurerequest.height;
560 xwc.border_width = e->xconfigurerequest.border_width;
561 xwc.sibling = e->xconfigurerequest.above;
562 xwc.stack_mode = e->xconfigurerequest.detail;
563
564 /* we are not to be held responsible if someone sends us an
565 invalid request! */
566 xerror_set_ignore(TRUE);
567 XConfigureWindow(ob_display, window,
568 e->xconfigurerequest.value_mask, &xwc);
569 xerror_set_ignore(FALSE);
570 }
571
572 /* user input (action-bound) events */
573 if (e->type == ButtonPress || e->type == ButtonRelease ||
574 e->type == MotionNotify || e->type == KeyPress ||
575 e->type == KeyRelease)
576 {
577 if (menu_frame_visible)
578 event_handle_menu(e);
579 else {
580 if (!keyboard_process_interactive_grab(e, &client)) {
581 if (moveresize_in_progress) {
582 moveresize_event(e);
583
584 /* make further actions work on the client being
585 moved/resized */
586 client = moveresize_client;
587 }
588
589 menu_can_hide = FALSE;
590 ob_main_loop_timeout_add(ob_main_loop,
591 config_menu_hide_delay * 1000,
592 menu_hide_delay_func,
593 NULL, g_direct_equal, NULL);
594
595 if (e->type == ButtonPress || e->type == ButtonRelease ||
596 e->type == MotionNotify)
597 mouse_event(client, e);
598 else if (e->type == KeyPress) {
599 keyboard_event((focus_cycle_target ? focus_cycle_target :
600 (focus_hilite ? focus_hilite : client)),
601 e);
602 }
603 }
604 }
605 }
606 /* if something happens and it's not from an XEvent, then we don't know
607 the time */
608 event_curtime = CurrentTime;
609 }
610
611 static void event_handle_root(XEvent *e)
612 {
613 Atom msgtype;
614
615 switch(e->type) {
616 case SelectionClear:
617 ob_debug("Another WM has requested to replace us. Exiting.\n");
618 ob_exit_replace();
619 break;
620
621 case ClientMessage:
622 if (e->xclient.format != 32) break;
623
624 msgtype = e->xclient.message_type;
625 if (msgtype == prop_atoms.net_current_desktop) {
626 guint d = e->xclient.data.l[0];
627 if (d < screen_num_desktops) {
628 event_curtime = e->xclient.data.l[1];
629 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
630 screen_set_desktop(d);
631 }
632 } else if (msgtype == prop_atoms.net_number_of_desktops) {
633 guint d = e->xclient.data.l[0];
634 if (d > 0)
635 screen_set_num_desktops(d);
636 } else if (msgtype == prop_atoms.net_showing_desktop) {
637 screen_show_desktop(e->xclient.data.l[0] != 0);
638 } else if (msgtype == prop_atoms.ob_control) {
639 if (e->xclient.data.l[0] == 1)
640 ob_reconfigure();
641 else if (e->xclient.data.l[0] == 2)
642 ob_restart();
643 }
644 break;
645 case PropertyNotify:
646 if (e->xproperty.atom == prop_atoms.net_desktop_names)
647 screen_update_desktop_names();
648 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
649 screen_update_layout();
650 break;
651 case ConfigureNotify:
652 #ifdef XRANDR
653 XRRUpdateConfiguration(e);
654 #endif
655 screen_resize();
656 break;
657 default:
658 ;
659 }
660 }
661
662 static void event_handle_group(ObGroup *group, XEvent *e)
663 {
664 GSList *it;
665
666 g_assert(e->type == PropertyNotify);
667
668 for (it = group->members; it; it = g_slist_next(it))
669 event_handle_client(it->data, e);
670 }
671
672 void event_enter_client(ObClient *client)
673 {
674 g_assert(config_focus_follow);
675
676 if (client_normal(client) && client_can_focus(client)) {
677 if (config_focus_delay) {
678 ObFocusDelayData *data;
679
680 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
681
682 data = g_new(ObFocusDelayData, 1);
683 data->client = client;
684 data->time = event_curtime;
685
686 ob_main_loop_timeout_add(ob_main_loop,
687 config_focus_delay,
688 focus_delay_func,
689 data, focus_delay_cmp, focus_delay_dest);
690 } else {
691 ObFocusDelayData data;
692 data.client = client;
693 data.time = event_curtime;
694 focus_delay_func(&data);
695 }
696 }
697 }
698
699 static void event_handle_client(ObClient *client, XEvent *e)
700 {
701 XEvent ce;
702 Atom msgtype;
703 gint i=0;
704 ObFrameContext con;
705
706 switch (e->type) {
707 case VisibilityNotify:
708 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
709 break;
710 case ButtonPress:
711 case ButtonRelease:
712 /* Wheel buttons don't draw because they are an instant click, so it
713 is a waste of resources to go drawing it. */
714 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
715 con = frame_context(client, e->xbutton.window);
716 con = mouse_button_frame_context(con, e->xbutton.button);
717 switch (con) {
718 case OB_FRAME_CONTEXT_MAXIMIZE:
719 client->frame->max_press = (e->type == ButtonPress);
720 framerender_frame(client->frame);
721 break;
722 case OB_FRAME_CONTEXT_CLOSE:
723 client->frame->close_press = (e->type == ButtonPress);
724 framerender_frame(client->frame);
725 break;
726 case OB_FRAME_CONTEXT_ICONIFY:
727 client->frame->iconify_press = (e->type == ButtonPress);
728 framerender_frame(client->frame);
729 break;
730 case OB_FRAME_CONTEXT_ALLDESKTOPS:
731 client->frame->desk_press = (e->type == ButtonPress);
732 framerender_frame(client->frame);
733 break;
734 case OB_FRAME_CONTEXT_SHADE:
735 client->frame->shade_press = (e->type == ButtonPress);
736 framerender_frame(client->frame);
737 break;
738 default:
739 /* nothing changes with clicks for any other contexts */
740 break;
741 }
742 }
743 break;
744 case LeaveNotify:
745 con = frame_context(client, e->xcrossing.window);
746 switch (con) {
747 case OB_FRAME_CONTEXT_MAXIMIZE:
748 client->frame->max_hover = FALSE;
749 frame_adjust_state(client->frame);
750 break;
751 case OB_FRAME_CONTEXT_ALLDESKTOPS:
752 client->frame->desk_hover = FALSE;
753 frame_adjust_state(client->frame);
754 break;
755 case OB_FRAME_CONTEXT_SHADE:
756 client->frame->shade_hover = FALSE;
757 frame_adjust_state(client->frame);
758 break;
759 case OB_FRAME_CONTEXT_ICONIFY:
760 client->frame->iconify_hover = FALSE;
761 frame_adjust_state(client->frame);
762 break;
763 case OB_FRAME_CONTEXT_CLOSE:
764 client->frame->close_hover = FALSE;
765 frame_adjust_state(client->frame);
766 break;
767 case OB_FRAME_CONTEXT_FRAME:
768 if (config_focus_follow && config_focus_delay)
769 ob_main_loop_timeout_remove_data(ob_main_loop,
770 focus_delay_func,
771 client, FALSE);
772 break;
773 default:
774 break;
775 }
776 break;
777 case EnterNotify:
778 {
779 gboolean nofocus = FALSE;
780
781 if (ignore_enter_focus) {
782 ignore_enter_focus--;
783 nofocus = TRUE;
784 }
785
786 con = frame_context(client, e->xcrossing.window);
787 switch (con) {
788 case OB_FRAME_CONTEXT_MAXIMIZE:
789 client->frame->max_hover = TRUE;
790 frame_adjust_state(client->frame);
791 break;
792 case OB_FRAME_CONTEXT_ALLDESKTOPS:
793 client->frame->desk_hover = TRUE;
794 frame_adjust_state(client->frame);
795 break;
796 case OB_FRAME_CONTEXT_SHADE:
797 client->frame->shade_hover = TRUE;
798 frame_adjust_state(client->frame);
799 break;
800 case OB_FRAME_CONTEXT_ICONIFY:
801 client->frame->iconify_hover = TRUE;
802 frame_adjust_state(client->frame);
803 break;
804 case OB_FRAME_CONTEXT_CLOSE:
805 client->frame->close_hover = TRUE;
806 frame_adjust_state(client->frame);
807 break;
808 case OB_FRAME_CONTEXT_FRAME:
809 if (e->xcrossing.mode == NotifyGrab ||
810 e->xcrossing.mode == NotifyUngrab)
811 {
812 ob_debug_type(OB_DEBUG_FOCUS,
813 "%sNotify mode %d detail %d on %lx IGNORED\n",
814 (e->type == EnterNotify ? "Enter" : "Leave"),
815 e->xcrossing.mode,
816 e->xcrossing.detail, client?client->window:0);
817 } else {
818 ob_debug_type(OB_DEBUG_FOCUS,
819 "%sNotify mode %d detail %d on %lx, "
820 "focusing window: %d\n",
821 (e->type == EnterNotify ? "Enter" : "Leave"),
822 e->xcrossing.mode,
823 e->xcrossing.detail, (client?client->window:0),
824 !nofocus);
825 if (!nofocus && config_focus_follow)
826 event_enter_client(client);
827 }
828 break;
829 default:
830 break;
831 }
832 break;
833 }
834 case ConfigureRequest:
835 /* compress these */
836 while (XCheckTypedWindowEvent(ob_display, client->window,
837 ConfigureRequest, &ce)) {
838 ++i;
839 /* XXX if this causes bad things.. we can compress config req's
840 with the same mask. */
841 e->xconfigurerequest.value_mask |=
842 ce.xconfigurerequest.value_mask;
843 if (ce.xconfigurerequest.value_mask & CWX)
844 e->xconfigurerequest.x = ce.xconfigurerequest.x;
845 if (ce.xconfigurerequest.value_mask & CWY)
846 e->xconfigurerequest.y = ce.xconfigurerequest.y;
847 if (ce.xconfigurerequest.value_mask & CWWidth)
848 e->xconfigurerequest.width = ce.xconfigurerequest.width;
849 if (ce.xconfigurerequest.value_mask & CWHeight)
850 e->xconfigurerequest.height = ce.xconfigurerequest.height;
851 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
852 e->xconfigurerequest.border_width =
853 ce.xconfigurerequest.border_width;
854 if (ce.xconfigurerequest.value_mask & CWStackMode)
855 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
856 }
857
858 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
859 if (client->iconic || client->shaded) return;
860
861 /* resize, then move, as specified in the EWMH section 7.7 */
862 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
863 CWX | CWY |
864 CWBorderWidth)) {
865 gint x, y, w, h;
866 ObCorner corner;
867
868 if (e->xconfigurerequest.value_mask & CWBorderWidth)
869 client->border_width = e->xconfigurerequest.border_width;
870
871 x = (e->xconfigurerequest.value_mask & CWX) ?
872 e->xconfigurerequest.x : client->area.x;
873 y = (e->xconfigurerequest.value_mask & CWY) ?
874 e->xconfigurerequest.y : client->area.y;
875 w = (e->xconfigurerequest.value_mask & CWWidth) ?
876 e->xconfigurerequest.width : client->area.width;
877 h = (e->xconfigurerequest.value_mask & CWHeight) ?
878 e->xconfigurerequest.height : client->area.height;
879
880 {
881 gint newx = x;
882 gint newy = y;
883 gint fw = w +
884 client->frame->size.left + client->frame->size.right;
885 gint fh = h +
886 client->frame->size.top + client->frame->size.bottom;
887 /* make this rude for size-only changes but not for position
888 changes.. */
889 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
890 (e->xconfigurerequest.value_mask & CWY));
891
892 client_find_onscreen(client, &newx, &newy, fw, fh,
893 !moving);
894 if (e->xconfigurerequest.value_mask & CWX)
895 x = newx;
896 if (e->xconfigurerequest.value_mask & CWY)
897 y = newy;
898 }
899
900 switch (client->gravity) {
901 case NorthEastGravity:
902 case EastGravity:
903 corner = OB_CORNER_TOPRIGHT;
904 break;
905 case SouthWestGravity:
906 case SouthGravity:
907 corner = OB_CORNER_BOTTOMLEFT;
908 break;
909 case SouthEastGravity:
910 corner = OB_CORNER_BOTTOMRIGHT;
911 break;
912 default: /* NorthWest, Static, etc */
913 corner = OB_CORNER_TOPLEFT;
914 }
915
916 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
917 TRUE);
918 }
919
920 if (e->xconfigurerequest.value_mask & CWStackMode) {
921 switch (e->xconfigurerequest.detail) {
922 case Below:
923 case BottomIf:
924 /* Apps are so rude. And this is totally disconnected from
925 activation/focus. Bleh. */
926 /*client_lower(client);*/
927 break;
928
929 case Above:
930 case TopIf:
931 default:
932 /* Apps are so rude. And this is totally disconnected from
933 activation/focus. Bleh. */
934 /*client_raise(client);*/
935 break;
936 }
937 }
938 break;
939 case UnmapNotify:
940 if (client->ignore_unmaps) {
941 client->ignore_unmaps--;
942 break;
943 }
944 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
945 "ignores left %d\n",
946 client->window, e->xunmap.event, e->xunmap.from_configure,
947 client->ignore_unmaps);
948 client_unmanage(client);
949 break;
950 case DestroyNotify:
951 ob_debug("DestroyNotify for window 0x%x\n", client->window);
952 client_unmanage(client);
953 break;
954 case ReparentNotify:
955 /* this is when the client is first taken captive in the frame */
956 if (e->xreparent.parent == client->frame->plate) break;
957
958 /*
959 This event is quite rare and is usually handled in unmapHandler.
960 However, if the window is unmapped when the reparent event occurs,
961 the window manager never sees it because an unmap event is not sent
962 to an already unmapped window.
963 */
964
965 /* we don't want the reparent event, put it back on the stack for the
966 X server to deal with after we unmanage the window */
967 XPutBackEvent(ob_display, e);
968
969 ob_debug("ReparentNotify for window 0x%x\n", client->window);
970 client_unmanage(client);
971 break;
972 case MapRequest:
973 ob_debug("MapRequest for 0x%lx\n", client->window);
974 if (!client->iconic) break; /* this normally doesn't happen, but if it
975 does, we don't want it!
976 it can happen now when the window is on
977 another desktop, but we still don't
978 want it! */
979 client_activate(client, FALSE, TRUE);
980 break;
981 case ClientMessage:
982 /* validate cuz we query stuff off the client here */
983 if (!client_validate(client)) break;
984
985 if (e->xclient.format != 32) return;
986
987 msgtype = e->xclient.message_type;
988 if (msgtype == prop_atoms.wm_change_state) {
989 /* compress changes into a single change */
990 while (XCheckTypedWindowEvent(ob_display, client->window,
991 e->type, &ce)) {
992 /* XXX: it would be nice to compress ALL messages of a
993 type, not just messages in a row without other
994 message types between. */
995 if (ce.xclient.message_type != msgtype) {
996 XPutBackEvent(ob_display, &ce);
997 break;
998 }
999 e->xclient = ce.xclient;
1000 }
1001 client_set_wm_state(client, e->xclient.data.l[0]);
1002 } else if (msgtype == prop_atoms.net_wm_desktop) {
1003 /* compress changes into a single change */
1004 while (XCheckTypedWindowEvent(ob_display, client->window,
1005 e->type, &ce)) {
1006 /* XXX: it would be nice to compress ALL messages of a
1007 type, not just messages in a row without other
1008 message types between. */
1009 if (ce.xclient.message_type != msgtype) {
1010 XPutBackEvent(ob_display, &ce);
1011 break;
1012 }
1013 e->xclient = ce.xclient;
1014 }
1015 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1016 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1017 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1018 FALSE);
1019 } else if (msgtype == prop_atoms.net_wm_state) {
1020 /* can't compress these */
1021 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1022 (e->xclient.data.l[0] == 0 ? "Remove" :
1023 e->xclient.data.l[0] == 1 ? "Add" :
1024 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1025 e->xclient.data.l[1], e->xclient.data.l[2],
1026 client->window);
1027 client_set_state(client, e->xclient.data.l[0],
1028 e->xclient.data.l[1], e->xclient.data.l[2]);
1029 } else if (msgtype == prop_atoms.net_close_window) {
1030 ob_debug("net_close_window for 0x%lx\n", client->window);
1031 client_close(client);
1032 } else if (msgtype == prop_atoms.net_active_window) {
1033 ob_debug("net_active_window for 0x%lx source=%s\n",
1034 client->window,
1035 (e->xclient.data.l[0] == 0 ? "unknown" :
1036 (e->xclient.data.l[0] == 1 ? "application" :
1037 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1038 /* XXX make use of data.l[2] ! */
1039 event_curtime = e->xclient.data.l[1];
1040 client_activate(client, FALSE,
1041 (e->xclient.data.l[0] == 0 ||
1042 e->xclient.data.l[0] == 2));
1043 } else if (msgtype == prop_atoms.net_wm_moveresize) {
1044 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1045 client->window, e->xclient.data.l[2]);
1046 if ((Atom)e->xclient.data.l[2] ==
1047 prop_atoms.net_wm_moveresize_size_topleft ||
1048 (Atom)e->xclient.data.l[2] ==
1049 prop_atoms.net_wm_moveresize_size_top ||
1050 (Atom)e->xclient.data.l[2] ==
1051 prop_atoms.net_wm_moveresize_size_topright ||
1052 (Atom)e->xclient.data.l[2] ==
1053 prop_atoms.net_wm_moveresize_size_right ||
1054 (Atom)e->xclient.data.l[2] ==
1055 prop_atoms.net_wm_moveresize_size_right ||
1056 (Atom)e->xclient.data.l[2] ==
1057 prop_atoms.net_wm_moveresize_size_bottomright ||
1058 (Atom)e->xclient.data.l[2] ==
1059 prop_atoms.net_wm_moveresize_size_bottom ||
1060 (Atom)e->xclient.data.l[2] ==
1061 prop_atoms.net_wm_moveresize_size_bottomleft ||
1062 (Atom)e->xclient.data.l[2] ==
1063 prop_atoms.net_wm_moveresize_size_left ||
1064 (Atom)e->xclient.data.l[2] ==
1065 prop_atoms.net_wm_moveresize_move ||
1066 (Atom)e->xclient.data.l[2] ==
1067 prop_atoms.net_wm_moveresize_size_keyboard ||
1068 (Atom)e->xclient.data.l[2] ==
1069 prop_atoms.net_wm_moveresize_move_keyboard) {
1070
1071 moveresize_start(client, e->xclient.data.l[0],
1072 e->xclient.data.l[1], e->xclient.data.l[3],
1073 e->xclient.data.l[2]);
1074 }
1075 else if ((Atom)e->xclient.data.l[2] ==
1076 prop_atoms.net_wm_moveresize_cancel)
1077 moveresize_end(TRUE);
1078 } else if (msgtype == prop_atoms.net_moveresize_window) {
1079 gint oldg = client->gravity;
1080 gint tmpg, x, y, w, h;
1081
1082 if (e->xclient.data.l[0] & 0xff)
1083 tmpg = e->xclient.data.l[0] & 0xff;
1084 else
1085 tmpg = oldg;
1086
1087 if (e->xclient.data.l[0] & 1 << 8)
1088 x = e->xclient.data.l[1];
1089 else
1090 x = client->area.x;
1091 if (e->xclient.data.l[0] & 1 << 9)
1092 y = e->xclient.data.l[2];
1093 else
1094 y = client->area.y;
1095 if (e->xclient.data.l[0] & 1 << 10)
1096 w = e->xclient.data.l[3];
1097 else
1098 w = client->area.width;
1099 if (e->xclient.data.l[0] & 1 << 11)
1100 h = e->xclient.data.l[4];
1101 else
1102 h = client->area.height;
1103 client->gravity = tmpg;
1104
1105 {
1106 gint newx = x;
1107 gint newy = y;
1108 gint fw = w +
1109 client->frame->size.left + client->frame->size.right;
1110 gint fh = h +
1111 client->frame->size.top + client->frame->size.bottom;
1112 client_find_onscreen(client, &newx, &newy, fw, fh,
1113 client_normal(client));
1114 if (e->xclient.data.l[0] & 1 << 8)
1115 x = newx;
1116 if (e->xclient.data.l[0] & 1 << 9)
1117 y = newy;
1118 }
1119
1120 client_configure(client, OB_CORNER_TOPLEFT,
1121 x, y, w, h, FALSE, TRUE);
1122
1123 client->gravity = oldg;
1124 }
1125 break;
1126 case PropertyNotify:
1127 /* validate cuz we query stuff off the client here */
1128 if (!client_validate(client)) break;
1129
1130 /* compress changes to a single property into a single change */
1131 while (XCheckTypedWindowEvent(ob_display, client->window,
1132 e->type, &ce)) {
1133 Atom a, b;
1134
1135 /* XXX: it would be nice to compress ALL changes to a property,
1136 not just changes in a row without other props between. */
1137
1138 a = ce.xproperty.atom;
1139 b = e->xproperty.atom;
1140
1141 if (a == b)
1142 continue;
1143 if ((a == prop_atoms.net_wm_name ||
1144 a == prop_atoms.wm_name ||
1145 a == prop_atoms.net_wm_icon_name ||
1146 a == prop_atoms.wm_icon_name)
1147 &&
1148 (b == prop_atoms.net_wm_name ||
1149 b == prop_atoms.wm_name ||
1150 b == prop_atoms.net_wm_icon_name ||
1151 b == prop_atoms.wm_icon_name)) {
1152 continue;
1153 }
1154 if (a == prop_atoms.net_wm_icon &&
1155 b == prop_atoms.net_wm_icon)
1156 continue;
1157
1158 XPutBackEvent(ob_display, &ce);
1159 break;
1160 }
1161
1162 msgtype = e->xproperty.atom;
1163 if (msgtype == XA_WM_NORMAL_HINTS) {
1164 client_update_normal_hints(client);
1165 /* normal hints can make a window non-resizable */
1166 client_setup_decor_and_functions(client);
1167 } else if (msgtype == XA_WM_HINTS) {
1168 client_update_wmhints(client);
1169 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1170 client_update_transient_for(client);
1171 client_get_type(client);
1172 /* type may have changed, so update the layer */
1173 client_calc_layer(client);
1174 client_setup_decor_and_functions(client);
1175 } else if (msgtype == prop_atoms.net_wm_name ||
1176 msgtype == prop_atoms.wm_name ||
1177 msgtype == prop_atoms.net_wm_icon_name ||
1178 msgtype == prop_atoms.wm_icon_name) {
1179 client_update_title(client);
1180 } else if (msgtype == prop_atoms.wm_class) {
1181 client_update_class(client);
1182 } else if (msgtype == prop_atoms.wm_protocols) {
1183 client_update_protocols(client);
1184 client_setup_decor_and_functions(client);
1185 }
1186 else if (msgtype == prop_atoms.net_wm_strut) {
1187 client_update_strut(client);
1188 }
1189 else if (msgtype == prop_atoms.net_wm_icon) {
1190 client_update_icons(client);
1191 }
1192 else if (msgtype == prop_atoms.net_wm_user_time) {
1193 client_update_user_time(client);
1194 }
1195 else if (msgtype == prop_atoms.sm_client_id) {
1196 client_update_sm_client_id(client);
1197 }
1198 default:
1199 ;
1200 #ifdef SHAPE
1201 if (extensions_shape && e->type == extensions_shape_event_basep) {
1202 client->shaped = ((XShapeEvent*)e)->shaped;
1203 frame_adjust_shape(client->frame);
1204 }
1205 #endif
1206 }
1207 }
1208
1209 static void event_handle_dock(ObDock *s, XEvent *e)
1210 {
1211 switch (e->type) {
1212 case ButtonPress:
1213 if (e->xbutton.button == 1)
1214 stacking_raise(DOCK_AS_WINDOW(s));
1215 else if (e->xbutton.button == 2)
1216 stacking_lower(DOCK_AS_WINDOW(s));
1217 break;
1218 case EnterNotify:
1219 dock_hide(FALSE);
1220 break;
1221 case LeaveNotify:
1222 dock_hide(TRUE);
1223 break;
1224 }
1225 }
1226
1227 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1228 {
1229 switch (e->type) {
1230 case MotionNotify:
1231 dock_app_drag(app, &e->xmotion);
1232 break;
1233 case UnmapNotify:
1234 if (app->ignore_unmaps) {
1235 app->ignore_unmaps--;
1236 break;
1237 }
1238 dock_remove(app, TRUE);
1239 break;
1240 case DestroyNotify:
1241 dock_remove(app, FALSE);
1242 break;
1243 case ReparentNotify:
1244 dock_remove(app, FALSE);
1245 break;
1246 case ConfigureNotify:
1247 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1248 break;
1249 }
1250 }
1251
1252 ObMenuFrame* find_active_menu()
1253 {
1254 GList *it;
1255 ObMenuFrame *ret = NULL;
1256
1257 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1258 ret = it->data;
1259 if (ret->selected)
1260 break;
1261 ret = NULL;
1262 }
1263 return ret;
1264 }
1265
1266 ObMenuFrame* find_active_or_last_menu()
1267 {
1268 ObMenuFrame *ret = NULL;
1269
1270 ret = find_active_menu();
1271 if (!ret && menu_frame_visible)
1272 ret = menu_frame_visible->data;
1273 return ret;
1274 }
1275
1276 static void event_handle_menu(XEvent *ev)
1277 {
1278 ObMenuFrame *f;
1279 ObMenuEntryFrame *e;
1280
1281 switch (ev->type) {
1282 case ButtonRelease:
1283 if (menu_can_hide) {
1284 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1285 ev->xbutton.y_root)))
1286 menu_entry_frame_execute(e, ev->xbutton.state,
1287 ev->xbutton.time);
1288 else
1289 menu_frame_hide_all();
1290 }
1291 break;
1292 case EnterNotify:
1293 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1294 if (e->ignore_enters)
1295 --e->ignore_enters;
1296 else
1297 menu_frame_select(e->frame, e);
1298 }
1299 break;
1300 case LeaveNotify:
1301 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1302 (f = find_active_menu()) && f->selected == e &&
1303 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1304 {
1305 menu_frame_select(e->frame, NULL);
1306 }
1307 case MotionNotify:
1308 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1309 ev->xmotion.y_root)))
1310 menu_frame_select(e->frame, e);
1311 break;
1312 case KeyPress:
1313 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1314 menu_frame_hide_all();
1315 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1316 ObMenuFrame *f;
1317 if ((f = find_active_menu()))
1318 menu_entry_frame_execute(f->selected, ev->xkey.state,
1319 ev->xkey.time);
1320 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1321 ObMenuFrame *f;
1322 if ((f = find_active_or_last_menu()) && f->parent)
1323 menu_frame_select(f, NULL);
1324 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1325 ObMenuFrame *f;
1326 if ((f = find_active_or_last_menu()) && f->child)
1327 menu_frame_select_next(f->child);
1328 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1329 ObMenuFrame *f;
1330 if ((f = find_active_or_last_menu()))
1331 menu_frame_select_previous(f);
1332 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1333 ObMenuFrame *f;
1334 if ((f = find_active_or_last_menu()))
1335 menu_frame_select_next(f);
1336 }
1337 break;
1338 }
1339 }
1340
1341 static gboolean menu_hide_delay_func(gpointer data)
1342 {
1343 menu_can_hide = TRUE;
1344 return FALSE; /* no repeat */
1345 }
1346
1347 static void focus_delay_dest(gpointer data)
1348 {
1349 g_free(data);
1350 }
1351
1352 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1353 {
1354 const ObFocusDelayData *f1 = d1, *f2 = d2;
1355 return f1->client == f2->client;
1356 }
1357
1358 static gboolean focus_delay_func(gpointer data)
1359 {
1360 ObFocusDelayData *d = data;
1361 Time old = event_curtime;
1362
1363 event_curtime = d->time;
1364 if (focus_client != d->client) {
1365 if (client_focus(d->client) && config_focus_raise)
1366 client_raise(d->client);
1367 }
1368 event_curtime = old;
1369 return FALSE; /* no repeat */
1370 }
1371
1372 static void focus_delay_client_dest(ObClient *client, gpointer data)
1373 {
1374 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1375 client, FALSE);
1376 }
1377
1378 static void event_client_dest(ObClient *client, gpointer data)
1379 {
1380 if (client == focus_hilite)
1381 focus_hilite = NULL;
1382 }
1383
1384 void event_halt_focus_delay()
1385 {
1386 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1387 }
1388
1389 void event_ignore_queued_enters()
1390 {
1391 GSList *saved = NULL, *it;
1392 XEvent *e;
1393
1394 XSync(ob_display, FALSE);
1395
1396 /* count the events */
1397 while (TRUE) {
1398 e = g_new(XEvent, 1);
1399 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1400 ObWindow *win;
1401
1402 win = g_hash_table_lookup(window_map, &e->xany.window);
1403 if (win && WINDOW_IS_CLIENT(win))
1404 ++ignore_enter_focus;
1405
1406 saved = g_slist_append(saved, e);
1407 } else {
1408 g_free(e);
1409 break;
1410 }
1411 }
1412 /* put the events back */
1413 for (it = saved; it; it = g_slist_next(it)) {
1414 XPutBackEvent(ob_display, it->data);
1415 g_free(it->data);
1416 }
1417 g_slist_free(saved);
1418 }
1419
1420 gboolean event_time_after(Time t1, Time t2)
1421 {
1422 g_assert(t1 != CurrentTime);
1423 g_assert(t2 != CurrentTime);
1424
1425 /*
1426 Timestamp values wrap around (after about 49.7 days). The server, given
1427 its current time is represented by timestamp T, always interprets
1428 timestamps from clients by treating half of the timestamp space as being
1429 later in time than T.
1430 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1431 */
1432
1433 /* TIME_HALF is half of the number space of a Time type variable */
1434 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1435
1436 if (t2 >= TIME_HALF)
1437 /* t2 is in the second half so t1 might wrap around and be smaller than
1438 t2 */
1439 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1440 else
1441 /* t2 is in the first half so t1 has to come after it */
1442 return t1 >= t2 && t1 < (t2 + TIME_HALF);
1443 }
This page took 0.103026 seconds and 4 git commands to generate.