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