]> Dogcows Code - chaz/openbox/blob - openbox/event.c
only pass thru events when the menu is open, don't for other stuff
[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 FocusIn:
397 case FocusOut:
398 if (!wanted_focusevent(e)) {
399 ob_debug_type(OB_DEBUG_FOCUS, "focus event ignored\n");
400 return TRUE;
401 }
402 ob_debug_type(OB_DEBUG_FOCUS, "focus event used;\n");
403 break;
404 }
405 return FALSE;
406 }
407
408 static void event_process(const XEvent *ec, gpointer data)
409 {
410 Window window;
411 ObGroup *group = NULL;
412 ObClient *client = NULL;
413 ObDock *dock = NULL;
414 ObDockApp *dockapp = NULL;
415 ObWindow *obwin = NULL;
416 XEvent ee, *e;
417 ObEventData *ed = data;
418
419 /* make a copy we can mangle */
420 ee = *ec;
421 e = &ee;
422
423 window = event_get_window(e);
424 if (!(e->type == PropertyNotify &&
425 (group = g_hash_table_lookup(group_map, &window))))
426 if ((obwin = g_hash_table_lookup(window_map, &window))) {
427 switch (obwin->type) {
428 case Window_Dock:
429 dock = WINDOW_AS_DOCK(obwin);
430 break;
431 case Window_DockApp:
432 dockapp = WINDOW_AS_DOCKAPP(obwin);
433 break;
434 case Window_Client:
435 client = WINDOW_AS_CLIENT(obwin);
436 break;
437 case Window_Menu:
438 case Window_Internal:
439 /* not to be used for events */
440 g_assert_not_reached();
441 break;
442 }
443 }
444
445 if (e->type == FocusIn || e->type == FocusOut) {
446 gint mode = e->xfocus.mode;
447 gint detail = e->xfocus.detail;
448 Window window = e->xfocus.window;
449 if (detail == NotifyVirtual) {
450 ob_debug_type(OB_DEBUG_FOCUS,
451 "FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
452 (e->type == FocusIn ? "IN" : "OUT"), window);
453 }
454
455 else if (detail == NotifyNonlinearVirtual) {
456 ob_debug_type(OB_DEBUG_FOCUS,
457 "FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
458 (e->type == FocusIn ? "IN" : "OUT"), window);
459 }
460
461 else
462 ob_debug_type(OB_DEBUG_FOCUS,
463 "UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
464 (e->type == FocusIn ? "IN" : "OUT"),
465 detail, mode, window);
466 }
467
468 event_set_curtime(e);
469 event_hack_mods(e);
470 if (event_ignore(e, client)) {
471 if (ed)
472 ed->ignored = TRUE;
473 return;
474 } else if (ed)
475 ed->ignored = FALSE;
476
477 /* deal with it in the kernel */
478
479 if (menu_frame_visible &&
480 (e->type == EnterNotify || e->type == LeaveNotify))
481 {
482 /* crossing events for menu */
483 event_handle_menu(e);
484 } else if (e->type == FocusIn) {
485 if (e->xfocus.detail == NotifyPointerRoot ||
486 e->xfocus.detail == NotifyDetailNone) {
487 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root\n");
488 /* Focus has been reverted to the root window or nothing, so fall
489 back to something other than the window which just had it. */
490 focus_fallback(FALSE);
491 } else if (e->xfocus.detail == NotifyInferior) {
492 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to parent\n");
493 /* Focus has been reverted to parent, which is our frame window,
494 or the root window, so fall back to something other than the
495 window which had it. */
496 focus_fallback(FALSE);
497 } else if (client && client != focus_client) {
498 focus_set_client(client);
499 frame_adjust_focus(client->frame, TRUE);
500 client_calc_layer(client);
501 }
502 } else if (e->type == FocusOut) {
503 gboolean nomove = FALSE;
504 XEvent ce;
505
506 ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n");
507
508 /* Look for the followup FocusIn */
509 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
510 /* There is no FocusIn, this means focus went to a window that
511 is not being managed, or a window on another screen. */
512 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to a black hole !\n");
513 } else if (ce.xany.window == e->xany.window) {
514 /* If focus didn't actually move anywhere, there is nothing to do*/
515 nomove = TRUE;
516 } else {
517 /* Focus did move, so process the FocusIn event */
518 ObEventData ed = { .ignored = FALSE };
519 event_process(&ce, &ed);
520 if (ed.ignored) {
521 /* The FocusIn was ignored, this means it was on a window
522 that isn't a client. */
523 ob_debug_type(OB_DEBUG_FOCUS,
524 "Focus went to an unmanaged window 0x%x !\n",
525 ce.xfocus.window);
526 focus_fallback(TRUE);
527 }
528 }
529
530 if (client && !nomove) {
531 /* This client is no longer focused, so show that */
532 focus_hilite = NULL;
533 frame_adjust_focus(client->frame, FALSE);
534 client_calc_layer(client);
535 }
536 } else if (group)
537 event_handle_group(group, e);
538 else if (client)
539 event_handle_client(client, e);
540 else if (dockapp)
541 event_handle_dockapp(dockapp, e);
542 else if (dock)
543 event_handle_dock(dock, e);
544 else if (window == RootWindow(ob_display, ob_screen))
545 event_handle_root(e);
546 else if (e->type == MapRequest)
547 client_manage(window);
548 else if (e->type == ConfigureRequest) {
549 /* unhandled configure requests must be used to configure the
550 window directly */
551 XWindowChanges xwc;
552
553 xwc.x = e->xconfigurerequest.x;
554 xwc.y = e->xconfigurerequest.y;
555 xwc.width = e->xconfigurerequest.width;
556 xwc.height = e->xconfigurerequest.height;
557 xwc.border_width = e->xconfigurerequest.border_width;
558 xwc.sibling = e->xconfigurerequest.above;
559 xwc.stack_mode = e->xconfigurerequest.detail;
560
561 /* we are not to be held responsible if someone sends us an
562 invalid request! */
563 xerror_set_ignore(TRUE);
564 XConfigureWindow(ob_display, window,
565 e->xconfigurerequest.value_mask, &xwc);
566 xerror_set_ignore(FALSE);
567 }
568
569 /* user input (action-bound) events */
570 if (e->type == ButtonPress || e->type == ButtonRelease ||
571 e->type == MotionNotify || e->type == KeyPress ||
572 e->type == KeyRelease)
573 {
574 if (menu_frame_visible)
575 event_handle_menu(e);
576 else {
577 if (!keyboard_process_interactive_grab(e, &client)) {
578 if (moveresize_in_progress) {
579 moveresize_event(e);
580
581 /* make further actions work on the client being
582 moved/resized */
583 client = moveresize_client;
584 }
585
586 menu_can_hide = FALSE;
587 ob_main_loop_timeout_add(ob_main_loop,
588 config_menu_hide_delay * 1000,
589 menu_hide_delay_func,
590 NULL, g_direct_equal, NULL);
591
592 if (e->type == ButtonPress || e->type == ButtonRelease ||
593 e->type == MotionNotify)
594 mouse_event(client, e);
595 else if (e->type == KeyPress) {
596 keyboard_event((focus_cycle_target ? focus_cycle_target :
597 (focus_hilite ? focus_hilite : client)),
598 e);
599 }
600 }
601 }
602 }
603 /* if something happens and it's not from an XEvent, then we don't know
604 the time */
605 event_curtime = CurrentTime;
606 }
607
608 static void event_handle_root(XEvent *e)
609 {
610 Atom msgtype;
611
612 switch(e->type) {
613 case SelectionClear:
614 ob_debug("Another WM has requested to replace us. Exiting.\n");
615 ob_exit_replace();
616 break;
617
618 case ClientMessage:
619 if (e->xclient.format != 32) break;
620
621 msgtype = e->xclient.message_type;
622 if (msgtype == prop_atoms.net_current_desktop) {
623 guint d = e->xclient.data.l[0];
624 if (d < screen_num_desktops) {
625 event_curtime = e->xclient.data.l[1];
626 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
627 screen_set_desktop(d);
628 }
629 } else if (msgtype == prop_atoms.net_number_of_desktops) {
630 guint d = e->xclient.data.l[0];
631 if (d > 0)
632 screen_set_num_desktops(d);
633 } else if (msgtype == prop_atoms.net_showing_desktop) {
634 screen_show_desktop(e->xclient.data.l[0] != 0);
635 } else if (msgtype == prop_atoms.ob_control) {
636 if (e->xclient.data.l[0] == 1)
637 ob_reconfigure();
638 else if (e->xclient.data.l[0] == 2)
639 ob_restart();
640 }
641 break;
642 case PropertyNotify:
643 if (e->xproperty.atom == prop_atoms.net_desktop_names)
644 screen_update_desktop_names();
645 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
646 screen_update_layout();
647 break;
648 case ConfigureNotify:
649 #ifdef XRANDR
650 XRRUpdateConfiguration(e);
651 #endif
652 screen_resize();
653 break;
654 default:
655 ;
656 }
657 }
658
659 static void event_handle_group(ObGroup *group, XEvent *e)
660 {
661 GSList *it;
662
663 g_assert(e->type == PropertyNotify);
664
665 for (it = group->members; it; it = g_slist_next(it))
666 event_handle_client(it->data, e);
667 }
668
669 void event_enter_client(ObClient *client)
670 {
671 g_assert(config_focus_follow);
672
673 if (client_normal(client) && client_can_focus(client)) {
674 if (config_focus_delay) {
675 ObFocusDelayData *data;
676
677 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
678
679 data = g_new(ObFocusDelayData, 1);
680 data->client = client;
681 data->time = event_curtime;
682
683 ob_main_loop_timeout_add(ob_main_loop,
684 config_focus_delay,
685 focus_delay_func,
686 data, focus_delay_cmp, focus_delay_dest);
687 } else {
688 ObFocusDelayData data;
689 data.client = client;
690 data.time = event_curtime;
691 focus_delay_func(&data);
692 }
693 }
694 }
695
696 static void event_handle_client(ObClient *client, XEvent *e)
697 {
698 XEvent ce;
699 Atom msgtype;
700 gint i=0;
701 ObFrameContext con;
702
703 switch (e->type) {
704 case VisibilityNotify:
705 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
706 break;
707 case ButtonPress:
708 case ButtonRelease:
709 /* Wheel buttons don't draw because they are an instant click, so it
710 is a waste of resources to go drawing it. */
711 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
712 con = frame_context(client, e->xbutton.window);
713 con = mouse_button_frame_context(con, e->xbutton.button);
714 switch (con) {
715 case OB_FRAME_CONTEXT_MAXIMIZE:
716 client->frame->max_press = (e->type == ButtonPress);
717 framerender_frame(client->frame);
718 break;
719 case OB_FRAME_CONTEXT_CLOSE:
720 client->frame->close_press = (e->type == ButtonPress);
721 framerender_frame(client->frame);
722 break;
723 case OB_FRAME_CONTEXT_ICONIFY:
724 client->frame->iconify_press = (e->type == ButtonPress);
725 framerender_frame(client->frame);
726 break;
727 case OB_FRAME_CONTEXT_ALLDESKTOPS:
728 client->frame->desk_press = (e->type == ButtonPress);
729 framerender_frame(client->frame);
730 break;
731 case OB_FRAME_CONTEXT_SHADE:
732 client->frame->shade_press = (e->type == ButtonPress);
733 framerender_frame(client->frame);
734 break;
735 default:
736 /* nothing changes with clicks for any other contexts */
737 break;
738 }
739 }
740 break;
741 case LeaveNotify:
742 con = frame_context(client, e->xcrossing.window);
743 switch (con) {
744 case OB_FRAME_CONTEXT_MAXIMIZE:
745 client->frame->max_hover = FALSE;
746 frame_adjust_state(client->frame);
747 break;
748 case OB_FRAME_CONTEXT_ALLDESKTOPS:
749 client->frame->desk_hover = FALSE;
750 frame_adjust_state(client->frame);
751 break;
752 case OB_FRAME_CONTEXT_SHADE:
753 client->frame->shade_hover = FALSE;
754 frame_adjust_state(client->frame);
755 break;
756 case OB_FRAME_CONTEXT_ICONIFY:
757 client->frame->iconify_hover = FALSE;
758 frame_adjust_state(client->frame);
759 break;
760 case OB_FRAME_CONTEXT_CLOSE:
761 client->frame->close_hover = FALSE;
762 frame_adjust_state(client->frame);
763 break;
764 case OB_FRAME_CONTEXT_FRAME:
765 if (config_focus_follow && config_focus_delay)
766 ob_main_loop_timeout_remove_data(ob_main_loop,
767 focus_delay_func,
768 client, FALSE);
769 break;
770 default:
771 break;
772 }
773 break;
774 case EnterNotify:
775 {
776 gboolean nofocus = FALSE;
777
778 if (ignore_enter_focus) {
779 ignore_enter_focus--;
780 nofocus = TRUE;
781 }
782
783 con = frame_context(client, e->xcrossing.window);
784 switch (con) {
785 case OB_FRAME_CONTEXT_MAXIMIZE:
786 client->frame->max_hover = TRUE;
787 frame_adjust_state(client->frame);
788 break;
789 case OB_FRAME_CONTEXT_ALLDESKTOPS:
790 client->frame->desk_hover = TRUE;
791 frame_adjust_state(client->frame);
792 break;
793 case OB_FRAME_CONTEXT_SHADE:
794 client->frame->shade_hover = TRUE;
795 frame_adjust_state(client->frame);
796 break;
797 case OB_FRAME_CONTEXT_ICONIFY:
798 client->frame->iconify_hover = TRUE;
799 frame_adjust_state(client->frame);
800 break;
801 case OB_FRAME_CONTEXT_CLOSE:
802 client->frame->close_hover = TRUE;
803 frame_adjust_state(client->frame);
804 break;
805 case OB_FRAME_CONTEXT_FRAME:
806 if (e->xcrossing.mode == NotifyGrab ||
807 e->xcrossing.mode == NotifyUngrab)
808 {
809 ob_debug_type(OB_DEBUG_FOCUS,
810 "%sNotify mode %d detail %d on %lx IGNORED\n",
811 (e->type == EnterNotify ? "Enter" : "Leave"),
812 e->xcrossing.mode,
813 e->xcrossing.detail, client?client->window:0);
814 } else {
815 ob_debug_type(OB_DEBUG_FOCUS,
816 "%sNotify mode %d detail %d on %lx, "
817 "focusing window: %d\n",
818 (e->type == EnterNotify ? "Enter" : "Leave"),
819 e->xcrossing.mode,
820 e->xcrossing.detail, (client?client->window:0),
821 !nofocus);
822 if (!nofocus && config_focus_follow)
823 event_enter_client(client);
824 }
825 break;
826 default:
827 break;
828 }
829 break;
830 }
831 case ConfigureRequest:
832 /* compress these */
833 while (XCheckTypedWindowEvent(ob_display, client->window,
834 ConfigureRequest, &ce)) {
835 ++i;
836 /* XXX if this causes bad things.. we can compress config req's
837 with the same mask. */
838 e->xconfigurerequest.value_mask |=
839 ce.xconfigurerequest.value_mask;
840 if (ce.xconfigurerequest.value_mask & CWX)
841 e->xconfigurerequest.x = ce.xconfigurerequest.x;
842 if (ce.xconfigurerequest.value_mask & CWY)
843 e->xconfigurerequest.y = ce.xconfigurerequest.y;
844 if (ce.xconfigurerequest.value_mask & CWWidth)
845 e->xconfigurerequest.width = ce.xconfigurerequest.width;
846 if (ce.xconfigurerequest.value_mask & CWHeight)
847 e->xconfigurerequest.height = ce.xconfigurerequest.height;
848 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
849 e->xconfigurerequest.border_width =
850 ce.xconfigurerequest.border_width;
851 if (ce.xconfigurerequest.value_mask & CWStackMode)
852 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
853 }
854
855 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
856 if (client->iconic || client->shaded) return;
857
858 /* resize, then move, as specified in the EWMH section 7.7 */
859 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
860 CWX | CWY |
861 CWBorderWidth)) {
862 gint x, y, w, h;
863 ObCorner corner;
864
865 if (e->xconfigurerequest.value_mask & CWBorderWidth)
866 client->border_width = e->xconfigurerequest.border_width;
867
868 x = (e->xconfigurerequest.value_mask & CWX) ?
869 e->xconfigurerequest.x : client->area.x;
870 y = (e->xconfigurerequest.value_mask & CWY) ?
871 e->xconfigurerequest.y : client->area.y;
872 w = (e->xconfigurerequest.value_mask & CWWidth) ?
873 e->xconfigurerequest.width : client->area.width;
874 h = (e->xconfigurerequest.value_mask & CWHeight) ?
875 e->xconfigurerequest.height : client->area.height;
876
877 {
878 gint newx = x;
879 gint newy = y;
880 gint fw = w +
881 client->frame->size.left + client->frame->size.right;
882 gint fh = h +
883 client->frame->size.top + client->frame->size.bottom;
884 /* make this rude for size-only changes but not for position
885 changes.. */
886 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
887 (e->xconfigurerequest.value_mask & CWY));
888
889 client_find_onscreen(client, &newx, &newy, fw, fh,
890 !moving);
891 if (e->xconfigurerequest.value_mask & CWX)
892 x = newx;
893 if (e->xconfigurerequest.value_mask & CWY)
894 y = newy;
895 }
896
897 switch (client->gravity) {
898 case NorthEastGravity:
899 case EastGravity:
900 corner = OB_CORNER_TOPRIGHT;
901 break;
902 case SouthWestGravity:
903 case SouthGravity:
904 corner = OB_CORNER_BOTTOMLEFT;
905 break;
906 case SouthEastGravity:
907 corner = OB_CORNER_BOTTOMRIGHT;
908 break;
909 default: /* NorthWest, Static, etc */
910 corner = OB_CORNER_TOPLEFT;
911 }
912
913 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
914 TRUE);
915 }
916
917 if (e->xconfigurerequest.value_mask & CWStackMode) {
918 switch (e->xconfigurerequest.detail) {
919 case Below:
920 case BottomIf:
921 /* Apps are so rude. And this is totally disconnected from
922 activation/focus. Bleh. */
923 /*client_lower(client);*/
924 break;
925
926 case Above:
927 case TopIf:
928 default:
929 /* Apps are so rude. And this is totally disconnected from
930 activation/focus. Bleh. */
931 /*client_raise(client);*/
932 break;
933 }
934 }
935 break;
936 case UnmapNotify:
937 if (client->ignore_unmaps) {
938 client->ignore_unmaps--;
939 break;
940 }
941 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
942 "ignores left %d\n",
943 client->window, e->xunmap.event, e->xunmap.from_configure,
944 client->ignore_unmaps);
945 client_unmanage(client);
946 break;
947 case DestroyNotify:
948 ob_debug("DestroyNotify for window 0x%x\n", client->window);
949 client_unmanage(client);
950 break;
951 case ReparentNotify:
952 /* this is when the client is first taken captive in the frame */
953 if (e->xreparent.parent == client->frame->plate) break;
954
955 /*
956 This event is quite rare and is usually handled in unmapHandler.
957 However, if the window is unmapped when the reparent event occurs,
958 the window manager never sees it because an unmap event is not sent
959 to an already unmapped window.
960 */
961
962 /* we don't want the reparent event, put it back on the stack for the
963 X server to deal with after we unmanage the window */
964 XPutBackEvent(ob_display, e);
965
966 ob_debug("ReparentNotify for window 0x%x\n", client->window);
967 client_unmanage(client);
968 break;
969 case MapRequest:
970 ob_debug("MapRequest for 0x%lx\n", client->window);
971 if (!client->iconic) break; /* this normally doesn't happen, but if it
972 does, we don't want it!
973 it can happen now when the window is on
974 another desktop, but we still don't
975 want it! */
976 client_activate(client, FALSE, TRUE);
977 break;
978 case ClientMessage:
979 /* validate cuz we query stuff off the client here */
980 if (!client_validate(client)) break;
981
982 if (e->xclient.format != 32) return;
983
984 msgtype = e->xclient.message_type;
985 if (msgtype == prop_atoms.wm_change_state) {
986 /* compress changes into a single change */
987 while (XCheckTypedWindowEvent(ob_display, client->window,
988 e->type, &ce)) {
989 /* XXX: it would be nice to compress ALL messages of a
990 type, not just messages in a row without other
991 message types between. */
992 if (ce.xclient.message_type != msgtype) {
993 XPutBackEvent(ob_display, &ce);
994 break;
995 }
996 e->xclient = ce.xclient;
997 }
998 client_set_wm_state(client, e->xclient.data.l[0]);
999 } else if (msgtype == prop_atoms.net_wm_desktop) {
1000 /* compress changes into a single change */
1001 while (XCheckTypedWindowEvent(ob_display, client->window,
1002 e->type, &ce)) {
1003 /* XXX: it would be nice to compress ALL messages of a
1004 type, not just messages in a row without other
1005 message types between. */
1006 if (ce.xclient.message_type != msgtype) {
1007 XPutBackEvent(ob_display, &ce);
1008 break;
1009 }
1010 e->xclient = ce.xclient;
1011 }
1012 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1013 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1014 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1015 FALSE);
1016 } else if (msgtype == prop_atoms.net_wm_state) {
1017 /* can't compress these */
1018 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1019 (e->xclient.data.l[0] == 0 ? "Remove" :
1020 e->xclient.data.l[0] == 1 ? "Add" :
1021 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1022 e->xclient.data.l[1], e->xclient.data.l[2],
1023 client->window);
1024 client_set_state(client, e->xclient.data.l[0],
1025 e->xclient.data.l[1], e->xclient.data.l[2]);
1026 } else if (msgtype == prop_atoms.net_close_window) {
1027 ob_debug("net_close_window for 0x%lx\n", client->window);
1028 client_close(client);
1029 } else if (msgtype == prop_atoms.net_active_window) {
1030 ob_debug("net_active_window for 0x%lx source=%s\n",
1031 client->window,
1032 (e->xclient.data.l[0] == 0 ? "unknown" :
1033 (e->xclient.data.l[0] == 1 ? "application" :
1034 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1035 /* XXX make use of data.l[2] ! */
1036 event_curtime = e->xclient.data.l[1];
1037 client_activate(client, FALSE,
1038 (e->xclient.data.l[0] == 0 ||
1039 e->xclient.data.l[0] == 2));
1040 } else if (msgtype == prop_atoms.net_wm_moveresize) {
1041 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1042 client->window, e->xclient.data.l[2]);
1043 if ((Atom)e->xclient.data.l[2] ==
1044 prop_atoms.net_wm_moveresize_size_topleft ||
1045 (Atom)e->xclient.data.l[2] ==
1046 prop_atoms.net_wm_moveresize_size_top ||
1047 (Atom)e->xclient.data.l[2] ==
1048 prop_atoms.net_wm_moveresize_size_topright ||
1049 (Atom)e->xclient.data.l[2] ==
1050 prop_atoms.net_wm_moveresize_size_right ||
1051 (Atom)e->xclient.data.l[2] ==
1052 prop_atoms.net_wm_moveresize_size_right ||
1053 (Atom)e->xclient.data.l[2] ==
1054 prop_atoms.net_wm_moveresize_size_bottomright ||
1055 (Atom)e->xclient.data.l[2] ==
1056 prop_atoms.net_wm_moveresize_size_bottom ||
1057 (Atom)e->xclient.data.l[2] ==
1058 prop_atoms.net_wm_moveresize_size_bottomleft ||
1059 (Atom)e->xclient.data.l[2] ==
1060 prop_atoms.net_wm_moveresize_size_left ||
1061 (Atom)e->xclient.data.l[2] ==
1062 prop_atoms.net_wm_moveresize_move ||
1063 (Atom)e->xclient.data.l[2] ==
1064 prop_atoms.net_wm_moveresize_size_keyboard ||
1065 (Atom)e->xclient.data.l[2] ==
1066 prop_atoms.net_wm_moveresize_move_keyboard) {
1067
1068 moveresize_start(client, e->xclient.data.l[0],
1069 e->xclient.data.l[1], e->xclient.data.l[3],
1070 e->xclient.data.l[2]);
1071 }
1072 else if ((Atom)e->xclient.data.l[2] ==
1073 prop_atoms.net_wm_moveresize_cancel)
1074 moveresize_end(TRUE);
1075 } else if (msgtype == prop_atoms.net_moveresize_window) {
1076 gint oldg = client->gravity;
1077 gint tmpg, x, y, w, h;
1078
1079 if (e->xclient.data.l[0] & 0xff)
1080 tmpg = e->xclient.data.l[0] & 0xff;
1081 else
1082 tmpg = oldg;
1083
1084 if (e->xclient.data.l[0] & 1 << 8)
1085 x = e->xclient.data.l[1];
1086 else
1087 x = client->area.x;
1088 if (e->xclient.data.l[0] & 1 << 9)
1089 y = e->xclient.data.l[2];
1090 else
1091 y = client->area.y;
1092 if (e->xclient.data.l[0] & 1 << 10)
1093 w = e->xclient.data.l[3];
1094 else
1095 w = client->area.width;
1096 if (e->xclient.data.l[0] & 1 << 11)
1097 h = e->xclient.data.l[4];
1098 else
1099 h = client->area.height;
1100 client->gravity = tmpg;
1101
1102 {
1103 gint newx = x;
1104 gint newy = y;
1105 gint fw = w +
1106 client->frame->size.left + client->frame->size.right;
1107 gint fh = h +
1108 client->frame->size.top + client->frame->size.bottom;
1109 client_find_onscreen(client, &newx, &newy, fw, fh,
1110 client_normal(client));
1111 if (e->xclient.data.l[0] & 1 << 8)
1112 x = newx;
1113 if (e->xclient.data.l[0] & 1 << 9)
1114 y = newy;
1115 }
1116
1117 client_configure(client, OB_CORNER_TOPLEFT,
1118 x, y, w, h, FALSE, TRUE);
1119
1120 client->gravity = oldg;
1121 }
1122 break;
1123 case PropertyNotify:
1124 /* validate cuz we query stuff off the client here */
1125 if (!client_validate(client)) break;
1126
1127 /* compress changes to a single property into a single change */
1128 while (XCheckTypedWindowEvent(ob_display, client->window,
1129 e->type, &ce)) {
1130 Atom a, b;
1131
1132 /* XXX: it would be nice to compress ALL changes to a property,
1133 not just changes in a row without other props between. */
1134
1135 a = ce.xproperty.atom;
1136 b = e->xproperty.atom;
1137
1138 if (a == b)
1139 continue;
1140 if ((a == prop_atoms.net_wm_name ||
1141 a == prop_atoms.wm_name ||
1142 a == prop_atoms.net_wm_icon_name ||
1143 a == prop_atoms.wm_icon_name)
1144 &&
1145 (b == prop_atoms.net_wm_name ||
1146 b == prop_atoms.wm_name ||
1147 b == prop_atoms.net_wm_icon_name ||
1148 b == prop_atoms.wm_icon_name)) {
1149 continue;
1150 }
1151 if (a == prop_atoms.net_wm_icon &&
1152 b == prop_atoms.net_wm_icon)
1153 continue;
1154
1155 XPutBackEvent(ob_display, &ce);
1156 break;
1157 }
1158
1159 msgtype = e->xproperty.atom;
1160 if (msgtype == XA_WM_NORMAL_HINTS) {
1161 client_update_normal_hints(client);
1162 /* normal hints can make a window non-resizable */
1163 client_setup_decor_and_functions(client);
1164 } else if (msgtype == XA_WM_HINTS) {
1165 client_update_wmhints(client);
1166 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1167 client_update_transient_for(client);
1168 client_get_type(client);
1169 /* type may have changed, so update the layer */
1170 client_calc_layer(client);
1171 client_setup_decor_and_functions(client);
1172 } else if (msgtype == prop_atoms.net_wm_name ||
1173 msgtype == prop_atoms.wm_name ||
1174 msgtype == prop_atoms.net_wm_icon_name ||
1175 msgtype == prop_atoms.wm_icon_name) {
1176 client_update_title(client);
1177 } else if (msgtype == prop_atoms.wm_class) {
1178 client_update_class(client);
1179 } else if (msgtype == prop_atoms.wm_protocols) {
1180 client_update_protocols(client);
1181 client_setup_decor_and_functions(client);
1182 }
1183 else if (msgtype == prop_atoms.net_wm_strut) {
1184 client_update_strut(client);
1185 }
1186 else if (msgtype == prop_atoms.net_wm_icon) {
1187 client_update_icons(client);
1188 }
1189 else if (msgtype == prop_atoms.net_wm_user_time) {
1190 client_update_user_time(client);
1191 }
1192 else if (msgtype == prop_atoms.sm_client_id) {
1193 client_update_sm_client_id(client);
1194 }
1195 default:
1196 ;
1197 #ifdef SHAPE
1198 if (extensions_shape && e->type == extensions_shape_event_basep) {
1199 client->shaped = ((XShapeEvent*)e)->shaped;
1200 frame_adjust_shape(client->frame);
1201 }
1202 #endif
1203 }
1204 }
1205
1206 static void event_handle_dock(ObDock *s, XEvent *e)
1207 {
1208 switch (e->type) {
1209 case ButtonPress:
1210 if (e->xbutton.button == 1)
1211 stacking_raise(DOCK_AS_WINDOW(s));
1212 else if (e->xbutton.button == 2)
1213 stacking_lower(DOCK_AS_WINDOW(s));
1214 break;
1215 case EnterNotify:
1216 dock_hide(FALSE);
1217 break;
1218 case LeaveNotify:
1219 dock_hide(TRUE);
1220 break;
1221 }
1222 }
1223
1224 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1225 {
1226 switch (e->type) {
1227 case MotionNotify:
1228 dock_app_drag(app, &e->xmotion);
1229 break;
1230 case UnmapNotify:
1231 if (app->ignore_unmaps) {
1232 app->ignore_unmaps--;
1233 break;
1234 }
1235 dock_remove(app, TRUE);
1236 break;
1237 case DestroyNotify:
1238 dock_remove(app, FALSE);
1239 break;
1240 case ReparentNotify:
1241 dock_remove(app, FALSE);
1242 break;
1243 case ConfigureNotify:
1244 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1245 break;
1246 }
1247 }
1248
1249 ObMenuFrame* find_active_menu()
1250 {
1251 GList *it;
1252 ObMenuFrame *ret = NULL;
1253
1254 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1255 ret = it->data;
1256 if (ret->selected)
1257 break;
1258 ret = NULL;
1259 }
1260 return ret;
1261 }
1262
1263 ObMenuFrame* find_active_or_last_menu()
1264 {
1265 ObMenuFrame *ret = NULL;
1266
1267 ret = find_active_menu();
1268 if (!ret && menu_frame_visible)
1269 ret = menu_frame_visible->data;
1270 return ret;
1271 }
1272
1273 static void event_handle_menu(XEvent *ev)
1274 {
1275 ObMenuFrame *f;
1276 ObMenuEntryFrame *e;
1277
1278 switch (ev->type) {
1279 case ButtonRelease:
1280 if (menu_can_hide) {
1281 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1282 ev->xbutton.y_root)))
1283 menu_entry_frame_execute(e, ev->xbutton.state,
1284 ev->xbutton.time);
1285 else
1286 menu_frame_hide_all();
1287 }
1288 break;
1289 case EnterNotify:
1290 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1291 if (e->ignore_enters)
1292 --e->ignore_enters;
1293 else
1294 menu_frame_select(e->frame, e);
1295 }
1296 break;
1297 case LeaveNotify:
1298 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1299 (f = find_active_menu()) && f->selected == e &&
1300 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1301 {
1302 menu_frame_select(e->frame, NULL);
1303 }
1304 case MotionNotify:
1305 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1306 ev->xmotion.y_root)))
1307 menu_frame_select(e->frame, e);
1308 break;
1309 case KeyPress:
1310 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1311 menu_frame_hide_all();
1312 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1313 ObMenuFrame *f;
1314 if ((f = find_active_menu()))
1315 menu_entry_frame_execute(f->selected, ev->xkey.state,
1316 ev->xkey.time);
1317 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1318 ObMenuFrame *f;
1319 if ((f = find_active_or_last_menu()) && f->parent)
1320 menu_frame_select(f, NULL);
1321 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1322 ObMenuFrame *f;
1323 if ((f = find_active_or_last_menu()) && f->child)
1324 menu_frame_select_next(f->child);
1325 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1326 ObMenuFrame *f;
1327 if ((f = find_active_or_last_menu()))
1328 menu_frame_select_previous(f);
1329 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1330 ObMenuFrame *f;
1331 if ((f = find_active_or_last_menu()))
1332 menu_frame_select_next(f);
1333 }
1334 break;
1335 }
1336 }
1337
1338 static gboolean menu_hide_delay_func(gpointer data)
1339 {
1340 menu_can_hide = TRUE;
1341 return FALSE; /* no repeat */
1342 }
1343
1344 static void focus_delay_dest(gpointer data)
1345 {
1346 g_free(data);
1347 }
1348
1349 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1350 {
1351 const ObFocusDelayData *f1 = d1, *f2 = d2;
1352 return f1->client == f2->client;
1353 }
1354
1355 static gboolean focus_delay_func(gpointer data)
1356 {
1357 ObFocusDelayData *d = data;
1358 Time old = event_curtime;
1359
1360 event_curtime = d->time;
1361 if (focus_client != d->client) {
1362 if (client_focus(d->client) && config_focus_raise)
1363 client_raise(d->client);
1364 }
1365 event_curtime = old;
1366 return FALSE; /* no repeat */
1367 }
1368
1369 static void focus_delay_client_dest(ObClient *client, gpointer data)
1370 {
1371 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1372 client, FALSE);
1373 }
1374
1375 static void event_client_dest(ObClient *client, gpointer data)
1376 {
1377 if (client == focus_hilite)
1378 focus_hilite = NULL;
1379 }
1380
1381 void event_halt_focus_delay()
1382 {
1383 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1384 }
1385
1386 void event_ignore_queued_enters()
1387 {
1388 GSList *saved = NULL, *it;
1389 XEvent *e;
1390
1391 XSync(ob_display, FALSE);
1392
1393 /* count the events */
1394 while (TRUE) {
1395 e = g_new(XEvent, 1);
1396 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1397 ObWindow *win;
1398
1399 win = g_hash_table_lookup(window_map, &e->xany.window);
1400 if (win && WINDOW_IS_CLIENT(win))
1401 ++ignore_enter_focus;
1402
1403 saved = g_slist_append(saved, e);
1404 } else {
1405 g_free(e);
1406 break;
1407 }
1408 }
1409 /* put the events back */
1410 for (it = saved; it; it = g_slist_next(it)) {
1411 XPutBackEvent(ob_display, it->data);
1412 g_free(it->data);
1413 }
1414 g_slist_free(saved);
1415 }
1416
1417 gboolean event_time_after(Time t1, Time t2)
1418 {
1419 g_assert(t1 != CurrentTime);
1420 g_assert(t2 != CurrentTime);
1421
1422 /*
1423 Timestamp values wrap around (after about 49.7 days). The server, given
1424 its current time is represented by timestamp T, always interprets
1425 timestamps from clients by treating half of the timestamp space as being
1426 later in time than T.
1427 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1428 */
1429
1430 /* TIME_HALF is half of the number space of a Time type variable */
1431 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1432
1433 if (t2 >= TIME_HALF)
1434 /* t2 is in the second half so t1 might wrap around and be smaller than
1435 t2 */
1436 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1437 else
1438 /* t2 is in the first half so t1 has to come after it */
1439 return t1 >= t2 && t1 < (t2 + TIME_HALF);
1440 }
This page took 0.096266 seconds and 5 git commands to generate.