1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
33 #include "menuframe.h"
39 #include "framerender.h"
41 #include "focus_cycle.h"
42 #include "moveresize.h"
45 #include "extensions.h"
46 #include "translate.h"
49 #include <X11/Xatom.h>
52 #ifdef HAVE_SYS_SELECT_H
53 # include <sys/select.h>
59 # include <unistd.h> /* for usleep() */
62 # include <X11/XKBlib.h>
66 #include <X11/ICE/ICElib.h>
80 static void event_process(const XEvent *e, gpointer data);
81 static void event_handle_root(XEvent *e);
82 static gboolean event_handle_menu_keyboard(XEvent *e);
83 static gboolean event_handle_menu(XEvent *e);
84 static void event_handle_dock(ObDock *s, XEvent *e);
85 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
86 static void event_handle_client(ObClient *c, XEvent *e);
87 static void event_handle_user_time_window_clients(GSList *l, XEvent *e);
88 static void event_handle_user_input(ObClient *client, XEvent *e);
89 static gboolean is_enter_focus_event_ignored(XEvent *e);
91 static void focus_delay_dest(gpointer data);
92 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
93 static gboolean focus_delay_func(gpointer data);
94 static void focus_delay_client_dest(ObClient *client, gpointer data);
96 /* The time for the current event being processed */
97 Time event_curtime = CurrentTime;
99 static guint ignore_enter_focus = 0;
100 static gboolean focus_left_screen = FALSE;
103 static void ice_handler(gint fd, gpointer conn)
106 IceProcessMessages(conn, NULL, &b);
109 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
110 IcePointer *watch_data)
115 fd = IceConnectionNumber(conn);
116 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
118 ob_main_loop_fd_remove(ob_main_loop, fd);
124 void event_startup(gboolean reconfig)
126 if (reconfig) return;
128 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
131 IceAddConnectionWatch(ice_watch, NULL);
134 client_add_destroy_notify(focus_delay_client_dest, NULL);
137 void event_shutdown(gboolean reconfig)
139 if (reconfig) return;
142 IceRemoveConnectionWatch(ice_watch, NULL);
145 client_remove_destroy_notify(focus_delay_client_dest);
148 static Window event_get_window(XEvent *e)
155 window = RootWindow(ob_display, ob_screen);
158 window = e->xmap.window;
161 window = e->xunmap.window;
164 window = e->xdestroywindow.window;
166 case ConfigureRequest:
167 window = e->xconfigurerequest.window;
169 case ConfigureNotify:
170 window = e->xconfigure.window;
174 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
175 switch (((XkbAnyEvent*)e)->xkb_type) {
177 window = ((XkbBellNotifyEvent*)e)->window;
184 if (extensions_sync &&
185 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
190 window = e->xany.window;
195 static void event_set_curtime(XEvent *e)
197 Time t = CurrentTime;
199 /* grab the lasttime and hack up the state */
215 t = e->xproperty.time;
219 t = e->xcrossing.time;
223 if (extensions_sync &&
224 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
226 t = ((XSyncAlarmNotifyEvent*)e)->time;
229 /* if more event types are anticipated, get their timestamp
237 static void event_hack_mods(XEvent *e)
240 XkbStateRec xkb_state;
246 e->xbutton.state = modkeys_only_modifier_masks(e->xbutton.state);
249 e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
252 e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
254 if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
255 e->xkey.state = xkb_state.compat_state;
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);
264 e->xmotion.state = modkeys_only_modifier_masks(e->xmotion.state);
265 /* compress events */
268 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
270 e->xmotion.x = ce.xmotion.x;
271 e->xmotion.y = ce.xmotion.y;
272 e->xmotion.x_root = ce.xmotion.x_root;
273 e->xmotion.y_root = ce.xmotion.y_root;
280 static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
282 gint mode = e->xfocus.mode;
283 gint detail = e->xfocus.detail;
284 Window win = e->xany.window;
286 if (e->type == FocusIn) {
287 /* These are ones we never want.. */
289 /* This means focus was given by a keyboard/mouse grab. */
290 if (mode == NotifyGrab)
292 /* This means focus was given back from a keyboard/mouse grab. */
293 if (mode == NotifyUngrab)
296 /* These are the ones we want.. */
298 if (win == RootWindow(ob_display, ob_screen)) {
299 /* If looking for a focus in on a client, then always return
300 FALSE for focus in's to the root window */
303 /* This means focus reverted off of a client */
304 else if (detail == NotifyPointerRoot ||
305 detail == NotifyDetailNone ||
306 detail == NotifyInferior ||
307 /* This means focus got here from another screen */
308 detail == NotifyNonlinear)
314 /* It was on a client, was it a valid one?
315 It's possible to get a FocusIn event for a client that was managed
318 if (in_client_only) {
319 ObWindow *w = g_hash_table_lookup(window_map, &e->xfocus.window);
320 if (!w || !WINDOW_IS_CLIENT(w))
324 /* This means focus reverted to parent from the client (this
325 happens often during iconify animation) */
326 if (detail == NotifyInferior)
330 /* This means focus moved from the root window to a client */
331 if (detail == NotifyVirtual)
333 /* This means focus moved from one client to another */
334 if (detail == NotifyNonlinearVirtual)
340 g_assert(e->type == FocusOut);
342 /* These are ones we never want.. */
344 /* This means focus was taken by a keyboard/mouse grab. */
345 if (mode == NotifyGrab)
347 /* This means focus was grabbed on a window and it was released. */
348 if (mode == NotifyUngrab)
351 /* Focus left the root window revertedto state */
352 if (win == RootWindow(ob_display, ob_screen))
355 /* These are the ones we want.. */
357 /* This means focus moved from a client to the root window */
358 if (detail == NotifyVirtual)
360 /* This means focus moved from one client to another */
361 if (detail == NotifyNonlinearVirtual)
369 static Bool event_look_for_focusin(Display *d, XEvent *e, XPointer arg)
371 return e->type == FocusIn && wanted_focusevent(e, FALSE);
374 static Bool event_look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
376 return e->type == FocusIn && wanted_focusevent(e, TRUE);
379 static void print_focusevent(XEvent *e)
381 gint mode = e->xfocus.mode;
382 gint detail = e->xfocus.detail;
383 Window win = e->xany.window;
384 const gchar *modestr, *detailstr;
387 case NotifyNormal: modestr="NotifyNormal"; break;
388 case NotifyGrab: modestr="NotifyGrab"; break;
389 case NotifyUngrab: modestr="NotifyUngrab"; break;
390 case NotifyWhileGrabbed: modestr="NotifyWhileGrabbed"; break;
393 case NotifyAncestor: detailstr="NotifyAncestor"; break;
394 case NotifyVirtual: detailstr="NotifyVirtual"; break;
395 case NotifyInferior: detailstr="NotifyInferior"; break;
396 case NotifyNonlinear: detailstr="NotifyNonlinear"; break;
397 case NotifyNonlinearVirtual: detailstr="NotifyNonlinearVirtual"; break;
398 case NotifyPointer: detailstr="NotifyPointer"; break;
399 case NotifyPointerRoot: detailstr="NotifyPointerRoot"; break;
400 case NotifyDetailNone: detailstr="NotifyDetailNone"; break;
403 if (mode == NotifyGrab || mode == NotifyUngrab)
408 ob_debug_type(OB_DEBUG_FOCUS, "Focus%s 0x%x mode=%s detail=%s\n",
409 (e->xfocus.type == FocusIn ? "In" : "Out"),
415 static gboolean event_ignore(XEvent *e, ObClient *client)
420 if (!wanted_focusevent(e, FALSE))
425 if (!wanted_focusevent(e, FALSE))
432 static void event_process(const XEvent *ec, gpointer data)
435 ObClient *client = NULL;
437 ObDockApp *dockapp = NULL;
438 ObWindow *obwin = NULL;
439 GSList *timewinclients = NULL;
441 ObEventData *ed = data;
443 /* make a copy we can mangle */
447 window = event_get_window(e);
448 if (e->type != PropertyNotify ||
449 !(timewinclients = propwin_get_clients(window,
450 OB_PROPWIN_USER_TIME)))
451 if ((obwin = g_hash_table_lookup(window_map, &window))) {
452 switch (obwin->type) {
454 dock = WINDOW_AS_DOCK(obwin);
457 dockapp = WINDOW_AS_DOCKAPP(obwin);
460 client = WINDOW_AS_CLIENT(obwin);
463 case Window_Internal:
464 /* not to be used for events */
465 g_assert_not_reached();
470 event_set_curtime(e);
472 if (event_ignore(e, client)) {
479 /* deal with it in the kernel */
481 if (menu_frame_visible &&
482 (e->type == EnterNotify || e->type == LeaveNotify))
484 /* crossing events for menu */
485 event_handle_menu(e);
486 } else if (e->type == FocusIn) {
487 if (e->xfocus.detail == NotifyPointerRoot ||
488 e->xfocus.detail == NotifyDetailNone ||
489 e->xfocus.detail == NotifyInferior ||
490 e->xfocus.detail == NotifyNonlinear)
494 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root, "
495 "pointer root/none or "
496 "the frame window\n");
498 if (e->xfocus.detail == NotifyInferior ||
499 e->xfocus.detail == NotifyNonlinear)
501 focus_left_screen = FALSE;
504 /* If another FocusIn is in the queue then don't fallback yet. This
505 fixes the fun case of:
506 window map -> send focusin
507 window unmap -> get focusout
508 window map -> send focusin
509 get first focus out -> fall back to something (new window
510 hasn't received focus yet, so something else) -> send focusin
511 which means the "something else" is the last thing to get a
512 focusin sent to it, so the new window doesn't end up with focus.
514 But if the other focus in is something like PointerRoot then we
515 still want to fall back.
517 if (XCheckIfEvent(ob_display, &ce, event_look_for_focusin_client,
520 XPutBackEvent(ob_display, &ce);
521 ob_debug_type(OB_DEBUG_FOCUS,
522 " but another FocusIn is coming\n");
524 /* Focus has been reverted.
526 FocusOut events come after UnmapNotify, so we don't need to
527 worry about focusing an invalid window
530 if (!focus_left_screen)
531 focus_fallback(TRUE, FALSE);
536 ob_debug_type(OB_DEBUG_FOCUS,
537 "Focus went to a window that is already gone\n");
539 /* If you send focus to a window and then it disappears, you can
540 get the FocusIn for it, after it is unmanaged.
541 Just wait for the next FocusOut/FocusIn pair. */
543 else if (client != focus_client) {
544 focus_left_screen = FALSE;
545 frame_adjust_focus(client->frame, TRUE);
546 focus_set_client(client);
547 client_calc_layer(client);
548 client_bring_helper_windows(client);
550 } else if (e->type == FocusOut) {
553 /* Look for the followup FocusIn */
554 if (!XCheckIfEvent(ob_display, &ce, event_look_for_focusin, NULL)) {
555 /* There is no FocusIn, this means focus went to a window that
556 is not being managed, or a window on another screen. */
560 xerror_set_ignore(TRUE);
561 if (XGetInputFocus(ob_display, &win, &i) != 0 &&
562 XGetGeometry(ob_display, win, &root, &i,&i,&u,&u,&u,&u) != 0 &&
563 root != RootWindow(ob_display, ob_screen))
565 ob_debug_type(OB_DEBUG_FOCUS,
566 "Focus went to another screen !\n");
567 focus_left_screen = TRUE;
570 ob_debug_type(OB_DEBUG_FOCUS,
571 "Focus went to a black hole !\n");
572 xerror_set_ignore(FALSE);
573 /* nothing is focused */
574 focus_set_client(NULL);
576 /* Focus moved, so process the FocusIn event */
577 ObEventData ed = { .ignored = FALSE };
578 event_process(&ce, &ed);
580 /* The FocusIn was ignored, this means it was on a window
581 that isn't a client. */
582 ob_debug_type(OB_DEBUG_FOCUS,
583 "Focus went to an unmanaged window 0x%x !\n",
585 focus_fallback(TRUE, FALSE);
589 if (client && client != focus_client) {
590 frame_adjust_focus(client->frame, FALSE);
591 /* focus_set_client(NULL) has already been called in this
592 section or by focus_fallback */
593 client_calc_layer(client);
595 } else if (timewinclients)
596 event_handle_user_time_window_clients(timewinclients, e);
598 event_handle_client(client, e);
600 event_handle_dockapp(dockapp, e);
602 event_handle_dock(dock, e);
603 else if (window == RootWindow(ob_display, ob_screen))
604 event_handle_root(e);
605 else if (e->type == MapRequest)
606 client_manage(window);
607 else if (e->type == ClientMessage) {
608 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
609 windows that are not managed yet. */
610 if (e->xclient.message_type == prop_atoms.net_request_frame_extents) {
611 /* Pretend to manage the client, getting information used to
612 determine its decorations */
613 ObClient *c = client_fake_manage(e->xclient.window);
616 /* set the frame extents on the window */
617 vals[0] = c->frame->size.left;
618 vals[1] = c->frame->size.right;
619 vals[2] = c->frame->size.top;
620 vals[3] = c->frame->size.bottom;
621 PROP_SETA32(e->xclient.window, net_frame_extents,
624 /* Free the pretend client */
625 client_fake_unmanage(c);
628 else if (e->type == ConfigureRequest) {
629 /* unhandled configure requests must be used to configure the
633 xwc.x = e->xconfigurerequest.x;
634 xwc.y = e->xconfigurerequest.y;
635 xwc.width = e->xconfigurerequest.width;
636 xwc.height = e->xconfigurerequest.height;
637 xwc.border_width = e->xconfigurerequest.border_width;
638 xwc.sibling = e->xconfigurerequest.above;
639 xwc.stack_mode = e->xconfigurerequest.detail;
641 /* we are not to be held responsible if someone sends us an
643 xerror_set_ignore(TRUE);
644 XConfigureWindow(ob_display, window,
645 e->xconfigurerequest.value_mask, &xwc);
646 xerror_set_ignore(FALSE);
649 else if (extensions_sync &&
650 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
652 XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
653 if (se->alarm == moveresize_alarm && moveresize_in_progress)
658 if (e->type == ButtonPress || e->type == ButtonRelease ||
659 e->type == MotionNotify || e->type == KeyPress ||
660 e->type == KeyRelease)
662 event_handle_user_input(client, e);
665 /* if something happens and it's not from an XEvent, then we don't know
667 event_curtime = CurrentTime;
670 static void event_handle_root(XEvent *e)
676 ob_debug("Another WM has requested to replace us. Exiting.\n");
681 if (e->xclient.format != 32) break;
683 msgtype = e->xclient.message_type;
684 if (msgtype == prop_atoms.net_current_desktop) {
685 guint d = e->xclient.data.l[0];
686 if (d < screen_num_desktops) {
687 event_curtime = e->xclient.data.l[1];
688 if (event_curtime == 0)
689 ob_debug_type(OB_DEBUG_APP_BUGS,
690 "_NET_CURRENT_DESKTOP message is missing "
692 screen_set_desktop(d, TRUE);
694 } else if (msgtype == prop_atoms.net_number_of_desktops) {
695 guint d = e->xclient.data.l[0];
696 if (d > 0 && d <= 1000)
697 screen_set_num_desktops(d);
698 } else if (msgtype == prop_atoms.net_showing_desktop) {
699 screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
700 } else if (msgtype == prop_atoms.ob_control) {
701 if (e->xclient.data.l[0] == 1)
703 else if (e->xclient.data.l[0] == 2)
708 if (e->xproperty.atom == prop_atoms.net_desktop_names)
709 screen_update_desktop_names();
710 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
711 screen_update_layout();
713 case ConfigureNotify:
715 XRRUpdateConfiguration(e);
724 void event_enter_client(ObClient *client)
726 g_assert(config_focus_follow);
728 if (client_enter_focusable(client) && client_can_focus(client)) {
729 if (config_focus_delay) {
730 ObFocusDelayData *data;
732 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
734 data = g_new(ObFocusDelayData, 1);
735 data->client = client;
736 data->time = event_curtime;
738 ob_main_loop_timeout_add(ob_main_loop,
741 data, focus_delay_cmp, focus_delay_dest);
743 ObFocusDelayData data;
744 data.client = client;
745 data.time = event_curtime;
746 focus_delay_func(&data);
751 static void event_handle_user_time_window_clients(GSList *l, XEvent *e)
753 g_assert(e->type == PropertyNotify);
754 if (e->xproperty.atom == prop_atoms.net_wm_user_time) {
755 for (; l; l = g_slist_next(l))
756 client_update_user_time(l->data);
760 static void event_handle_client(ObClient *client, XEvent *e)
765 static gint px = -1, py = -1;
770 /* save where the press occured for the first button pressed */
772 pb = e->xbutton.button;
777 /* Wheel buttons don't draw because they are an instant click, so it
778 is a waste of resources to go drawing it.
779 if the user is doing an intereactive thing, or has a menu open then
780 the mouse is grabbed (possibly) and if we get these events we don't
781 want to deal with them
783 if (!(e->xbutton.button == 4 || e->xbutton.button == 5) &&
784 !keyboard_interactively_grabbed() &&
787 /* use where the press occured */
788 con = frame_context(client, e->xbutton.window, px, py);
789 con = mouse_button_frame_context(con, e->xbutton.button,
792 if (e->type == ButtonRelease && e->xbutton.button == pb)
793 pb = 0, px = py = -1;
796 case OB_FRAME_CONTEXT_MAXIMIZE:
797 client->frame->max_press = (e->type == ButtonPress);
798 framerender_frame(client->frame);
800 case OB_FRAME_CONTEXT_CLOSE:
801 client->frame->close_press = (e->type == ButtonPress);
802 framerender_frame(client->frame);
804 case OB_FRAME_CONTEXT_ICONIFY:
805 client->frame->iconify_press = (e->type == ButtonPress);
806 framerender_frame(client->frame);
808 case OB_FRAME_CONTEXT_ALLDESKTOPS:
809 client->frame->desk_press = (e->type == ButtonPress);
810 framerender_frame(client->frame);
812 case OB_FRAME_CONTEXT_SHADE:
813 client->frame->shade_press = (e->type == ButtonPress);
814 framerender_frame(client->frame);
817 /* nothing changes with clicks for any other contexts */
823 /* when there is a grab on the pointer, we won't get enter/leave
824 notifies, but we still get motion events */
825 if (grab_on_pointer()) break;
827 con = frame_context(client, e->xmotion.window,
828 e->xmotion.x, e->xmotion.y);
830 case OB_FRAME_CONTEXT_TITLEBAR:
831 case OB_FRAME_CONTEXT_TLCORNER:
832 case OB_FRAME_CONTEXT_TRCORNER:
833 /* we've left the button area inside the titlebar */
834 if (client->frame->max_hover || client->frame->desk_hover ||
835 client->frame->shade_hover || client->frame->iconify_hover ||
836 client->frame->close_hover)
838 client->frame->max_hover = FALSE;
839 client->frame->desk_hover = FALSE;
840 client->frame->shade_hover = FALSE;
841 client->frame->iconify_hover = FALSE;
842 client->frame->close_hover = FALSE;
843 frame_adjust_state(client->frame);
846 case OB_FRAME_CONTEXT_MAXIMIZE:
847 if (!client->frame->max_hover) {
848 client->frame->max_hover = TRUE;
849 frame_adjust_state(client->frame);
852 case OB_FRAME_CONTEXT_ALLDESKTOPS:
853 if (!client->frame->desk_hover) {
854 client->frame->desk_hover = TRUE;
855 frame_adjust_state(client->frame);
858 case OB_FRAME_CONTEXT_SHADE:
859 if (!client->frame->shade_hover) {
860 client->frame->shade_hover = TRUE;
861 frame_adjust_state(client->frame);
864 case OB_FRAME_CONTEXT_ICONIFY:
865 if (!client->frame->iconify_hover) {
866 client->frame->iconify_hover = TRUE;
867 frame_adjust_state(client->frame);
870 case OB_FRAME_CONTEXT_CLOSE:
871 if (!client->frame->close_hover) {
872 client->frame->close_hover = TRUE;
873 frame_adjust_state(client->frame);
881 con = frame_context(client, e->xcrossing.window,
882 e->xcrossing.x, e->xcrossing.y);
884 case OB_FRAME_CONTEXT_TITLEBAR:
885 case OB_FRAME_CONTEXT_TLCORNER:
886 case OB_FRAME_CONTEXT_TRCORNER:
887 /* we've left the button area inside the titlebar */
888 if (client->frame->max_hover || client->frame->desk_hover ||
889 client->frame->shade_hover || client->frame->iconify_hover ||
890 client->frame->close_hover)
892 client->frame->max_hover = FALSE;
893 client->frame->desk_hover = FALSE;
894 client->frame->shade_hover = FALSE;
895 client->frame->iconify_hover = FALSE;
896 client->frame->close_hover = FALSE;
897 frame_adjust_state(client->frame);
900 case OB_FRAME_CONTEXT_MAXIMIZE:
901 client->frame->max_hover = FALSE;
902 frame_adjust_state(client->frame);
904 case OB_FRAME_CONTEXT_ALLDESKTOPS:
905 client->frame->desk_hover = FALSE;
906 frame_adjust_state(client->frame);
908 case OB_FRAME_CONTEXT_SHADE:
909 client->frame->shade_hover = FALSE;
910 frame_adjust_state(client->frame);
912 case OB_FRAME_CONTEXT_ICONIFY:
913 client->frame->iconify_hover = FALSE;
914 frame_adjust_state(client->frame);
916 case OB_FRAME_CONTEXT_CLOSE:
917 client->frame->close_hover = FALSE;
918 frame_adjust_state(client->frame);
920 case OB_FRAME_CONTEXT_FRAME:
921 /* When the mouse leaves an animating window, don't use the
922 corresponding enter events. Pretend like the animating window
923 doesn't even exist..! */
924 if (frame_iconify_animating(client->frame))
925 event_ignore_all_queued_enters();
927 ob_debug_type(OB_DEBUG_FOCUS,
928 "%sNotify mode %d detail %d on %lx\n",
929 (e->type == EnterNotify ? "Enter" : "Leave"),
931 e->xcrossing.detail, (client?client->window:0));
932 if (keyboard_interactively_grabbed())
934 if (config_focus_follow && config_focus_delay &&
935 /* leave inferior events can happen when the mouse goes onto
936 the window's border and then into the window before the
938 e->xcrossing.detail != NotifyInferior)
940 ob_main_loop_timeout_remove_data(ob_main_loop,
951 con = frame_context(client, e->xcrossing.window,
952 e->xcrossing.x, e->xcrossing.y);
954 case OB_FRAME_CONTEXT_MAXIMIZE:
955 client->frame->max_hover = TRUE;
956 frame_adjust_state(client->frame);
958 case OB_FRAME_CONTEXT_ALLDESKTOPS:
959 client->frame->desk_hover = TRUE;
960 frame_adjust_state(client->frame);
962 case OB_FRAME_CONTEXT_SHADE:
963 client->frame->shade_hover = TRUE;
964 frame_adjust_state(client->frame);
966 case OB_FRAME_CONTEXT_ICONIFY:
967 client->frame->iconify_hover = TRUE;
968 frame_adjust_state(client->frame);
970 case OB_FRAME_CONTEXT_CLOSE:
971 client->frame->close_hover = TRUE;
972 frame_adjust_state(client->frame);
974 case OB_FRAME_CONTEXT_FRAME:
975 if (keyboard_interactively_grabbed())
977 if (e->xcrossing.mode == NotifyGrab ||
978 e->xcrossing.mode == NotifyUngrab ||
979 /*ignore enters when we're already in the window */
980 e->xcrossing.detail == NotifyInferior ||
981 is_enter_focus_event_ignored(e))
983 ob_debug_type(OB_DEBUG_FOCUS,
984 "%sNotify mode %d detail %d on %lx IGNORED\n",
985 (e->type == EnterNotify ? "Enter" : "Leave"),
987 e->xcrossing.detail, client?client->window:0);
990 ob_debug_type(OB_DEBUG_FOCUS,
991 "%sNotify mode %d detail %d on %lx, "
993 (e->type == EnterNotify ? "Enter" : "Leave"),
995 e->xcrossing.detail, (client?client->window:0));
996 if (config_focus_follow)
997 event_enter_client(client);
1005 case ConfigureRequest:
1007 /* dont compress these unless you're going to watch for property
1008 notifies in between (these can change what the configure would
1010 also you can't compress stacking events
1014 gboolean move = FALSE;
1015 gboolean resize = FALSE;
1017 /* get the current area */
1018 RECT_TO_DIMS(client->area, x, y, w, h);
1020 ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1022 " x %d y %d w %d h %d b %d\n",
1024 screen_desktop, client->wmstate, client->frame->visible,
1025 x, y, w, h, client->border_width);
1027 if (e->xconfigurerequest.value_mask & CWBorderWidth)
1028 if (client->border_width != e->xconfigurerequest.border_width) {
1029 client->border_width = e->xconfigurerequest.border_width;
1031 /* if the border width is changing then that is the same
1032 as requesting a resize, but we don't actually change
1033 the client's border, so it will change their root
1034 coordiantes (since they include the border width) and
1035 we need to a notify then */
1040 if (e->xconfigurerequest.value_mask & CWStackMode) {
1041 ObClient *sibling = NULL;
1043 /* get the sibling */
1044 if (e->xconfigurerequest.value_mask & CWSibling) {
1046 win = g_hash_table_lookup(window_map,
1047 &e->xconfigurerequest.above);
1048 if (WINDOW_IS_CLIENT(win) && WINDOW_AS_CLIENT(win) != client)
1049 sibling = WINDOW_AS_CLIENT(win);
1052 /* activate it rather than just focus it */
1053 stacking_restack_request(client, sibling,
1054 e->xconfigurerequest.detail, TRUE);
1056 /* if a stacking change moves the window without resizing */
1060 if ((e->xconfigurerequest.value_mask & CWX) ||
1061 (e->xconfigurerequest.value_mask & CWY) ||
1062 (e->xconfigurerequest.value_mask & CWWidth) ||
1063 (e->xconfigurerequest.value_mask & CWHeight))
1065 if (e->xconfigurerequest.value_mask & CWX) {
1066 /* don't allow clients to move shaded windows (fvwm does this)
1068 if (!client->shaded)
1069 x = e->xconfigurerequest.x;
1072 if (e->xconfigurerequest.value_mask & CWY) {
1073 /* don't allow clients to move shaded windows (fvwm does this)
1075 if (!client->shaded)
1076 y = e->xconfigurerequest.y;
1080 if (e->xconfigurerequest.value_mask & CWWidth) {
1081 w = e->xconfigurerequest.width;
1084 if (e->xconfigurerequest.value_mask & CWHeight) {
1085 h = e->xconfigurerequest.height;
1090 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1091 "move %d resize %d\n",
1092 e->xconfigurerequest.value_mask & CWX, x,
1093 e->xconfigurerequest.value_mask & CWY, y,
1094 e->xconfigurerequest.value_mask & CWWidth, w,
1095 e->xconfigurerequest.value_mask & CWHeight, h,
1098 /* check for broken apps moving to their root position
1100 XXX remove this some day...that would be nice. right now all
1101 kde apps do this when they try activate themselves on another
1102 desktop. eg. open amarok window on desktop 1, switch to desktop
1103 2, click amarok tray icon. it will move by its decoration size.
1105 if (x != client->area.x &&
1106 x == (client->frame->area.x + client->frame->size.left -
1107 (gint)client->border_width) &&
1108 y != client->area.y &&
1109 y == (client->frame->area.y + client->frame->size.top -
1110 (gint)client->border_width) &&
1111 w == client->area.width &&
1112 h == client->area.height)
1114 ob_debug_type(OB_DEBUG_APP_BUGS,
1115 "Application %s is trying to move via "
1116 "ConfigureRequest to it's root window position "
1117 "but it is not using StaticGravity\n",
1123 /* they still requested a move, so don't change whether a
1124 notify is sent or not */
1127 if (move || resize) {
1130 client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
1132 /* if x was not given, then use gravity to figure out the new
1133 x. the reference point should not be moved */
1134 if ((e->xconfigurerequest.value_mask & CWWidth &&
1135 !(e->xconfigurerequest.value_mask & CWX)))
1136 client_gravity_resize_w(client, &x, client->area.width, w);
1137 /* if y was not given, then use gravity to figure out the new
1138 y. the reference point should not be moved */
1139 if ((e->xconfigurerequest.value_mask & CWHeight &&
1140 !(e->xconfigurerequest.value_mask & CWY)))
1141 client_gravity_resize_h(client, &y, client->area.height,h);
1143 client_find_onscreen(client, &x, &y, w, h, FALSE);
1145 /* if they requested something that moves the window, or if
1146 the window is actually being changed then configure it and
1147 send a configure notify to them */
1148 if (move || !RECT_EQUAL_DIMS(client->area, x, y, w, h)) {
1149 ob_debug("Granting ConfigureRequest x %d y %d w %d h %d\n",
1151 client_configure(client, x, y, w, h, FALSE, TRUE);
1154 /* ignore enter events caused by these like ob actions do */
1155 event_ignore_all_queued_enters();
1160 if (client->ignore_unmaps) {
1161 client->ignore_unmaps--;
1164 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1165 "ignores left %d\n",
1166 client->window, e->xunmap.event, e->xunmap.from_configure,
1167 client->ignore_unmaps);
1168 client_unmanage(client);
1171 ob_debug("DestroyNotify for window 0x%x\n", client->window);
1172 client_unmanage(client);
1174 case ReparentNotify:
1175 /* this is when the client is first taken captive in the frame */
1176 if (e->xreparent.parent == client->frame->window) break;
1179 This event is quite rare and is usually handled in unmapHandler.
1180 However, if the window is unmapped when the reparent event occurs,
1181 the window manager never sees it because an unmap event is not sent
1182 to an already unmapped window.
1185 /* we don't want the reparent event, put it back on the stack for the
1186 X server to deal with after we unmanage the window */
1187 XPutBackEvent(ob_display, e);
1189 ob_debug("ReparentNotify for window 0x%x\n", client->window);
1190 client_unmanage(client);
1193 ob_debug("MapRequest for 0x%lx\n", client->window);
1194 if (!client->iconic) break; /* this normally doesn't happen, but if it
1195 does, we don't want it!
1196 it can happen now when the window is on
1197 another desktop, but we still don't
1199 client_activate(client, FALSE, TRUE);
1202 /* validate cuz we query stuff off the client here */
1203 if (!client_validate(client)) break;
1205 if (e->xclient.format != 32) return;
1207 msgtype = e->xclient.message_type;
1208 if (msgtype == prop_atoms.wm_change_state) {
1209 /* compress changes into a single change */
1210 while (XCheckTypedWindowEvent(ob_display, client->window,
1212 /* XXX: it would be nice to compress ALL messages of a
1213 type, not just messages in a row without other
1214 message types between. */
1215 if (ce.xclient.message_type != msgtype) {
1216 XPutBackEvent(ob_display, &ce);
1219 e->xclient = ce.xclient;
1221 client_set_wm_state(client, e->xclient.data.l[0]);
1222 } else if (msgtype == prop_atoms.net_wm_desktop) {
1223 /* compress changes into a single change */
1224 while (XCheckTypedWindowEvent(ob_display, client->window,
1226 /* XXX: it would be nice to compress ALL messages of a
1227 type, not just messages in a row without other
1228 message types between. */
1229 if (ce.xclient.message_type != msgtype) {
1230 XPutBackEvent(ob_display, &ce);
1233 e->xclient = ce.xclient;
1235 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1236 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1237 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1239 } else if (msgtype == prop_atoms.net_wm_state) {
1240 /* can't compress these */
1241 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1242 (e->xclient.data.l[0] == 0 ? "Remove" :
1243 e->xclient.data.l[0] == 1 ? "Add" :
1244 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1245 e->xclient.data.l[1], e->xclient.data.l[2],
1247 client_set_state(client, e->xclient.data.l[0],
1248 e->xclient.data.l[1], e->xclient.data.l[2]);
1250 /* ignore enter events caused by these like ob actions do */
1251 event_ignore_all_queued_enters();
1252 } else if (msgtype == prop_atoms.net_close_window) {
1253 ob_debug("net_close_window for 0x%lx\n", client->window);
1254 client_close(client);
1255 } else if (msgtype == prop_atoms.net_active_window) {
1256 ob_debug("net_active_window for 0x%lx source=%s\n",
1258 (e->xclient.data.l[0] == 0 ? "unknown" :
1259 (e->xclient.data.l[0] == 1 ? "application" :
1260 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1261 /* XXX make use of data.l[2] !? */
1262 if (e->xclient.data.l[0] == 1 || e->xclient.data.l[0] == 2) {
1263 event_curtime = e->xclient.data.l[1];
1264 if (event_curtime == 0)
1265 ob_debug_type(OB_DEBUG_APP_BUGS,
1266 "_NET_ACTIVE_WINDOW message for window %s is"
1267 " missing a timestamp\n", client->title);
1269 ob_debug_type(OB_DEBUG_APP_BUGS,
1270 "_NET_ACTIVE_WINDOW message for window %s is "
1271 "missing source indication\n");
1272 client_activate(client, FALSE,
1273 (e->xclient.data.l[0] == 0 ||
1274 e->xclient.data.l[0] == 2));
1275 } else if (msgtype == prop_atoms.net_wm_moveresize) {
1276 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1277 client->window, e->xclient.data.l[2]);
1278 if ((Atom)e->xclient.data.l[2] ==
1279 prop_atoms.net_wm_moveresize_size_topleft ||
1280 (Atom)e->xclient.data.l[2] ==
1281 prop_atoms.net_wm_moveresize_size_top ||
1282 (Atom)e->xclient.data.l[2] ==
1283 prop_atoms.net_wm_moveresize_size_topright ||
1284 (Atom)e->xclient.data.l[2] ==
1285 prop_atoms.net_wm_moveresize_size_right ||
1286 (Atom)e->xclient.data.l[2] ==
1287 prop_atoms.net_wm_moveresize_size_right ||
1288 (Atom)e->xclient.data.l[2] ==
1289 prop_atoms.net_wm_moveresize_size_bottomright ||
1290 (Atom)e->xclient.data.l[2] ==
1291 prop_atoms.net_wm_moveresize_size_bottom ||
1292 (Atom)e->xclient.data.l[2] ==
1293 prop_atoms.net_wm_moveresize_size_bottomleft ||
1294 (Atom)e->xclient.data.l[2] ==
1295 prop_atoms.net_wm_moveresize_size_left ||
1296 (Atom)e->xclient.data.l[2] ==
1297 prop_atoms.net_wm_moveresize_move ||
1298 (Atom)e->xclient.data.l[2] ==
1299 prop_atoms.net_wm_moveresize_size_keyboard ||
1300 (Atom)e->xclient.data.l[2] ==
1301 prop_atoms.net_wm_moveresize_move_keyboard) {
1303 moveresize_start(client, e->xclient.data.l[0],
1304 e->xclient.data.l[1], e->xclient.data.l[3],
1305 e->xclient.data.l[2]);
1307 else if ((Atom)e->xclient.data.l[2] ==
1308 prop_atoms.net_wm_moveresize_cancel)
1309 moveresize_end(TRUE);
1310 } else if (msgtype == prop_atoms.net_moveresize_window) {
1311 gint ograv, x, y, w, h;
1313 ograv = client->gravity;
1315 if (e->xclient.data.l[0] & 0xff)
1316 client->gravity = e->xclient.data.l[0] & 0xff;
1318 if (e->xclient.data.l[0] & 1 << 8)
1319 x = e->xclient.data.l[1];
1322 if (e->xclient.data.l[0] & 1 << 9)
1323 y = e->xclient.data.l[2];
1327 if (e->xclient.data.l[0] & 1 << 10) {
1328 w = e->xclient.data.l[3];
1330 /* if x was not given, then use gravity to figure out the new
1331 x. the reference point should not be moved */
1332 if (!(e->xclient.data.l[0] & 1 << 8))
1333 client_gravity_resize_w(client, &x, client->area.width, w);
1336 w = client->area.width;
1338 if (e->xclient.data.l[0] & 1 << 11) {
1339 h = e->xclient.data.l[4];
1341 /* if y was not given, then use gravity to figure out the new
1342 y. the reference point should not be moved */
1343 if (!(e->xclient.data.l[0] & 1 << 9))
1344 client_gravity_resize_h(client, &y, client->area.height,h);
1347 h = client->area.height;
1349 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)\n",
1350 e->xclient.data.l[0] & 1 << 8, x,
1351 e->xclient.data.l[0] & 1 << 9, y,
1354 client_find_onscreen(client, &x, &y, w, h, FALSE);
1356 client_configure(client, x, y, w, h, FALSE, TRUE);
1358 client->gravity = ograv;
1360 /* ignore enter events caused by these like ob actions do */
1361 event_ignore_all_queued_enters();
1362 } else if (msgtype == prop_atoms.net_restack_window) {
1363 if (e->xclient.data.l[0] != 2) {
1364 ob_debug_type(OB_DEBUG_APP_BUGS,
1365 "_NET_RESTACK_WINDOW sent for window %s with "
1366 "invalid source indication %ld\n",
1367 client->title, e->xclient.data.l[0]);
1369 ObClient *sibling = NULL;
1370 if (e->xclient.data.l[1]) {
1371 ObWindow *win = g_hash_table_lookup
1372 (window_map, &e->xclient.data.l[1]);
1373 if (WINDOW_IS_CLIENT(win) &&
1374 WINDOW_AS_CLIENT(win) != client)
1376 sibling = WINDOW_AS_CLIENT(win);
1378 if (sibling == NULL)
1379 ob_debug_type(OB_DEBUG_APP_BUGS,
1380 "_NET_RESTACK_WINDOW sent for window %s "
1381 "with invalid sibling 0x%x\n",
1382 client->title, e->xclient.data.l[1]);
1384 if (e->xclient.data.l[2] == Below ||
1385 e->xclient.data.l[2] == BottomIf ||
1386 e->xclient.data.l[2] == Above ||
1387 e->xclient.data.l[2] == TopIf ||
1388 e->xclient.data.l[2] == Opposite)
1390 /* just raise, don't activate */
1391 stacking_restack_request(client, sibling,
1392 e->xclient.data.l[2], FALSE);
1393 /* send a synthetic ConfigureNotify, cuz this is supposed
1394 to be like a ConfigureRequest. */
1395 client_reconfigure(client);
1397 ob_debug_type(OB_DEBUG_APP_BUGS,
1398 "_NET_RESTACK_WINDOW sent for window %s "
1399 "with invalid detail %d\n",
1400 client->title, e->xclient.data.l[2]);
1404 case PropertyNotify:
1405 /* validate cuz we query stuff off the client here */
1406 if (!client_validate(client)) break;
1408 /* compress changes to a single property into a single change */
1409 while (XCheckTypedWindowEvent(ob_display, client->window,
1413 /* XXX: it would be nice to compress ALL changes to a property,
1414 not just changes in a row without other props between. */
1416 a = ce.xproperty.atom;
1417 b = e->xproperty.atom;
1421 if ((a == prop_atoms.net_wm_name ||
1422 a == prop_atoms.wm_name ||
1423 a == prop_atoms.net_wm_icon_name ||
1424 a == prop_atoms.wm_icon_name)
1426 (b == prop_atoms.net_wm_name ||
1427 b == prop_atoms.wm_name ||
1428 b == prop_atoms.net_wm_icon_name ||
1429 b == prop_atoms.wm_icon_name)) {
1432 if (a == prop_atoms.net_wm_icon &&
1433 b == prop_atoms.net_wm_icon)
1436 XPutBackEvent(ob_display, &ce);
1440 msgtype = e->xproperty.atom;
1441 if (msgtype == XA_WM_NORMAL_HINTS) {
1442 client_update_normal_hints(client);
1443 /* normal hints can make a window non-resizable */
1444 client_setup_decor_and_functions(client, TRUE);
1445 } else if (msgtype == XA_WM_HINTS) {
1446 client_update_wmhints(client);
1447 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1448 client_update_transient_for(client);
1449 client_get_type_and_transientness(client);
1450 /* type may have changed, so update the layer */
1451 client_calc_layer(client);
1452 client_setup_decor_and_functions(client, TRUE);
1453 } else if (msgtype == prop_atoms.net_wm_name ||
1454 msgtype == prop_atoms.wm_name ||
1455 msgtype == prop_atoms.net_wm_icon_name ||
1456 msgtype == prop_atoms.wm_icon_name) {
1457 client_update_title(client);
1458 } else if (msgtype == prop_atoms.wm_protocols) {
1459 client_update_protocols(client);
1460 client_setup_decor_and_functions(client, TRUE);
1462 else if (msgtype == prop_atoms.net_wm_strut) {
1463 client_update_strut(client);
1465 else if (msgtype == prop_atoms.net_wm_strut_partial) {
1466 client_update_strut(client);
1468 else if (msgtype == prop_atoms.net_wm_icon) {
1469 client_update_icons(client);
1471 else if (msgtype == prop_atoms.net_wm_icon_geometry) {
1472 client_update_icon_geometry(client);
1474 else if (msgtype == prop_atoms.net_wm_user_time) {
1475 client_update_user_time(client);
1477 else if (msgtype == prop_atoms.net_wm_user_time_window) {
1478 client_update_user_time_window(client);
1481 else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
1482 client_update_sync_request_counter(client);
1486 case ColormapNotify:
1487 client_update_colormap(client, e->xcolormap.colormap);
1492 if (extensions_shape && e->type == extensions_shape_event_basep) {
1493 client->shaped = ((XShapeEvent*)e)->shaped;
1494 frame_adjust_shape(client->frame);
1500 static void event_handle_dock(ObDock *s, XEvent *e)
1504 if (e->xbutton.button == 1)
1505 stacking_raise(DOCK_AS_WINDOW(s));
1506 else if (e->xbutton.button == 2)
1507 stacking_lower(DOCK_AS_WINDOW(s));
1513 /* don't hide when moving into a dock app */
1514 if (e->xcrossing.detail != NotifyInferior)
1520 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1524 dock_app_drag(app, &e->xmotion);
1527 if (app->ignore_unmaps) {
1528 app->ignore_unmaps--;
1531 dock_remove(app, TRUE);
1534 dock_remove(app, FALSE);
1536 case ReparentNotify:
1537 dock_remove(app, FALSE);
1539 case ConfigureNotify:
1540 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1545 static ObMenuFrame* find_active_menu()
1548 ObMenuFrame *ret = NULL;
1550 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1559 static ObMenuFrame* find_active_or_last_menu()
1561 ObMenuFrame *ret = NULL;
1563 ret = find_active_menu();
1564 if (!ret && menu_frame_visible)
1565 ret = menu_frame_visible->data;
1569 static gboolean event_handle_menu_keyboard(XEvent *ev)
1571 guint keycode, state;
1574 gboolean ret = TRUE;
1576 keycode = ev->xkey.keycode;
1577 state = ev->xkey.state;
1578 unikey = translate_unichar(keycode);
1580 frame = find_active_or_last_menu();
1584 else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0)
1585 menu_frame_hide_all();
1587 else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
1588 state == ControlMask))
1590 /* Enter runs the active item or goes into the submenu.
1591 Control-Enter runs it without closing the menu. */
1593 menu_frame_select_next(frame->child);
1594 else if (frame->selected)
1595 menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
1598 else if (keycode == ob_keycode(OB_KEY_LEFT) && ev->xkey.state == 0) {
1599 /* Left goes to the parent menu */
1600 menu_frame_select(frame, NULL, TRUE);
1603 else if (keycode == ob_keycode(OB_KEY_RIGHT) && ev->xkey.state == 0) {
1604 /* Right goes to the selected submenu */
1605 if (frame->child) menu_frame_select_next(frame->child);
1608 else if (keycode == ob_keycode(OB_KEY_UP) && state == 0) {
1609 menu_frame_select_previous(frame);
1612 else if (keycode == ob_keycode(OB_KEY_DOWN) && state == 0) {
1613 menu_frame_select_next(frame);
1616 /* keyboard accelerator shortcuts. (allow controlmask) */
1617 else if ((ev->xkey.state & ~ControlMask) == 0 &&
1618 /* was it a valid key? */
1620 /* don't bother if the menu is empty. */
1625 ObMenuEntryFrame *found = NULL;
1626 guint num_found = 0;
1628 /* start after the selected one */
1629 start = frame->entries;
1630 if (frame->selected) {
1631 for (it = start; frame->selected != it->data; it = g_list_next(it))
1632 g_assert(it != NULL); /* nothing was selected? */
1633 /* next with wraparound */
1634 start = g_list_next(it);
1635 if (start == NULL) start = frame->entries;
1640 ObMenuEntryFrame *e = it->data;
1641 gunichar entrykey = 0;
1643 if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
1644 entrykey = e->entry->data.normal.shortcut;
1645 else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1646 entrykey = e->entry->data.submenu.submenu->shortcut;
1648 if (unikey == entrykey) {
1649 if (found == NULL) found = e;
1653 /* next with wraparound */
1654 it = g_list_next(it);
1655 if (it == NULL) it = frame->entries;
1656 } while (it != start);
1659 if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1662 menu_frame_select(frame, found, TRUE);
1663 usleep(50000); /* highlight the item for a short bit so the
1664 user can see what happened */
1665 menu_entry_frame_execute(found, state, ev->xkey.time);
1667 menu_frame_select(frame, found, TRUE);
1669 menu_frame_select_next(frame->child);
1680 static gboolean event_handle_menu(XEvent *ev)
1683 ObMenuEntryFrame *e;
1684 gboolean ret = TRUE;
1688 if (menu_hide_delay_reached() &&
1689 (ev->xbutton.button < 4 || ev->xbutton.button > 5))
1691 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1692 ev->xbutton.y_root)))
1694 menu_frame_select(e->frame, e, TRUE);
1695 menu_entry_frame_execute(e, ev->xbutton.state,
1699 menu_frame_hide_all();
1703 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1704 if (e->ignore_enters)
1706 else if (!(f = find_active_menu()) ||
1708 f->parent == e->frame ||
1709 f->child == e->frame)
1710 menu_frame_select(e->frame, e, FALSE);
1714 /*ignore leaves when we're already in the window */
1715 if (ev->xcrossing.detail == NotifyInferior)
1718 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1719 (f = find_active_menu()) && f->selected == e &&
1720 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1722 menu_frame_select(e->frame, NULL, FALSE);
1726 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1727 ev->xmotion.y_root)))
1728 if (!(f = find_active_menu()) ||
1730 f->parent == e->frame ||
1731 f->child == e->frame)
1732 menu_frame_select(e->frame, e, FALSE);
1735 ret = event_handle_menu_keyboard(ev);
1741 static void event_handle_user_input(ObClient *client, XEvent *e)
1743 g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1744 e->type == MotionNotify || e->type == KeyPress ||
1745 e->type == KeyRelease);
1747 if (menu_frame_visible) {
1748 if (event_handle_menu(e))
1749 /* don't use the event if the menu used it, but if the menu
1750 didn't use it and it's a keypress that is bound, it will
1751 close the menu and be used */
1755 /* if the keyboard interactive action uses the event then dont
1756 use it for bindings. likewise is moveresize uses the event. */
1757 if (!keyboard_process_interactive_grab(e, &client) &&
1758 !(moveresize_in_progress && moveresize_event(e)))
1760 if (moveresize_in_progress)
1761 /* make further actions work on the client being
1763 client = moveresize_client;
1765 if (e->type == ButtonPress ||
1766 e->type == ButtonRelease ||
1767 e->type == MotionNotify)
1769 /* the frame may not be "visible" but they can still click on it
1770 in the case where it is animating before disappearing */
1771 if (!client || !frame_iconify_animating(client->frame))
1772 mouse_event(client, e);
1773 } else if (e->type == KeyPress) {
1774 keyboard_event((focus_cycle_target ? focus_cycle_target :
1775 (client ? client : focus_client)), e);
1780 static void focus_delay_dest(gpointer data)
1785 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1787 const ObFocusDelayData *f1 = d1;
1788 return f1->client == d2;
1791 static gboolean focus_delay_func(gpointer data)
1793 ObFocusDelayData *d = data;
1794 Time old = event_curtime;
1796 event_curtime = d->time;
1797 if (focus_client != d->client) {
1798 if (client_focus(d->client) && config_focus_raise)
1799 stacking_raise(CLIENT_AS_WINDOW(d->client));
1801 event_curtime = old;
1802 return FALSE; /* no repeat */
1805 static void focus_delay_client_dest(ObClient *client, gpointer data)
1807 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1811 void event_halt_focus_delay()
1813 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1816 static Bool event_look_for_enters(Display *d, XEvent *e, XPointer arg)
1818 if (e->type == EnterNotify &&
1819 /* these types aren't used for focusing */
1820 !(e->xcrossing.mode == NotifyGrab ||
1821 e->xcrossing.mode == NotifyUngrab ||
1822 e->xcrossing.detail == NotifyInferior))
1826 /* found an enter for that leave, ignore it if it's going to
1828 win = g_hash_table_lookup(window_map, &e->xany.window);
1829 if (win && WINDOW_IS_CLIENT(win))
1830 ++ignore_enter_focus;
1832 return False; /* don't disrupt the queue order, just count them */
1835 void event_ignore_all_queued_enters()
1839 XSync(ob_display, FALSE);
1841 /* count the events without disrupting them */
1842 ignore_enter_focus = 0;
1843 XCheckIfEvent(ob_display, &e, event_look_for_enters, NULL);
1846 static gboolean is_enter_focus_event_ignored(XEvent *e)
1848 g_assert(e->type == EnterNotify &&
1849 !(e->xcrossing.mode == NotifyGrab ||
1850 e->xcrossing.mode == NotifyUngrab ||
1851 e->xcrossing.detail == NotifyInferior));
1853 ob_debug_type(OB_DEBUG_FOCUS, "# enters ignored: %d\n",
1854 ignore_enter_focus);
1856 if (ignore_enter_focus) {
1857 --ignore_enter_focus;
1863 void event_cancel_all_key_grabs()
1865 if (keyboard_interactively_grabbed())
1866 keyboard_interactive_cancel();
1867 else if (menu_frame_visible)
1868 menu_frame_hide_all();
1869 else if (grab_on_keyboard())
1872 /* If we don't have the keyboard grabbed, then ungrab it with
1873 XUngrabKeyboard, so that there is not a passive grab left
1874 on from the KeyPress. If the grab is left on, and focus
1875 moves during that time, it will be NotifyWhileGrabbed, and
1876 applications like to ignore those! */
1877 if (!keyboard_interactively_grabbed())
1878 XUngrabKeyboard(ob_display, CurrentTime);
1882 gboolean event_time_after(Time t1, Time t2)
1884 g_assert(t1 != CurrentTime);
1885 g_assert(t2 != CurrentTime);
1888 Timestamp values wrap around (after about 49.7 days). The server, given
1889 its current time is represented by timestamp T, always interprets
1890 timestamps from clients by treating half of the timestamp space as being
1891 later in time than T.
1892 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1895 /* TIME_HALF is half of the number space of a Time type variable */
1896 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1898 if (t2 >= TIME_HALF)
1899 /* t2 is in the second half so t1 might wrap around and be smaller than
1901 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1903 /* t2 is in the first half so t1 has to come after it */
1904 return t1 >= t2 && t1 < (t2 + TIME_HALF);