]> Dogcows Code - chaz/openbox/blob - openbox/event.c
don't grab the pointer during interactive events. this allows you to alt-tab during...
[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 if (!keyboard_interactively_grabbed())
595 mouse_event(client, e);
596 } else if (e->type == KeyPress) {
597 keyboard_event((focus_cycle_target ? focus_cycle_target :
598 (focus_hilite ? focus_hilite : client)),
599 e);
600 }
601 }
602 }
603 }
604 /* if something happens and it's not from an XEvent, then we don't know
605 the time */
606 event_curtime = CurrentTime;
607 }
608
609 static void event_handle_root(XEvent *e)
610 {
611 Atom msgtype;
612
613 switch(e->type) {
614 case SelectionClear:
615 ob_debug("Another WM has requested to replace us. Exiting.\n");
616 ob_exit_replace();
617 break;
618
619 case ClientMessage:
620 if (e->xclient.format != 32) break;
621
622 msgtype = e->xclient.message_type;
623 if (msgtype == prop_atoms.net_current_desktop) {
624 guint d = e->xclient.data.l[0];
625 if (d < screen_num_desktops) {
626 event_curtime = e->xclient.data.l[1];
627 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
628 screen_set_desktop(d);
629 }
630 } else if (msgtype == prop_atoms.net_number_of_desktops) {
631 guint d = e->xclient.data.l[0];
632 if (d > 0)
633 screen_set_num_desktops(d);
634 } else if (msgtype == prop_atoms.net_showing_desktop) {
635 screen_show_desktop(e->xclient.data.l[0] != 0);
636 } else if (msgtype == prop_atoms.ob_control) {
637 if (e->xclient.data.l[0] == 1)
638 ob_reconfigure();
639 else if (e->xclient.data.l[0] == 2)
640 ob_restart();
641 }
642 break;
643 case PropertyNotify:
644 if (e->xproperty.atom == prop_atoms.net_desktop_names)
645 screen_update_desktop_names();
646 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
647 screen_update_layout();
648 break;
649 case ConfigureNotify:
650 #ifdef XRANDR
651 XRRUpdateConfiguration(e);
652 #endif
653 screen_resize();
654 break;
655 default:
656 ;
657 }
658 }
659
660 static void event_handle_group(ObGroup *group, XEvent *e)
661 {
662 GSList *it;
663
664 g_assert(e->type == PropertyNotify);
665
666 for (it = group->members; it; it = g_slist_next(it))
667 event_handle_client(it->data, e);
668 }
669
670 void event_enter_client(ObClient *client)
671 {
672 g_assert(config_focus_follow);
673
674 if (client_normal(client) && client_can_focus(client)) {
675 if (config_focus_delay) {
676 ObFocusDelayData *data;
677
678 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
679
680 data = g_new(ObFocusDelayData, 1);
681 data->client = client;
682 data->time = event_curtime;
683
684 ob_main_loop_timeout_add(ob_main_loop,
685 config_focus_delay,
686 focus_delay_func,
687 data, focus_delay_cmp, focus_delay_dest);
688 } else {
689 ObFocusDelayData data;
690 data.client = client;
691 data.time = event_curtime;
692 focus_delay_func(&data);
693 }
694 }
695 }
696
697 static void event_handle_client(ObClient *client, XEvent *e)
698 {
699 XEvent ce;
700 Atom msgtype;
701 gint i=0;
702 ObFrameContext con;
703
704 switch (e->type) {
705 case VisibilityNotify:
706 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
707 break;
708 case ButtonPress:
709 case ButtonRelease:
710 /* Wheel buttons don't draw because they are an instant click, so it
711 is a waste of resources to go drawing it. */
712 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
713 con = frame_context(client, e->xbutton.window);
714 con = mouse_button_frame_context(con, e->xbutton.button);
715 switch (con) {
716 case OB_FRAME_CONTEXT_MAXIMIZE:
717 client->frame->max_press = (e->type == ButtonPress);
718 framerender_frame(client->frame);
719 break;
720 case OB_FRAME_CONTEXT_CLOSE:
721 client->frame->close_press = (e->type == ButtonPress);
722 framerender_frame(client->frame);
723 break;
724 case OB_FRAME_CONTEXT_ICONIFY:
725 client->frame->iconify_press = (e->type == ButtonPress);
726 framerender_frame(client->frame);
727 break;
728 case OB_FRAME_CONTEXT_ALLDESKTOPS:
729 client->frame->desk_press = (e->type == ButtonPress);
730 framerender_frame(client->frame);
731 break;
732 case OB_FRAME_CONTEXT_SHADE:
733 client->frame->shade_press = (e->type == ButtonPress);
734 framerender_frame(client->frame);
735 break;
736 default:
737 /* nothing changes with clicks for any other contexts */
738 break;
739 }
740 }
741 break;
742 case LeaveNotify:
743 con = frame_context(client, e->xcrossing.window);
744 switch (con) {
745 case OB_FRAME_CONTEXT_MAXIMIZE:
746 client->frame->max_hover = FALSE;
747 frame_adjust_state(client->frame);
748 break;
749 case OB_FRAME_CONTEXT_ALLDESKTOPS:
750 client->frame->desk_hover = FALSE;
751 frame_adjust_state(client->frame);
752 break;
753 case OB_FRAME_CONTEXT_SHADE:
754 client->frame->shade_hover = FALSE;
755 frame_adjust_state(client->frame);
756 break;
757 case OB_FRAME_CONTEXT_ICONIFY:
758 client->frame->iconify_hover = FALSE;
759 frame_adjust_state(client->frame);
760 break;
761 case OB_FRAME_CONTEXT_CLOSE:
762 client->frame->close_hover = FALSE;
763 frame_adjust_state(client->frame);
764 break;
765 case OB_FRAME_CONTEXT_FRAME:
766 if (keyboard_interactively_grabbed())
767 break;
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 (keyboard_interactively_grabbed())
810 break;
811 if (e->xcrossing.mode == NotifyGrab ||
812 e->xcrossing.mode == NotifyUngrab)
813 {
814 ob_debug_type(OB_DEBUG_FOCUS,
815 "%sNotify mode %d detail %d on %lx IGNORED\n",
816 (e->type == EnterNotify ? "Enter" : "Leave"),
817 e->xcrossing.mode,
818 e->xcrossing.detail, client?client->window:0);
819 } else {
820 ob_debug_type(OB_DEBUG_FOCUS,
821 "%sNotify mode %d detail %d on %lx, "
822 "focusing window: %d\n",
823 (e->type == EnterNotify ? "Enter" : "Leave"),
824 e->xcrossing.mode,
825 e->xcrossing.detail, (client?client->window:0),
826 !nofocus);
827 if (!nofocus && config_focus_follow)
828 event_enter_client(client);
829 }
830 break;
831 default:
832 break;
833 }
834 break;
835 }
836 case ConfigureRequest:
837 /* compress these */
838 while (XCheckTypedWindowEvent(ob_display, client->window,
839 ConfigureRequest, &ce)) {
840 ++i;
841 /* XXX if this causes bad things.. we can compress config req's
842 with the same mask. */
843 e->xconfigurerequest.value_mask |=
844 ce.xconfigurerequest.value_mask;
845 if (ce.xconfigurerequest.value_mask & CWX)
846 e->xconfigurerequest.x = ce.xconfigurerequest.x;
847 if (ce.xconfigurerequest.value_mask & CWY)
848 e->xconfigurerequest.y = ce.xconfigurerequest.y;
849 if (ce.xconfigurerequest.value_mask & CWWidth)
850 e->xconfigurerequest.width = ce.xconfigurerequest.width;
851 if (ce.xconfigurerequest.value_mask & CWHeight)
852 e->xconfigurerequest.height = ce.xconfigurerequest.height;
853 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
854 e->xconfigurerequest.border_width =
855 ce.xconfigurerequest.border_width;
856 if (ce.xconfigurerequest.value_mask & CWStackMode)
857 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
858 }
859
860 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
861 if (client->iconic || client->shaded) return;
862
863 /* resize, then move, as specified in the EWMH section 7.7 */
864 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
865 CWX | CWY |
866 CWBorderWidth)) {
867 gint x, y, w, h;
868 ObCorner corner;
869
870 if (e->xconfigurerequest.value_mask & CWBorderWidth)
871 client->border_width = e->xconfigurerequest.border_width;
872
873 x = (e->xconfigurerequest.value_mask & CWX) ?
874 e->xconfigurerequest.x : client->area.x;
875 y = (e->xconfigurerequest.value_mask & CWY) ?
876 e->xconfigurerequest.y : client->area.y;
877 w = (e->xconfigurerequest.value_mask & CWWidth) ?
878 e->xconfigurerequest.width : client->area.width;
879 h = (e->xconfigurerequest.value_mask & CWHeight) ?
880 e->xconfigurerequest.height : client->area.height;
881
882 {
883 gint newx = x;
884 gint newy = y;
885 gint fw = w +
886 client->frame->size.left + client->frame->size.right;
887 gint fh = h +
888 client->frame->size.top + client->frame->size.bottom;
889 /* make this rude for size-only changes but not for position
890 changes.. */
891 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
892 (e->xconfigurerequest.value_mask & CWY));
893
894 client_find_onscreen(client, &newx, &newy, fw, fh,
895 !moving);
896 if (e->xconfigurerequest.value_mask & CWX)
897 x = newx;
898 if (e->xconfigurerequest.value_mask & CWY)
899 y = newy;
900 }
901
902 switch (client->gravity) {
903 case NorthEastGravity:
904 case EastGravity:
905 corner = OB_CORNER_TOPRIGHT;
906 break;
907 case SouthWestGravity:
908 case SouthGravity:
909 corner = OB_CORNER_BOTTOMLEFT;
910 break;
911 case SouthEastGravity:
912 corner = OB_CORNER_BOTTOMRIGHT;
913 break;
914 default: /* NorthWest, Static, etc */
915 corner = OB_CORNER_TOPLEFT;
916 }
917
918 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
919 TRUE);
920 }
921
922 if (e->xconfigurerequest.value_mask & CWStackMode) {
923 switch (e->xconfigurerequest.detail) {
924 case Below:
925 case BottomIf:
926 /* Apps are so rude. And this is totally disconnected from
927 activation/focus. Bleh. */
928 /*client_lower(client);*/
929 break;
930
931 case Above:
932 case TopIf:
933 default:
934 /* Apps are so rude. And this is totally disconnected from
935 activation/focus. Bleh. */
936 /*client_raise(client);*/
937 break;
938 }
939 }
940 break;
941 case UnmapNotify:
942 if (client->ignore_unmaps) {
943 client->ignore_unmaps--;
944 break;
945 }
946 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
947 "ignores left %d\n",
948 client->window, e->xunmap.event, e->xunmap.from_configure,
949 client->ignore_unmaps);
950 client_unmanage(client);
951 break;
952 case DestroyNotify:
953 ob_debug("DestroyNotify for window 0x%x\n", client->window);
954 client_unmanage(client);
955 break;
956 case ReparentNotify:
957 /* this is when the client is first taken captive in the frame */
958 if (e->xreparent.parent == client->frame->plate) break;
959
960 /*
961 This event is quite rare and is usually handled in unmapHandler.
962 However, if the window is unmapped when the reparent event occurs,
963 the window manager never sees it because an unmap event is not sent
964 to an already unmapped window.
965 */
966
967 /* we don't want the reparent event, put it back on the stack for the
968 X server to deal with after we unmanage the window */
969 XPutBackEvent(ob_display, e);
970
971 ob_debug("ReparentNotify for window 0x%x\n", client->window);
972 client_unmanage(client);
973 break;
974 case MapRequest:
975 ob_debug("MapRequest for 0x%lx\n", client->window);
976 if (!client->iconic) break; /* this normally doesn't happen, but if it
977 does, we don't want it!
978 it can happen now when the window is on
979 another desktop, but we still don't
980 want it! */
981 client_activate(client, FALSE, TRUE);
982 break;
983 case ClientMessage:
984 /* validate cuz we query stuff off the client here */
985 if (!client_validate(client)) break;
986
987 if (e->xclient.format != 32) return;
988
989 msgtype = e->xclient.message_type;
990 if (msgtype == prop_atoms.wm_change_state) {
991 /* compress changes into a single change */
992 while (XCheckTypedWindowEvent(ob_display, client->window,
993 e->type, &ce)) {
994 /* XXX: it would be nice to compress ALL messages of a
995 type, not just messages in a row without other
996 message types between. */
997 if (ce.xclient.message_type != msgtype) {
998 XPutBackEvent(ob_display, &ce);
999 break;
1000 }
1001 e->xclient = ce.xclient;
1002 }
1003 client_set_wm_state(client, e->xclient.data.l[0]);
1004 } else if (msgtype == prop_atoms.net_wm_desktop) {
1005 /* compress changes into a single change */
1006 while (XCheckTypedWindowEvent(ob_display, client->window,
1007 e->type, &ce)) {
1008 /* XXX: it would be nice to compress ALL messages of a
1009 type, not just messages in a row without other
1010 message types between. */
1011 if (ce.xclient.message_type != msgtype) {
1012 XPutBackEvent(ob_display, &ce);
1013 break;
1014 }
1015 e->xclient = ce.xclient;
1016 }
1017 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1018 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1019 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1020 FALSE);
1021 } else if (msgtype == prop_atoms.net_wm_state) {
1022 /* can't compress these */
1023 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1024 (e->xclient.data.l[0] == 0 ? "Remove" :
1025 e->xclient.data.l[0] == 1 ? "Add" :
1026 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1027 e->xclient.data.l[1], e->xclient.data.l[2],
1028 client->window);
1029 client_set_state(client, e->xclient.data.l[0],
1030 e->xclient.data.l[1], e->xclient.data.l[2]);
1031 } else if (msgtype == prop_atoms.net_close_window) {
1032 ob_debug("net_close_window for 0x%lx\n", client->window);
1033 client_close(client);
1034 } else if (msgtype == prop_atoms.net_active_window) {
1035 ob_debug("net_active_window for 0x%lx source=%s\n",
1036 client->window,
1037 (e->xclient.data.l[0] == 0 ? "unknown" :
1038 (e->xclient.data.l[0] == 1 ? "application" :
1039 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1040 /* XXX make use of data.l[2] ! */
1041 event_curtime = e->xclient.data.l[1];
1042 client_activate(client, FALSE,
1043 (e->xclient.data.l[0] == 0 ||
1044 e->xclient.data.l[0] == 2));
1045 } else if (msgtype == prop_atoms.net_wm_moveresize) {
1046 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1047 client->window, e->xclient.data.l[2]);
1048 if ((Atom)e->xclient.data.l[2] ==
1049 prop_atoms.net_wm_moveresize_size_topleft ||
1050 (Atom)e->xclient.data.l[2] ==
1051 prop_atoms.net_wm_moveresize_size_top ||
1052 (Atom)e->xclient.data.l[2] ==
1053 prop_atoms.net_wm_moveresize_size_topright ||
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_right ||
1058 (Atom)e->xclient.data.l[2] ==
1059 prop_atoms.net_wm_moveresize_size_bottomright ||
1060 (Atom)e->xclient.data.l[2] ==
1061 prop_atoms.net_wm_moveresize_size_bottom ||
1062 (Atom)e->xclient.data.l[2] ==
1063 prop_atoms.net_wm_moveresize_size_bottomleft ||
1064 (Atom)e->xclient.data.l[2] ==
1065 prop_atoms.net_wm_moveresize_size_left ||
1066 (Atom)e->xclient.data.l[2] ==
1067 prop_atoms.net_wm_moveresize_move ||
1068 (Atom)e->xclient.data.l[2] ==
1069 prop_atoms.net_wm_moveresize_size_keyboard ||
1070 (Atom)e->xclient.data.l[2] ==
1071 prop_atoms.net_wm_moveresize_move_keyboard) {
1072
1073 moveresize_start(client, e->xclient.data.l[0],
1074 e->xclient.data.l[1], e->xclient.data.l[3],
1075 e->xclient.data.l[2]);
1076 }
1077 else if ((Atom)e->xclient.data.l[2] ==
1078 prop_atoms.net_wm_moveresize_cancel)
1079 moveresize_end(TRUE);
1080 } else if (msgtype == prop_atoms.net_moveresize_window) {
1081 gint oldg = client->gravity;
1082 gint tmpg, x, y, w, h;
1083
1084 if (e->xclient.data.l[0] & 0xff)
1085 tmpg = e->xclient.data.l[0] & 0xff;
1086 else
1087 tmpg = oldg;
1088
1089 if (e->xclient.data.l[0] & 1 << 8)
1090 x = e->xclient.data.l[1];
1091 else
1092 x = client->area.x;
1093 if (e->xclient.data.l[0] & 1 << 9)
1094 y = e->xclient.data.l[2];
1095 else
1096 y = client->area.y;
1097 if (e->xclient.data.l[0] & 1 << 10)
1098 w = e->xclient.data.l[3];
1099 else
1100 w = client->area.width;
1101 if (e->xclient.data.l[0] & 1 << 11)
1102 h = e->xclient.data.l[4];
1103 else
1104 h = client->area.height;
1105 client->gravity = tmpg;
1106
1107 {
1108 gint newx = x;
1109 gint newy = y;
1110 gint fw = w +
1111 client->frame->size.left + client->frame->size.right;
1112 gint fh = h +
1113 client->frame->size.top + client->frame->size.bottom;
1114 client_find_onscreen(client, &newx, &newy, fw, fh,
1115 client_normal(client));
1116 if (e->xclient.data.l[0] & 1 << 8)
1117 x = newx;
1118 if (e->xclient.data.l[0] & 1 << 9)
1119 y = newy;
1120 }
1121
1122 client_configure(client, OB_CORNER_TOPLEFT,
1123 x, y, w, h, FALSE, TRUE);
1124
1125 client->gravity = oldg;
1126 }
1127 break;
1128 case PropertyNotify:
1129 /* validate cuz we query stuff off the client here */
1130 if (!client_validate(client)) break;
1131
1132 /* compress changes to a single property into a single change */
1133 while (XCheckTypedWindowEvent(ob_display, client->window,
1134 e->type, &ce)) {
1135 Atom a, b;
1136
1137 /* XXX: it would be nice to compress ALL changes to a property,
1138 not just changes in a row without other props between. */
1139
1140 a = ce.xproperty.atom;
1141 b = e->xproperty.atom;
1142
1143 if (a == b)
1144 continue;
1145 if ((a == prop_atoms.net_wm_name ||
1146 a == prop_atoms.wm_name ||
1147 a == prop_atoms.net_wm_icon_name ||
1148 a == prop_atoms.wm_icon_name)
1149 &&
1150 (b == prop_atoms.net_wm_name ||
1151 b == prop_atoms.wm_name ||
1152 b == prop_atoms.net_wm_icon_name ||
1153 b == prop_atoms.wm_icon_name)) {
1154 continue;
1155 }
1156 if (a == prop_atoms.net_wm_icon &&
1157 b == prop_atoms.net_wm_icon)
1158 continue;
1159
1160 XPutBackEvent(ob_display, &ce);
1161 break;
1162 }
1163
1164 msgtype = e->xproperty.atom;
1165 if (msgtype == XA_WM_NORMAL_HINTS) {
1166 client_update_normal_hints(client);
1167 /* normal hints can make a window non-resizable */
1168 client_setup_decor_and_functions(client);
1169 } else if (msgtype == XA_WM_HINTS) {
1170 client_update_wmhints(client);
1171 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1172 client_update_transient_for(client);
1173 client_get_type(client);
1174 /* type may have changed, so update the layer */
1175 client_calc_layer(client);
1176 client_setup_decor_and_functions(client);
1177 } else if (msgtype == prop_atoms.net_wm_name ||
1178 msgtype == prop_atoms.wm_name ||
1179 msgtype == prop_atoms.net_wm_icon_name ||
1180 msgtype == prop_atoms.wm_icon_name) {
1181 client_update_title(client);
1182 } else if (msgtype == prop_atoms.wm_class) {
1183 client_update_class(client);
1184 } else if (msgtype == prop_atoms.wm_protocols) {
1185 client_update_protocols(client);
1186 client_setup_decor_and_functions(client);
1187 }
1188 else if (msgtype == prop_atoms.net_wm_strut) {
1189 client_update_strut(client);
1190 }
1191 else if (msgtype == prop_atoms.net_wm_icon) {
1192 client_update_icons(client);
1193 }
1194 else if (msgtype == prop_atoms.net_wm_user_time) {
1195 client_update_user_time(client);
1196 }
1197 else if (msgtype == prop_atoms.sm_client_id) {
1198 client_update_sm_client_id(client);
1199 }
1200 default:
1201 ;
1202 #ifdef SHAPE
1203 if (extensions_shape && e->type == extensions_shape_event_basep) {
1204 client->shaped = ((XShapeEvent*)e)->shaped;
1205 frame_adjust_shape(client->frame);
1206 }
1207 #endif
1208 }
1209 }
1210
1211 static void event_handle_dock(ObDock *s, XEvent *e)
1212 {
1213 switch (e->type) {
1214 case ButtonPress:
1215 if (e->xbutton.button == 1)
1216 stacking_raise(DOCK_AS_WINDOW(s));
1217 else if (e->xbutton.button == 2)
1218 stacking_lower(DOCK_AS_WINDOW(s));
1219 break;
1220 case EnterNotify:
1221 dock_hide(FALSE);
1222 break;
1223 case LeaveNotify:
1224 dock_hide(TRUE);
1225 break;
1226 }
1227 }
1228
1229 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1230 {
1231 switch (e->type) {
1232 case MotionNotify:
1233 dock_app_drag(app, &e->xmotion);
1234 break;
1235 case UnmapNotify:
1236 if (app->ignore_unmaps) {
1237 app->ignore_unmaps--;
1238 break;
1239 }
1240 dock_remove(app, TRUE);
1241 break;
1242 case DestroyNotify:
1243 dock_remove(app, FALSE);
1244 break;
1245 case ReparentNotify:
1246 dock_remove(app, FALSE);
1247 break;
1248 case ConfigureNotify:
1249 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1250 break;
1251 }
1252 }
1253
1254 ObMenuFrame* find_active_menu()
1255 {
1256 GList *it;
1257 ObMenuFrame *ret = NULL;
1258
1259 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1260 ret = it->data;
1261 if (ret->selected)
1262 break;
1263 ret = NULL;
1264 }
1265 return ret;
1266 }
1267
1268 ObMenuFrame* find_active_or_last_menu()
1269 {
1270 ObMenuFrame *ret = NULL;
1271
1272 ret = find_active_menu();
1273 if (!ret && menu_frame_visible)
1274 ret = menu_frame_visible->data;
1275 return ret;
1276 }
1277
1278 static void event_handle_menu(XEvent *ev)
1279 {
1280 ObMenuFrame *f;
1281 ObMenuEntryFrame *e;
1282
1283 switch (ev->type) {
1284 case ButtonRelease:
1285 if (menu_can_hide) {
1286 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1287 ev->xbutton.y_root)))
1288 menu_entry_frame_execute(e, ev->xbutton.state,
1289 ev->xbutton.time);
1290 else
1291 menu_frame_hide_all();
1292 }
1293 break;
1294 case EnterNotify:
1295 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1296 if (e->ignore_enters)
1297 --e->ignore_enters;
1298 else
1299 menu_frame_select(e->frame, e);
1300 }
1301 break;
1302 case LeaveNotify:
1303 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1304 (f = find_active_menu()) && f->selected == e &&
1305 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1306 {
1307 menu_frame_select(e->frame, NULL);
1308 }
1309 case MotionNotify:
1310 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1311 ev->xmotion.y_root)))
1312 menu_frame_select(e->frame, e);
1313 break;
1314 case KeyPress:
1315 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1316 menu_frame_hide_all();
1317 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1318 ObMenuFrame *f;
1319 if ((f = find_active_menu()))
1320 menu_entry_frame_execute(f->selected, ev->xkey.state,
1321 ev->xkey.time);
1322 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1323 ObMenuFrame *f;
1324 if ((f = find_active_or_last_menu()) && f->parent)
1325 menu_frame_select(f, NULL);
1326 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1327 ObMenuFrame *f;
1328 if ((f = find_active_or_last_menu()) && f->child)
1329 menu_frame_select_next(f->child);
1330 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1331 ObMenuFrame *f;
1332 if ((f = find_active_or_last_menu()))
1333 menu_frame_select_previous(f);
1334 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1335 ObMenuFrame *f;
1336 if ((f = find_active_or_last_menu()))
1337 menu_frame_select_next(f);
1338 }
1339 break;
1340 }
1341 }
1342
1343 static gboolean menu_hide_delay_func(gpointer data)
1344 {
1345 menu_can_hide = TRUE;
1346 return FALSE; /* no repeat */
1347 }
1348
1349 static void focus_delay_dest(gpointer data)
1350 {
1351 g_free(data);
1352 }
1353
1354 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1355 {
1356 const ObFocusDelayData *f1 = d1, *f2 = d2;
1357 return f1->client == f2->client;
1358 }
1359
1360 static gboolean focus_delay_func(gpointer data)
1361 {
1362 ObFocusDelayData *d = data;
1363 Time old = event_curtime;
1364
1365 event_curtime = d->time;
1366 if (focus_client != d->client) {
1367 if (client_focus(d->client) && config_focus_raise)
1368 client_raise(d->client);
1369 }
1370 event_curtime = old;
1371 return FALSE; /* no repeat */
1372 }
1373
1374 static void focus_delay_client_dest(ObClient *client, gpointer data)
1375 {
1376 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1377 client, FALSE);
1378 }
1379
1380 static void event_client_dest(ObClient *client, gpointer data)
1381 {
1382 if (client == focus_hilite)
1383 focus_hilite = NULL;
1384 }
1385
1386 void event_halt_focus_delay()
1387 {
1388 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1389 }
1390
1391 void event_ignore_queued_enters()
1392 {
1393 GSList *saved = NULL, *it;
1394 XEvent *e;
1395
1396 XSync(ob_display, FALSE);
1397
1398 /* count the events */
1399 while (TRUE) {
1400 e = g_new(XEvent, 1);
1401 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1402 ObWindow *win;
1403
1404 win = g_hash_table_lookup(window_map, &e->xany.window);
1405 if (win && WINDOW_IS_CLIENT(win))
1406 ++ignore_enter_focus;
1407
1408 saved = g_slist_append(saved, e);
1409 } else {
1410 g_free(e);
1411 break;
1412 }
1413 }
1414 /* put the events back */
1415 for (it = saved; it; it = g_slist_next(it)) {
1416 XPutBackEvent(ob_display, it->data);
1417 g_free(it->data);
1418 }
1419 g_slist_free(saved);
1420 }
1421
1422 gboolean event_time_after(Time t1, Time t2)
1423 {
1424 g_assert(t1 != CurrentTime);
1425 g_assert(t2 != CurrentTime);
1426
1427 /*
1428 Timestamp values wrap around (after about 49.7 days). The server, given
1429 its current time is represented by timestamp T, always interprets
1430 timestamps from clients by treating half of the timestamp space as being
1431 later in time than T.
1432 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1433 */
1434
1435 /* TIME_HALF is half of the number space of a Time type variable */
1436 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1437
1438 if (t2 >= TIME_HALF)
1439 /* t2 is in the second half so t1 might wrap around and be smaller than
1440 t2 */
1441 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1442 else
1443 /* t2 is in the first half so t1 has to come after it */
1444 return t1 >= t2 && t1 < (t2 + TIME_HALF);
1445 }
This page took 0.108634 seconds and 5 git commands to generate.