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 Ben 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.
32 #include "menuframe.h"
36 #include "framerender.h"
38 #include "moveresize.h"
41 #include "extensions.h"
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
48 #ifdef HAVE_SYS_SELECT_H
49 # include <sys/select.h>
55 # include <X11/XKBlib.h>
59 #include <X11/ICE/ICElib.h>
67 static void event_process(const XEvent *e, gpointer data);
68 static void event_client_dest(ObClient *client, gpointer data);
69 static void event_handle_root(XEvent *e);
70 static void event_handle_menu(XEvent *e);
71 static void event_handle_dock(ObDock *s, XEvent *e);
72 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
73 static void event_handle_client(ObClient *c, XEvent *e);
74 static void event_handle_group(ObGroup *g, XEvent *e);
76 static gboolean focus_delay_func(gpointer data);
77 static void focus_delay_client_dest(ObClient *client, gpointer data);
79 static gboolean menu_hide_delay_func(gpointer data);
81 /* The time for the current event being processed */
82 Time event_curtime = CurrentTime;
84 /*! The value of the mask for the NumLock modifier */
86 /*! The value of the mask for the ScrollLock modifier */
88 /*! The key codes for the modifier keys */
89 static XModifierKeymap *modmap;
90 /*! Table of the constant modifier masks */
91 static const gint mask_table[] = {
92 ShiftMask, LockMask, ControlMask, Mod1Mask,
93 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
95 static gint mask_table_size;
97 static guint ignore_enter_focus = 0;
99 static gboolean menu_can_hide;
102 static void ice_handler(gint fd, gpointer conn)
105 IceProcessMessages(conn, NULL, &b);
108 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
109 IcePointer *watch_data)
114 fd = IceConnectionNumber(conn);
115 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
117 ob_main_loop_fd_remove(ob_main_loop, fd);
123 void event_startup(gboolean reconfig)
125 if (reconfig) return;
127 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
129 /* get lock masks that are defined by the display (not constant) */
130 modmap = XGetModifierMapping(ob_display);
132 if (modmap && modmap->max_keypermod > 0) {
134 const size_t size = mask_table_size * modmap->max_keypermod;
135 /* get the values of the keyboard lock modifiers
136 Note: Caps lock is not retrieved the same way as Scroll and Num
137 lock since it doesn't need to be. */
138 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
139 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
142 for (cnt = 0; cnt < size; ++cnt) {
143 if (! modmap->modifiermap[cnt]) continue;
145 if (num_lock == modmap->modifiermap[cnt])
146 NumLockMask = mask_table[cnt / modmap->max_keypermod];
147 if (scroll_lock == modmap->modifiermap[cnt])
148 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
152 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
155 IceAddConnectionWatch(ice_watch, NULL);
158 client_add_destructor(focus_delay_client_dest, NULL);
159 client_add_destructor(event_client_dest, NULL);
162 void event_shutdown(gboolean reconfig)
164 if (reconfig) return;
167 IceRemoveConnectionWatch(ice_watch, NULL);
170 client_remove_destructor(focus_delay_client_dest);
171 client_remove_destructor(event_client_dest);
172 XFreeModifiermap(modmap);
175 static Window event_get_window(XEvent *e)
182 window = RootWindow(ob_display, ob_screen);
185 window = e->xmap.window;
188 window = e->xunmap.window;
191 window = e->xdestroywindow.window;
193 case ConfigureRequest:
194 window = e->xconfigurerequest.window;
196 case ConfigureNotify:
197 window = e->xconfigure.window;
201 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
202 switch (((XkbAnyEvent*)e)->xkb_type) {
204 window = ((XkbBellNotifyEvent*)e)->window;
210 window = e->xany.window;
215 static void event_set_curtime(XEvent *e)
217 Time t = CurrentTime;
219 /* grab the lasttime and hack up the state */
235 t = e->xproperty.time;
239 t = e->xcrossing.time;
242 /* if more event types are anticipated, get their timestamp
250 #define STRIP_MODS(s) \
251 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
252 /* kill off the Button1Mask etc, only want the modifiers */ \
253 s &= (ControlMask | ShiftMask | Mod1Mask | \
254 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
256 static void event_hack_mods(XEvent *e)
259 XkbStateRec xkb_state;
267 STRIP_MODS(e->xbutton.state);
270 STRIP_MODS(e->xkey.state);
273 STRIP_MODS(e->xkey.state);
274 /* remove from the state the mask of the modifier being released, if
275 it is a modifier key being released (this is a little ugly..) */
277 if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
278 e->xkey.state = xkb_state.compat_state;
282 kp = modmap->modifiermap;
283 for (i = 0; i < mask_table_size; ++i) {
284 for (k = 0; k < modmap->max_keypermod; ++k) {
285 if (*kp == e->xkey.keycode) { /* found the keycode */
286 /* remove the mask for it */
287 e->xkey.state &= ~mask_table[i];
288 /* cause the first loop to break; */
290 break; /* get outta here! */
297 STRIP_MODS(e->xmotion.state);
298 /* compress events */
301 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
303 e->xmotion.x_root = ce.xmotion.x_root;
304 e->xmotion.y_root = ce.xmotion.y_root;
311 static gboolean wanted_focusevent(XEvent *e)
313 gint mode = e->xfocus.mode;
314 gint detail = e->xfocus.detail;
316 if (e->type == FocusIn) {
318 /* These are ones we never want.. */
320 /* This means focus was given by a keyboard/mouse grab. */
321 if (mode == NotifyGrab)
323 /* This means focus was given back from a keyboard/mouse grab. */
324 if (mode == NotifyUngrab)
327 /* These are the ones we want.. */
329 /* This means focus moved from the root window to a client */
330 if (detail == NotifyVirtual)
332 /* This means focus moved from one client to another */
333 if (detail == NotifyNonlinearVirtual)
339 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)
348 /* These are the ones we want.. */
350 /* This means focus moved from a client to the root window */
351 if (detail == NotifyVirtual)
353 /* This means focus moved from one client to another */
354 if (detail == NotifyNonlinearVirtual)
362 static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
364 return e->type == FocusIn && wanted_focusevent(e);
367 static gboolean event_ignore(XEvent *e, ObClient *client)
372 if (e->xcrossing.detail == NotifyInferior)
377 /* I don't think this should ever happen with our event masks, but
378 if it does, we don't want it. */
381 if (!wanted_focusevent(e))
388 static void event_process(const XEvent *ec, gpointer data)
391 ObGroup *group = NULL;
392 ObClient *client = NULL;
394 ObDockApp *dockapp = NULL;
395 ObWindow *obwin = NULL;
397 ObEventData *ed = data;
399 /* make a copy we can mangle */
403 window = event_get_window(e);
404 if (!(e->type == PropertyNotify &&
405 (group = g_hash_table_lookup(group_map, &window))))
406 if ((obwin = g_hash_table_lookup(window_map, &window))) {
407 switch (obwin->type) {
409 dock = WINDOW_AS_DOCK(obwin);
412 dockapp = WINDOW_AS_DOCKAPP(obwin);
415 client = WINDOW_AS_CLIENT(obwin);
418 case Window_Internal:
419 /* not to be used for events */
420 g_assert_not_reached();
425 #if 1 /* focus debugging stuff */
426 if (e->type == FocusIn || e->type == FocusOut) {
427 gint mode = e->xfocus.mode;
428 gint detail = e->xfocus.detail;
429 Window window = e->xfocus.window;
430 if (detail == NotifyVirtual) {
431 ob_debug("FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
432 (e->type == FocusIn ? "IN" : "OUT"), window);
435 else if (detail == NotifyNonlinearVirtual) {
436 ob_debug("FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
437 (e->type == FocusIn ? "IN" : "OUT"), window);
441 ob_debug("UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
442 (e->type == FocusIn ? "IN" : "OUT"),
443 detail, mode, window);
447 event_set_curtime(e);
449 if (event_ignore(e, client)) {
456 /* deal with it in the kernel */
458 event_handle_group(group, e);
460 event_handle_client(client, e);
462 event_handle_dockapp(dockapp, e);
464 event_handle_dock(dock, e);
465 else if (window == RootWindow(ob_display, ob_screen))
466 event_handle_root(e);
467 else if (e->type == MapRequest)
468 client_manage(window);
469 else if (e->type == ConfigureRequest) {
470 /* unhandled configure requests must be used to configure the
474 xwc.x = e->xconfigurerequest.x;
475 xwc.y = e->xconfigurerequest.y;
476 xwc.width = e->xconfigurerequest.width;
477 xwc.height = e->xconfigurerequest.height;
478 xwc.border_width = e->xconfigurerequest.border_width;
479 xwc.sibling = e->xconfigurerequest.above;
480 xwc.stack_mode = e->xconfigurerequest.detail;
482 /* we are not to be held responsible if someone sends us an
484 xerror_set_ignore(TRUE);
485 XConfigureWindow(ob_display, window,
486 e->xconfigurerequest.value_mask, &xwc);
487 xerror_set_ignore(FALSE);
490 /* user input (action-bound) events */
491 if (e->type == ButtonPress || e->type == ButtonRelease ||
492 e->type == MotionNotify || e->type == KeyPress ||
493 e->type == KeyRelease)
495 if (menu_frame_visible)
496 event_handle_menu(e);
498 if (!keyboard_process_interactive_grab(e, &client)) {
499 if (moveresize_in_progress) {
502 /* make further actions work on the client being
504 client = moveresize_client;
507 menu_can_hide = FALSE;
508 ob_main_loop_timeout_add(ob_main_loop,
509 config_menu_hide_delay * 1000,
510 menu_hide_delay_func,
513 if (e->type == ButtonPress || e->type == ButtonRelease ||
514 e->type == MotionNotify)
515 mouse_event(client, e);
516 else if (e->type == KeyPress) {
517 keyboard_event((focus_cycle_target ? focus_cycle_target :
518 (focus_hilite ? focus_hilite : client)),
524 /* if something happens and it's not from an XEvent, then we don't know
526 event_curtime = CurrentTime;
529 static void event_handle_root(XEvent *e)
535 ob_debug("Another WM has requested to replace us. Exiting.\n");
540 if (e->xclient.format != 32) break;
542 msgtype = e->xclient.message_type;
543 if (msgtype == prop_atoms.net_current_desktop) {
544 guint d = e->xclient.data.l[0];
545 event_curtime = e->xclient.data.l[1];
546 if (d < screen_num_desktops)
547 screen_set_desktop(d);
548 } else if (msgtype == prop_atoms.net_number_of_desktops) {
549 guint d = e->xclient.data.l[0];
551 screen_set_num_desktops(d);
552 } else if (msgtype == prop_atoms.net_showing_desktop) {
553 screen_show_desktop(e->xclient.data.l[0] != 0);
554 } else if (msgtype == prop_atoms.ob_control) {
555 if (e->xclient.data.l[0] == 1)
557 else if (e->xclient.data.l[0] == 2)
562 if (e->xproperty.atom == prop_atoms.net_desktop_names)
563 screen_update_desktop_names();
564 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
565 screen_update_layout();
567 case ConfigureNotify:
569 XRRUpdateConfiguration(e);
578 static void event_handle_group(ObGroup *group, XEvent *e)
582 g_assert(e->type == PropertyNotify);
584 for (it = group->members; it; it = g_slist_next(it))
585 event_handle_client(it->data, e);
588 void event_enter_client(ObClient *client)
590 g_assert(config_focus_follow);
592 if (client_normal(client) && client_can_focus(client)) {
593 if (config_focus_delay) {
594 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
595 ob_main_loop_timeout_add(ob_main_loop,
600 focus_delay_func(client);
604 static void event_handle_client(ObClient *client, XEvent *e)
612 case VisibilityNotify:
613 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
617 /* Wheel buttons don't draw because they are an instant click, so it
618 is a waste of resources to go drawing it. */
619 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
620 con = frame_context(client, e->xbutton.window);
621 con = mouse_button_frame_context(con, e->xbutton.button);
623 case OB_FRAME_CONTEXT_MAXIMIZE:
624 client->frame->max_press = (e->type == ButtonPress);
625 framerender_frame(client->frame);
627 case OB_FRAME_CONTEXT_CLOSE:
628 client->frame->close_press = (e->type == ButtonPress);
629 framerender_frame(client->frame);
631 case OB_FRAME_CONTEXT_ICONIFY:
632 client->frame->iconify_press = (e->type == ButtonPress);
633 framerender_frame(client->frame);
635 case OB_FRAME_CONTEXT_ALLDESKTOPS:
636 client->frame->desk_press = (e->type == ButtonPress);
637 framerender_frame(client->frame);
639 case OB_FRAME_CONTEXT_SHADE:
640 client->frame->shade_press = (e->type == ButtonPress);
641 framerender_frame(client->frame);
644 /* nothing changes with clicks for any other contexts */
650 if (client != focus_client) {
651 focus_set_client(client);
652 frame_adjust_focus(client->frame, TRUE);
653 client_calc_layer(client);
657 /* Look for the followup FocusIn */
658 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
659 /* There is no FocusIn, this means focus went to a window that
660 is not being managed. most likely, this went to PointerRoot
661 or None, meaning the window is no longer around so fallback
662 focus, but not to that window */
663 ob_debug("Focus went to a black hole !\n");
664 focus_fallback(FALSE);
665 } else if (ce.xany.window == e->xany.window) {
666 /* If focus didn't actually move anywhere, there is nothing to do*/
669 /* Focus did move, so process the FocusIn event */
671 event_process(&ce, &ed);
673 /* The FocusIn was ignored, this means it was on a window
674 that isn't a client. */
675 ob_debug("Focus went to an unmanaged window 0x%x !\n",
677 focus_fallback(TRUE);
681 /* This client is no longer focused, so show that */
683 frame_adjust_focus(client->frame, FALSE);
684 client_calc_layer(client);
687 con = frame_context(client, e->xcrossing.window);
689 case OB_FRAME_CONTEXT_MAXIMIZE:
690 client->frame->max_hover = FALSE;
691 frame_adjust_state(client->frame);
693 case OB_FRAME_CONTEXT_ALLDESKTOPS:
694 client->frame->desk_hover = FALSE;
695 frame_adjust_state(client->frame);
697 case OB_FRAME_CONTEXT_SHADE:
698 client->frame->shade_hover = FALSE;
699 frame_adjust_state(client->frame);
701 case OB_FRAME_CONTEXT_ICONIFY:
702 client->frame->iconify_hover = FALSE;
703 frame_adjust_state(client->frame);
705 case OB_FRAME_CONTEXT_CLOSE:
706 client->frame->close_hover = FALSE;
707 frame_adjust_state(client->frame);
709 case OB_FRAME_CONTEXT_FRAME:
710 if (config_focus_follow && config_focus_delay)
711 ob_main_loop_timeout_remove_data(ob_main_loop,
721 gboolean nofocus = FALSE;
723 if (ignore_enter_focus) {
724 ignore_enter_focus--;
728 con = frame_context(client, e->xcrossing.window);
730 case OB_FRAME_CONTEXT_MAXIMIZE:
731 client->frame->max_hover = TRUE;
732 frame_adjust_state(client->frame);
734 case OB_FRAME_CONTEXT_ALLDESKTOPS:
735 client->frame->desk_hover = TRUE;
736 frame_adjust_state(client->frame);
738 case OB_FRAME_CONTEXT_SHADE:
739 client->frame->shade_hover = TRUE;
740 frame_adjust_state(client->frame);
742 case OB_FRAME_CONTEXT_ICONIFY:
743 client->frame->iconify_hover = TRUE;
744 frame_adjust_state(client->frame);
746 case OB_FRAME_CONTEXT_CLOSE:
747 client->frame->close_hover = TRUE;
748 frame_adjust_state(client->frame);
750 case OB_FRAME_CONTEXT_FRAME:
751 if (e->xcrossing.mode == NotifyGrab ||
752 e->xcrossing.mode == NotifyUngrab)
755 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
756 (e->type == EnterNotify ? "Enter" : "Leave"),
758 e->xcrossing.detail, client?client->window:0);
762 ob_debug("%sNotify mode %d detail %d on %lx, "
763 "focusing window: %d\n",
764 (e->type == EnterNotify ? "Enter" : "Leave"),
766 e->xcrossing.detail, (client?client->window:0),
769 if (!nofocus && config_focus_follow)
770 event_enter_client(client);
778 case ConfigureRequest:
780 while (XCheckTypedWindowEvent(ob_display, client->window,
781 ConfigureRequest, &ce)) {
783 /* XXX if this causes bad things.. we can compress config req's
784 with the same mask. */
785 e->xconfigurerequest.value_mask |=
786 ce.xconfigurerequest.value_mask;
787 if (ce.xconfigurerequest.value_mask & CWX)
788 e->xconfigurerequest.x = ce.xconfigurerequest.x;
789 if (ce.xconfigurerequest.value_mask & CWY)
790 e->xconfigurerequest.y = ce.xconfigurerequest.y;
791 if (ce.xconfigurerequest.value_mask & CWWidth)
792 e->xconfigurerequest.width = ce.xconfigurerequest.width;
793 if (ce.xconfigurerequest.value_mask & CWHeight)
794 e->xconfigurerequest.height = ce.xconfigurerequest.height;
795 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
796 e->xconfigurerequest.border_width =
797 ce.xconfigurerequest.border_width;
798 if (ce.xconfigurerequest.value_mask & CWStackMode)
799 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
802 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
803 if (client->iconic || client->shaded) return;
805 /* resize, then move, as specified in the EWMH section 7.7 */
806 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
812 if (e->xconfigurerequest.value_mask & CWBorderWidth)
813 client->border_width = e->xconfigurerequest.border_width;
815 x = (e->xconfigurerequest.value_mask & CWX) ?
816 e->xconfigurerequest.x : client->area.x;
817 y = (e->xconfigurerequest.value_mask & CWY) ?
818 e->xconfigurerequest.y : client->area.y;
819 w = (e->xconfigurerequest.value_mask & CWWidth) ?
820 e->xconfigurerequest.width : client->area.width;
821 h = (e->xconfigurerequest.value_mask & CWHeight) ?
822 e->xconfigurerequest.height : client->area.height;
828 client->frame->size.left + client->frame->size.right;
830 client->frame->size.top + client->frame->size.bottom;
831 /* make this rude for size-only changes but not for position
833 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
834 (e->xconfigurerequest.value_mask & CWY));
836 client_find_onscreen(client, &newx, &newy, fw, fh,
838 if (e->xconfigurerequest.value_mask & CWX)
840 if (e->xconfigurerequest.value_mask & CWY)
844 switch (client->gravity) {
845 case NorthEastGravity:
847 corner = OB_CORNER_TOPRIGHT;
849 case SouthWestGravity:
851 corner = OB_CORNER_BOTTOMLEFT;
853 case SouthEastGravity:
854 corner = OB_CORNER_BOTTOMRIGHT;
856 default: /* NorthWest, Static, etc */
857 corner = OB_CORNER_TOPLEFT;
860 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
864 if (e->xconfigurerequest.value_mask & CWStackMode) {
865 switch (e->xconfigurerequest.detail) {
868 /* Apps are so rude. And this is totally disconnected from
869 activation/focus. Bleh. */
870 /*client_lower(client);*/
876 /* Apps are so rude. And this is totally disconnected from
877 activation/focus. Bleh. */
878 /*client_raise(client);*/
884 ob_debug("UnmapNotify for window 0x%x\n", client->window);
885 if (client->ignore_unmaps) {
886 client->ignore_unmaps--;
889 client_unmanage(client);
892 ob_debug("DestroyNotify for window 0x%x\n", client->window);
893 client_unmanage(client);
896 /* this is when the client is first taken captive in the frame */
897 if (e->xreparent.parent == client->frame->plate) break;
900 This event is quite rare and is usually handled in unmapHandler.
901 However, if the window is unmapped when the reparent event occurs,
902 the window manager never sees it because an unmap event is not sent
903 to an already unmapped window.
906 /* we don't want the reparent event, put it back on the stack for the
907 X server to deal with after we unmanage the window */
908 XPutBackEvent(ob_display, e);
910 client_unmanage(client);
913 ob_debug("MapRequest for 0x%lx\n", client->window);
914 if (!client->iconic) break; /* this normally doesn't happen, but if it
915 does, we don't want it!
916 it can happen now when the window is on
917 another desktop, but we still don't
919 client_activate(client, FALSE, TRUE);
922 /* validate cuz we query stuff off the client here */
923 if (!client_validate(client)) break;
925 if (e->xclient.format != 32) return;
927 msgtype = e->xclient.message_type;
928 if (msgtype == prop_atoms.wm_change_state) {
929 /* compress changes into a single change */
930 while (XCheckTypedWindowEvent(ob_display, client->window,
932 /* XXX: it would be nice to compress ALL messages of a
933 type, not just messages in a row without other
934 message types between. */
935 if (ce.xclient.message_type != msgtype) {
936 XPutBackEvent(ob_display, &ce);
939 e->xclient = ce.xclient;
941 client_set_wm_state(client, e->xclient.data.l[0]);
942 } else if (msgtype == prop_atoms.net_wm_desktop) {
943 /* compress changes into a single change */
944 while (XCheckTypedWindowEvent(ob_display, client->window,
946 /* XXX: it would be nice to compress ALL messages of a
947 type, not just messages in a row without other
948 message types between. */
949 if (ce.xclient.message_type != msgtype) {
950 XPutBackEvent(ob_display, &ce);
953 e->xclient = ce.xclient;
955 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
956 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
957 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
959 } else if (msgtype == prop_atoms.net_wm_state) {
960 /* can't compress these */
961 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
962 (e->xclient.data.l[0] == 0 ? "Remove" :
963 e->xclient.data.l[0] == 1 ? "Add" :
964 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
965 e->xclient.data.l[1], e->xclient.data.l[2],
967 client_set_state(client, e->xclient.data.l[0],
968 e->xclient.data.l[1], e->xclient.data.l[2]);
969 } else if (msgtype == prop_atoms.net_close_window) {
970 ob_debug("net_close_window for 0x%lx\n", client->window);
971 client_close(client);
972 } else if (msgtype == prop_atoms.net_active_window) {
973 ob_debug("net_active_window for 0x%lx source=%s\n",
975 (e->xclient.data.l[0] == 0 ? "unknown" :
976 (e->xclient.data.l[0] == 1 ? "application" :
977 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
978 /* XXX make use of data.l[2] ! */
979 event_curtime = e->xclient.data.l[1];
980 client_activate(client, FALSE,
981 (e->xclient.data.l[0] == 0 ||
982 e->xclient.data.l[0] == 2));
983 } else if (msgtype == prop_atoms.net_wm_moveresize) {
984 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
985 client->window, e->xclient.data.l[2]);
986 if ((Atom)e->xclient.data.l[2] ==
987 prop_atoms.net_wm_moveresize_size_topleft ||
988 (Atom)e->xclient.data.l[2] ==
989 prop_atoms.net_wm_moveresize_size_top ||
990 (Atom)e->xclient.data.l[2] ==
991 prop_atoms.net_wm_moveresize_size_topright ||
992 (Atom)e->xclient.data.l[2] ==
993 prop_atoms.net_wm_moveresize_size_right ||
994 (Atom)e->xclient.data.l[2] ==
995 prop_atoms.net_wm_moveresize_size_right ||
996 (Atom)e->xclient.data.l[2] ==
997 prop_atoms.net_wm_moveresize_size_bottomright ||
998 (Atom)e->xclient.data.l[2] ==
999 prop_atoms.net_wm_moveresize_size_bottom ||
1000 (Atom)e->xclient.data.l[2] ==
1001 prop_atoms.net_wm_moveresize_size_bottomleft ||
1002 (Atom)e->xclient.data.l[2] ==
1003 prop_atoms.net_wm_moveresize_size_left ||
1004 (Atom)e->xclient.data.l[2] ==
1005 prop_atoms.net_wm_moveresize_move ||
1006 (Atom)e->xclient.data.l[2] ==
1007 prop_atoms.net_wm_moveresize_size_keyboard ||
1008 (Atom)e->xclient.data.l[2] ==
1009 prop_atoms.net_wm_moveresize_move_keyboard) {
1011 moveresize_start(client, e->xclient.data.l[0],
1012 e->xclient.data.l[1], e->xclient.data.l[3],
1013 e->xclient.data.l[2]);
1015 else if ((Atom)e->xclient.data.l[2] ==
1016 prop_atoms.net_wm_moveresize_cancel)
1017 moveresize_end(TRUE);
1018 } else if (msgtype == prop_atoms.net_moveresize_window) {
1019 gint oldg = client->gravity;
1020 gint tmpg, x, y, w, h;
1022 if (e->xclient.data.l[0] & 0xff)
1023 tmpg = e->xclient.data.l[0] & 0xff;
1027 if (e->xclient.data.l[0] & 1 << 8)
1028 x = e->xclient.data.l[1];
1031 if (e->xclient.data.l[0] & 1 << 9)
1032 y = e->xclient.data.l[2];
1035 if (e->xclient.data.l[0] & 1 << 10)
1036 w = e->xclient.data.l[3];
1038 w = client->area.width;
1039 if (e->xclient.data.l[0] & 1 << 11)
1040 h = e->xclient.data.l[4];
1042 h = client->area.height;
1043 client->gravity = tmpg;
1049 client->frame->size.left + client->frame->size.right;
1051 client->frame->size.top + client->frame->size.bottom;
1052 client_find_onscreen(client, &newx, &newy, fw, fh,
1053 client_normal(client));
1054 if (e->xclient.data.l[0] & 1 << 8)
1056 if (e->xclient.data.l[0] & 1 << 9)
1060 client_configure(client, OB_CORNER_TOPLEFT,
1061 x, y, w, h, FALSE, TRUE);
1063 client->gravity = oldg;
1066 case PropertyNotify:
1067 /* validate cuz we query stuff off the client here */
1068 if (!client_validate(client)) break;
1070 /* compress changes to a single property into a single change */
1071 while (XCheckTypedWindowEvent(ob_display, client->window,
1075 /* XXX: it would be nice to compress ALL changes to a property,
1076 not just changes in a row without other props between. */
1078 a = ce.xproperty.atom;
1079 b = e->xproperty.atom;
1083 if ((a == prop_atoms.net_wm_name ||
1084 a == prop_atoms.wm_name ||
1085 a == prop_atoms.net_wm_icon_name ||
1086 a == prop_atoms.wm_icon_name)
1088 (b == prop_atoms.net_wm_name ||
1089 b == prop_atoms.wm_name ||
1090 b == prop_atoms.net_wm_icon_name ||
1091 b == prop_atoms.wm_icon_name)) {
1094 if (a == prop_atoms.net_wm_icon &&
1095 b == prop_atoms.net_wm_icon)
1098 XPutBackEvent(ob_display, &ce);
1102 msgtype = e->xproperty.atom;
1103 if (msgtype == XA_WM_NORMAL_HINTS) {
1104 client_update_normal_hints(client);
1105 /* normal hints can make a window non-resizable */
1106 client_setup_decor_and_functions(client);
1107 } else if (msgtype == XA_WM_HINTS) {
1108 client_update_wmhints(client);
1109 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1110 client_update_transient_for(client);
1111 client_get_type(client);
1112 /* type may have changed, so update the layer */
1113 client_calc_layer(client);
1114 client_setup_decor_and_functions(client);
1115 } else if (msgtype == prop_atoms.net_wm_name ||
1116 msgtype == prop_atoms.wm_name ||
1117 msgtype == prop_atoms.net_wm_icon_name ||
1118 msgtype == prop_atoms.wm_icon_name) {
1119 client_update_title(client);
1120 } else if (msgtype == prop_atoms.wm_class) {
1121 client_update_class(client);
1122 } else if (msgtype == prop_atoms.wm_protocols) {
1123 client_update_protocols(client);
1124 client_setup_decor_and_functions(client);
1126 else if (msgtype == prop_atoms.net_wm_strut) {
1127 client_update_strut(client);
1129 else if (msgtype == prop_atoms.net_wm_icon) {
1130 client_update_icons(client);
1132 else if (msgtype == prop_atoms.net_wm_user_time) {
1133 client_update_user_time(client, TRUE);
1135 else if (msgtype == prop_atoms.sm_client_id) {
1136 client_update_sm_client_id(client);
1141 if (extensions_shape && e->type == extensions_shape_event_basep) {
1142 client->shaped = ((XShapeEvent*)e)->shaped;
1143 frame_adjust_shape(client->frame);
1149 static void event_handle_dock(ObDock *s, XEvent *e)
1153 if (e->xbutton.button == 1)
1154 stacking_raise(DOCK_AS_WINDOW(s));
1155 else if (e->xbutton.button == 2)
1156 stacking_lower(DOCK_AS_WINDOW(s));
1167 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1171 dock_app_drag(app, &e->xmotion);
1174 if (app->ignore_unmaps) {
1175 app->ignore_unmaps--;
1178 dock_remove(app, TRUE);
1181 dock_remove(app, FALSE);
1183 case ReparentNotify:
1184 dock_remove(app, FALSE);
1186 case ConfigureNotify:
1187 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1192 ObMenuFrame* find_active_menu()
1195 ObMenuFrame *ret = NULL;
1197 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1206 ObMenuFrame* find_active_or_last_menu()
1208 ObMenuFrame *ret = NULL;
1210 ret = find_active_menu();
1211 if (!ret && menu_frame_visible)
1212 ret = menu_frame_visible->data;
1216 static void event_handle_menu(XEvent *ev)
1219 ObMenuEntryFrame *e;
1223 if (menu_can_hide) {
1224 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1225 ev->xbutton.y_root)))
1226 menu_entry_frame_execute(e, ev->xbutton.state,
1229 menu_frame_hide_all();
1233 if ((f = menu_frame_under(ev->xmotion.x_root,
1234 ev->xmotion.y_root))) {
1235 menu_frame_move_on_screen(f);
1236 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1237 ev->xmotion.y_root)))
1238 menu_frame_select(f, e);
1243 a = find_active_menu();
1245 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1247 menu_frame_select(a, NULL);
1252 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1253 menu_frame_hide_all();
1254 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1256 if ((f = find_active_menu()))
1257 menu_entry_frame_execute(f->selected, ev->xkey.state,
1259 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1261 if ((f = find_active_or_last_menu()) && f->parent)
1262 menu_frame_select(f, NULL);
1263 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1265 if ((f = find_active_or_last_menu()) && f->child)
1266 menu_frame_select_next(f->child);
1267 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1269 if ((f = find_active_or_last_menu()))
1270 menu_frame_select_previous(f);
1271 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1273 if ((f = find_active_or_last_menu()))
1274 menu_frame_select_next(f);
1280 static gboolean menu_hide_delay_func(gpointer data)
1282 menu_can_hide = TRUE;
1283 return FALSE; /* no repeat */
1286 static gboolean focus_delay_func(gpointer data)
1290 if (focus_client != c) {
1291 if (client_validate(c)) {
1293 if (config_focus_raise)
1297 return FALSE; /* no repeat */
1300 static void focus_delay_client_dest(ObClient *client, gpointer data)
1302 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1306 static void event_client_dest(ObClient *client, gpointer data)
1308 if (client == focus_hilite)
1309 focus_hilite = NULL;
1312 void event_halt_focus_delay()
1314 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1317 void event_ignore_queued_enters()
1319 GSList *saved = NULL, *it;
1322 XSync(ob_display, FALSE);
1324 /* count the events */
1326 e = g_new(XEvent, 1);
1327 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1330 win = g_hash_table_lookup(window_map, &e->xany.window);
1331 if (win && WINDOW_IS_CLIENT(win))
1332 ++ignore_enter_focus;
1334 saved = g_slist_append(saved, e);
1340 /* put the events back */
1341 for (it = saved; it; it = g_slist_next(it)) {
1342 XPutBackEvent(ob_display, it->data);
1345 g_slist_free(saved);