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