1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
31 #include "menuframe.h"
35 #include "framerender.h"
37 #include "moveresize.h"
40 #include "extensions.h"
43 #include <X11/keysym.h>
44 #include <X11/Xatom.h>
47 #ifdef HAVE_SYS_SELECT_H
48 # include <sys/select.h>
55 #include <X11/ICE/ICElib.h>
58 static void event_process(const XEvent *e, gpointer data);
59 static void event_done(gpointer data);
60 static void event_handle_root(XEvent *e);
61 static void event_handle_menu(XEvent *e);
62 static void event_handle_dock(ObDock *s, XEvent *e);
63 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
64 static void event_handle_client(ObClient *c, XEvent *e);
65 static void event_handle_group(ObGroup *g, XEvent *e);
67 static gboolean focus_delay_func(gpointer data);
68 static void focus_delay_client_dest(ObClient *client, gpointer data);
70 static gboolean menu_hide_delay_func(gpointer data);
72 Time event_lasttime = 0;
74 /*! The value of the mask for the NumLock modifier */
75 unsigned int NumLockMask;
76 /*! The value of the mask for the ScrollLock modifier */
77 unsigned int ScrollLockMask;
78 /*! The key codes for the modifier keys */
79 static XModifierKeymap *modmap;
80 /*! Table of the constant modifier masks */
81 static const int mask_table[] = {
82 ShiftMask, LockMask, ControlMask, Mod1Mask,
83 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
85 static int mask_table_size;
87 static guint ignore_enter_focus = 0;
89 static gboolean menu_can_hide;
91 static ObClient *focus_in, *focus_out;
94 static void ice_handler(int fd, gpointer conn)
97 IceProcessMessages(conn, NULL, &b);
100 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
101 IcePointer *watch_data)
106 fd = IceConnectionNumber(conn);
107 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
109 ob_main_loop_fd_remove(ob_main_loop, fd);
115 void event_startup(gboolean reconfig)
117 if (reconfig) return;
119 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
121 /* get lock masks that are defined by the display (not constant) */
122 modmap = XGetModifierMapping(ob_display);
124 if (modmap && modmap->max_keypermod > 0) {
126 const size_t size = mask_table_size * modmap->max_keypermod;
127 /* get the values of the keyboard lock modifiers
128 Note: Caps lock is not retrieved the same way as Scroll and Num
129 lock since it doesn't need to be. */
130 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
131 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
134 for (cnt = 0; cnt < size; ++cnt) {
135 if (! modmap->modifiermap[cnt]) continue;
137 if (num_lock == modmap->modifiermap[cnt])
138 NumLockMask = mask_table[cnt / modmap->max_keypermod];
139 if (scroll_lock == modmap->modifiermap[cnt])
140 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
144 ob_main_loop_x_add(ob_main_loop, event_process, event_done, NULL, NULL);
147 IceAddConnectionWatch(ice_watch, NULL);
150 client_add_destructor(focus_delay_client_dest, NULL);
153 void event_shutdown(gboolean reconfig)
155 if (reconfig) return;
158 IceRemoveConnectionWatch(ice_watch, NULL);
161 client_remove_destructor(focus_delay_client_dest);
162 XFreeModifiermap(modmap);
165 static Window event_get_window(XEvent *e)
172 window = RootWindow(ob_display, ob_screen);
175 window = e->xmap.window;
178 window = e->xunmap.window;
181 window = e->xdestroywindow.window;
183 case ConfigureRequest:
184 window = e->xconfigurerequest.window;
186 case ConfigureNotify:
187 window = e->xconfigure.window;
191 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
192 switch (((XkbAnyEvent*)e)->xkb_type) {
194 window = ((XkbBellNotifyEvent*)e)->window;
200 window = e->xany.window;
205 static void event_set_lasttime(XEvent *e)
209 /* grab the lasttime and hack up the state */
225 t = e->xproperty.time;
229 t = e->xcrossing.time;
232 /* if more event types are anticipated, get their timestamp
237 if (t > event_lasttime)
241 #define STRIP_MODS(s) \
242 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
243 /* kill off the Button1Mask etc, only want the modifiers */ \
244 s &= (ControlMask | ShiftMask | Mod1Mask | \
245 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
247 static void event_hack_mods(XEvent *e)
255 STRIP_MODS(e->xbutton.state);
258 STRIP_MODS(e->xkey.state);
261 STRIP_MODS(e->xkey.state);
262 /* remove from the state the mask of the modifier being released, if
263 it is a modifier key being released (this is a little ugly..) */
264 kp = modmap->modifiermap;
265 for (i = 0; i < mask_table_size; ++i) {
266 for (k = 0; k < modmap->max_keypermod; ++k) {
267 if (*kp == e->xkey.keycode) { /* found the keycode */
268 /* remove the mask for it */
269 e->xkey.state &= ~mask_table[i];
270 /* cause the first loop to break; */
272 break; /* get outta here! */
279 STRIP_MODS(e->xmotion.state);
280 /* compress events */
283 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
285 e->xmotion.x_root = ce.xmotion.x_root;
286 e->xmotion.y_root = ce.xmotion.y_root;
293 static gboolean event_ignore(XEvent *e, ObClient *client)
298 if (e->xcrossing.detail == NotifyInferior)
302 if (e->xfocus.detail > NotifyNonlinearVirtual)
306 if (e->xfocus.detail > NotifyNonlinearVirtual)
308 if (e->xfocus.detail == NotifyInferior ||
309 e->xfocus.mode == NotifyGrab)
316 static void event_done(gpointer data)
318 static ObClient *last = NULL;
321 if (focus_in != focus_client) {
322 focus_set_client(focus_in);
323 frame_adjust_focus(focus_in->frame, TRUE);
324 client_calc_layer(focus_in);
328 if (focus_out == focus_client)
329 focus_set_client(NULL);
330 frame_adjust_focus(focus_out->frame, FALSE);
331 client_calc_layer(focus_out);
334 if (focus_client != last) {
336 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
340 focus_in = focus_out = NULL;
343 static void event_process(const XEvent *ec, gpointer data)
346 ObGroup *group = NULL;
347 ObClient *client = NULL;
349 ObDockApp *dockapp = NULL;
350 ObWindow *obwin = NULL;
353 /* make a copy we can mangle */
357 window = event_get_window(e);
358 if (!(e->type == PropertyNotify &&
359 (group = g_hash_table_lookup(group_map, &window))))
360 if ((obwin = g_hash_table_lookup(window_map, &window))) {
361 switch (obwin->type) {
363 dock = WINDOW_AS_DOCK(obwin);
366 dockapp = WINDOW_AS_DOCKAPP(obwin);
369 client = WINDOW_AS_CLIENT(obwin);
372 case Window_Internal:
373 /* not to be used for events */
374 g_assert_not_reached();
379 event_set_lasttime(e);
381 if (event_ignore(e, client))
384 /* deal with it in the kernel */
386 event_handle_group(group, e);
388 event_handle_client(client, e);
390 event_handle_dockapp(dockapp, e);
392 event_handle_dock(dock, e);
393 else if (window == RootWindow(ob_display, ob_screen))
394 event_handle_root(e);
395 else if (e->type == MapRequest)
396 client_manage(window);
397 else if (e->type == ConfigureRequest) {
398 /* unhandled configure requests must be used to configure the
402 xwc.x = e->xconfigurerequest.x;
403 xwc.y = e->xconfigurerequest.y;
404 xwc.width = e->xconfigurerequest.width;
405 xwc.height = e->xconfigurerequest.height;
406 xwc.border_width = e->xconfigurerequest.border_width;
407 xwc.sibling = e->xconfigurerequest.above;
408 xwc.stack_mode = e->xconfigurerequest.detail;
410 /* we are not to be held responsible if someone sends us an
412 xerror_set_ignore(TRUE);
413 XConfigureWindow(ob_display, window,
414 e->xconfigurerequest.value_mask, &xwc);
415 xerror_set_ignore(FALSE);
418 /* user input (action-bound) events */
419 if (e->type == ButtonPress || e->type == ButtonRelease ||
420 e->type == MotionNotify || e->type == KeyPress ||
421 e->type == KeyRelease)
423 if (menu_frame_visible)
424 event_handle_menu(e);
426 if (!keyboard_process_interactive_grab(e, &client)) {
427 if (moveresize_in_progress) {
430 /* make further actions work on the client being
432 client = moveresize_client;
435 menu_can_hide = FALSE;
436 ob_main_loop_timeout_add(ob_main_loop,
438 menu_hide_delay_func,
441 if (e->type == ButtonPress || e->type == ButtonRelease ||
442 e->type == MotionNotify)
443 mouse_event(client, e);
444 else if (e->type == KeyPress)
445 /* when in the middle of a focus cycling action, this
446 causes the window which appears to be focused to be
447 the one on which the actions will be executed */
448 keyboard_event((focus_cycle_target ?
449 focus_cycle_target : client), e);
455 static void event_handle_root(XEvent *e)
461 ob_debug("Another WM has requested to replace us. Exiting.\n");
466 if (e->xclient.format != 32) break;
468 msgtype = e->xclient.message_type;
469 if (msgtype == prop_atoms.net_current_desktop) {
470 unsigned int d = e->xclient.data.l[0];
471 if (d < screen_num_desktops)
472 screen_set_desktop(d);
473 } else if (msgtype == prop_atoms.net_number_of_desktops) {
474 unsigned int d = e->xclient.data.l[0];
476 screen_set_num_desktops(d);
477 } else if (msgtype == prop_atoms.net_showing_desktop) {
478 screen_show_desktop(e->xclient.data.l[0] != 0);
482 if (e->xproperty.atom == prop_atoms.net_desktop_names)
483 screen_update_desktop_names();
484 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
485 screen_update_layout();
487 case ConfigureNotify:
489 XRRUpdateConfiguration(e);
496 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
497 ob_debug("VIDMODE EVENT\n");
503 static void event_handle_group(ObGroup *group, XEvent *e)
507 g_assert(e->type == PropertyNotify);
509 for (it = group->members; it; it = g_slist_next(it))
510 event_handle_client(it->data, e);
513 void event_enter_client(ObClient *client)
515 g_assert(config_focus_follow);
517 if (client_normal(client) && client_can_focus(client)) {
518 if (config_focus_delay) {
519 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
520 ob_main_loop_timeout_add(ob_main_loop,
525 focus_delay_func(client);
529 static void event_handle_client(ObClient *client, XEvent *e)
537 case VisibilityNotify:
538 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
542 /* Wheel buttons don't draw because they are an instant click, so it
543 is a waste of resources to go drawing it. */
544 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
545 con = frame_context(client, e->xbutton.window);
546 con = mouse_button_frame_context(con, e->xbutton.button);
548 case OB_FRAME_CONTEXT_MAXIMIZE:
549 client->frame->max_press = (e->type == ButtonPress);
550 framerender_frame(client->frame);
552 case OB_FRAME_CONTEXT_CLOSE:
553 client->frame->close_press = (e->type == ButtonPress);
554 framerender_frame(client->frame);
556 case OB_FRAME_CONTEXT_ICONIFY:
557 client->frame->iconify_press = (e->type == ButtonPress);
558 framerender_frame(client->frame);
560 case OB_FRAME_CONTEXT_ALLDESKTOPS:
561 client->frame->desk_press = (e->type == ButtonPress);
562 framerender_frame(client->frame);
564 case OB_FRAME_CONTEXT_SHADE:
565 client->frame->shade_press = (e->type == ButtonPress);
566 framerender_frame(client->frame);
569 /* nothing changes with clicks for any other contexts */
576 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
577 e->xfocus.window, client->window, e->xfocus.mode, e->xfocus.detail);
580 if (focus_out == client)
585 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
586 e->xfocus.window, client->window, e->xfocus.mode, e->xfocus.detail);
588 if (focus_in == client)
590 if (client == focus_client)
594 con = frame_context(client, e->xcrossing.window);
596 case OB_FRAME_CONTEXT_MAXIMIZE:
597 client->frame->max_hover = FALSE;
598 frame_adjust_state(client->frame);
600 case OB_FRAME_CONTEXT_ALLDESKTOPS:
601 client->frame->desk_hover = FALSE;
602 frame_adjust_state(client->frame);
604 case OB_FRAME_CONTEXT_SHADE:
605 client->frame->shade_hover = FALSE;
606 frame_adjust_state(client->frame);
608 case OB_FRAME_CONTEXT_ICONIFY:
609 client->frame->iconify_hover = FALSE;
610 frame_adjust_state(client->frame);
612 case OB_FRAME_CONTEXT_CLOSE:
613 client->frame->close_hover = FALSE;
614 frame_adjust_state(client->frame);
616 case OB_FRAME_CONTEXT_FRAME:
618 if (config_focus_follow && config_focus_delay)
619 ob_main_loop_timeout_remove_data(ob_main_loop,
630 gboolean nofocus = FALSE;
632 if (ignore_enter_focus) {
633 ignore_enter_focus--;
637 con = frame_context(client, e->xcrossing.window);
639 case OB_FRAME_CONTEXT_MAXIMIZE:
640 client->frame->max_hover = TRUE;
641 frame_adjust_state(client->frame);
643 case OB_FRAME_CONTEXT_ALLDESKTOPS:
644 client->frame->desk_hover = TRUE;
645 frame_adjust_state(client->frame);
647 case OB_FRAME_CONTEXT_SHADE:
648 client->frame->shade_hover = TRUE;
649 frame_adjust_state(client->frame);
651 case OB_FRAME_CONTEXT_ICONIFY:
652 client->frame->iconify_hover = TRUE;
653 frame_adjust_state(client->frame);
655 case OB_FRAME_CONTEXT_CLOSE:
656 client->frame->close_hover = TRUE;
657 frame_adjust_state(client->frame);
659 case OB_FRAME_CONTEXT_FRAME:
660 if (e->xcrossing.mode == NotifyGrab ||
661 e->xcrossing.mode == NotifyUngrab)
664 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
665 (e->type == EnterNotify ? "Enter" : "Leave"),
667 e->xcrossing.detail, client?client->window:0);
671 ob_debug("%sNotify mode %d detail %d on %lx, "
673 (e->type == EnterNotify ? "Enter" : "Leave"),
675 e->xcrossing.detail, client?client->window:0);
677 if (!nofocus && config_focus_follow)
678 event_enter_client(client);
686 case ConfigureRequest:
688 while (XCheckTypedWindowEvent(ob_display, client->window,
689 ConfigureRequest, &ce)) {
691 /* XXX if this causes bad things.. we can compress config req's
692 with the same mask. */
693 e->xconfigurerequest.value_mask |=
694 ce.xconfigurerequest.value_mask;
695 if (ce.xconfigurerequest.value_mask & CWX)
696 e->xconfigurerequest.x = ce.xconfigurerequest.x;
697 if (ce.xconfigurerequest.value_mask & CWY)
698 e->xconfigurerequest.y = ce.xconfigurerequest.y;
699 if (ce.xconfigurerequest.value_mask & CWWidth)
700 e->xconfigurerequest.width = ce.xconfigurerequest.width;
701 if (ce.xconfigurerequest.value_mask & CWHeight)
702 e->xconfigurerequest.height = ce.xconfigurerequest.height;
703 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
704 e->xconfigurerequest.border_width =
705 ce.xconfigurerequest.border_width;
706 if (ce.xconfigurerequest.value_mask & CWStackMode)
707 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
710 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
711 if (client->iconic || client->shaded) return;
713 /* resize, then move, as specified in the EWMH section 7.7 */
714 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
720 if (e->xconfigurerequest.value_mask & CWBorderWidth)
721 client->border_width = e->xconfigurerequest.border_width;
723 x = (e->xconfigurerequest.value_mask & CWX) ?
724 e->xconfigurerequest.x : client->area.x;
725 y = (e->xconfigurerequest.value_mask & CWY) ?
726 e->xconfigurerequest.y : client->area.y;
727 w = (e->xconfigurerequest.value_mask & CWWidth) ?
728 e->xconfigurerequest.width : client->area.width;
729 h = (e->xconfigurerequest.value_mask & CWHeight) ?
730 e->xconfigurerequest.height : client->area.height;
736 client->frame->size.left + client->frame->size.right;
738 client->frame->size.top + client->frame->size.bottom;
739 client_find_onscreen(client, &newx, &newy, fw, fh,
740 client_normal(client));
741 if (e->xconfigurerequest.value_mask & CWX)
743 if (e->xconfigurerequest.value_mask & CWY)
747 switch (client->gravity) {
748 case NorthEastGravity:
750 corner = OB_CORNER_TOPRIGHT;
752 case SouthWestGravity:
754 corner = OB_CORNER_BOTTOMLEFT;
756 case SouthEastGravity:
757 corner = OB_CORNER_BOTTOMRIGHT;
759 default: /* NorthWest, Static, etc */
760 corner = OB_CORNER_TOPLEFT;
763 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
767 if (e->xconfigurerequest.value_mask & CWStackMode) {
768 switch (e->xconfigurerequest.detail) {
771 client_lower(client);
777 client_raise(client);
783 if (client->ignore_unmaps) {
784 client->ignore_unmaps--;
787 client_unmanage(client);
790 client_unmanage(client);
793 /* this is when the client is first taken captive in the frame */
794 if (e->xreparent.parent == client->frame->plate) break;
797 This event is quite rare and is usually handled in unmapHandler.
798 However, if the window is unmapped when the reparent event occurs,
799 the window manager never sees it because an unmap event is not sent
800 to an already unmapped window.
803 /* we don't want the reparent event, put it back on the stack for the
804 X server to deal with after we unmanage the window */
805 XPutBackEvent(ob_display, e);
807 client_unmanage(client);
810 ob_debug("MapRequest for 0x%lx\n", client->window);
811 if (!client->iconic) break; /* this normally doesn't happen, but if it
812 does, we don't want it! */
813 client_activate(client, FALSE);
816 /* validate cuz we query stuff off the client here */
817 if (!client_validate(client)) break;
819 if (e->xclient.format != 32) return;
821 msgtype = e->xclient.message_type;
822 if (msgtype == prop_atoms.wm_change_state) {
823 /* compress changes into a single change */
824 while (XCheckTypedWindowEvent(ob_display, client->window,
826 /* XXX: it would be nice to compress ALL messages of a
827 type, not just messages in a row without other
828 message types between. */
829 if (ce.xclient.message_type != msgtype) {
830 XPutBackEvent(ob_display, &ce);
833 e->xclient = ce.xclient;
835 client_set_wm_state(client, e->xclient.data.l[0]);
836 } else if (msgtype == prop_atoms.net_wm_desktop) {
837 /* compress changes into a single change */
838 while (XCheckTypedWindowEvent(ob_display, client->window,
840 /* XXX: it would be nice to compress ALL messages of a
841 type, not just messages in a row without other
842 message types between. */
843 if (ce.xclient.message_type != msgtype) {
844 XPutBackEvent(ob_display, &ce);
847 e->xclient = ce.xclient;
849 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
850 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
851 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
853 } else if (msgtype == prop_atoms.net_wm_state) {
854 /* can't compress these */
855 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
856 (e->xclient.data.l[0] == 0 ? "Remove" :
857 e->xclient.data.l[0] == 1 ? "Add" :
858 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
859 e->xclient.data.l[1], e->xclient.data.l[2],
861 client_set_state(client, e->xclient.data.l[0],
862 e->xclient.data.l[1], e->xclient.data.l[2]);
863 } else if (msgtype == prop_atoms.net_close_window) {
864 ob_debug("net_close_window for 0x%lx\n", client->window);
865 client_close(client);
866 } else if (msgtype == prop_atoms.net_active_window) {
867 ob_debug("net_active_window for 0x%lx\n", client->window);
868 client_activate(client, FALSE);
869 } else if (msgtype == prop_atoms.net_wm_moveresize) {
870 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
871 if ((Atom)e->xclient.data.l[2] ==
872 prop_atoms.net_wm_moveresize_size_topleft ||
873 (Atom)e->xclient.data.l[2] ==
874 prop_atoms.net_wm_moveresize_size_top ||
875 (Atom)e->xclient.data.l[2] ==
876 prop_atoms.net_wm_moveresize_size_topright ||
877 (Atom)e->xclient.data.l[2] ==
878 prop_atoms.net_wm_moveresize_size_right ||
879 (Atom)e->xclient.data.l[2] ==
880 prop_atoms.net_wm_moveresize_size_right ||
881 (Atom)e->xclient.data.l[2] ==
882 prop_atoms.net_wm_moveresize_size_bottomright ||
883 (Atom)e->xclient.data.l[2] ==
884 prop_atoms.net_wm_moveresize_size_bottom ||
885 (Atom)e->xclient.data.l[2] ==
886 prop_atoms.net_wm_moveresize_size_bottomleft ||
887 (Atom)e->xclient.data.l[2] ==
888 prop_atoms.net_wm_moveresize_size_left ||
889 (Atom)e->xclient.data.l[2] ==
890 prop_atoms.net_wm_moveresize_move ||
891 (Atom)e->xclient.data.l[2] ==
892 prop_atoms.net_wm_moveresize_size_keyboard ||
893 (Atom)e->xclient.data.l[2] ==
894 prop_atoms.net_wm_moveresize_move_keyboard) {
896 moveresize_start(client, e->xclient.data.l[0],
897 e->xclient.data.l[1], e->xclient.data.l[3],
898 e->xclient.data.l[2]);
900 } else if (msgtype == prop_atoms.net_moveresize_window) {
901 int oldg = client->gravity;
902 int tmpg, x, y, w, h;
904 if (e->xclient.data.l[0] & 0xff)
905 tmpg = e->xclient.data.l[0] & 0xff;
909 if (e->xclient.data.l[0] & 1 << 8)
910 x = e->xclient.data.l[1];
913 if (e->xclient.data.l[0] & 1 << 9)
914 y = e->xclient.data.l[2];
917 if (e->xclient.data.l[0] & 1 << 10)
918 w = e->xclient.data.l[3];
920 w = client->area.width;
921 if (e->xclient.data.l[0] & 1 << 11)
922 h = e->xclient.data.l[4];
924 h = client->area.height;
925 client->gravity = tmpg;
931 client->frame->size.left + client->frame->size.right;
933 client->frame->size.top + client->frame->size.bottom;
934 client_find_onscreen(client, &newx, &newy, fw, fh,
935 client_normal(client));
936 if (e->xclient.data.l[0] & 1 << 8)
938 if (e->xclient.data.l[0] & 1 << 9)
942 client_configure(client, OB_CORNER_TOPLEFT,
943 x, y, w, h, FALSE, TRUE);
945 client->gravity = oldg;
949 /* validate cuz we query stuff off the client here */
950 if (!client_validate(client)) break;
952 /* compress changes to a single property into a single change */
953 while (XCheckTypedWindowEvent(ob_display, client->window,
957 /* XXX: it would be nice to compress ALL changes to a property,
958 not just changes in a row without other props between. */
960 a = ce.xproperty.atom;
961 b = e->xproperty.atom;
965 if ((a == prop_atoms.net_wm_name ||
966 a == prop_atoms.wm_name ||
967 a == prop_atoms.net_wm_icon_name ||
968 a == prop_atoms.wm_icon_name)
970 (b == prop_atoms.net_wm_name ||
971 b == prop_atoms.wm_name ||
972 b == prop_atoms.net_wm_icon_name ||
973 b == prop_atoms.wm_icon_name)) {
976 if ((a == prop_atoms.net_wm_icon ||
977 a == prop_atoms.kwm_win_icon)
979 (b == prop_atoms.net_wm_icon ||
980 b == prop_atoms.kwm_win_icon))
983 XPutBackEvent(ob_display, &ce);
987 msgtype = e->xproperty.atom;
988 if (msgtype == XA_WM_NORMAL_HINTS) {
989 client_update_normal_hints(client);
990 /* normal hints can make a window non-resizable */
991 client_setup_decor_and_functions(client);
992 } else if (msgtype == XA_WM_HINTS) {
993 client_update_wmhints(client);
994 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
995 client_update_transient_for(client);
996 client_get_type(client);
997 /* type may have changed, so update the layer */
998 client_calc_layer(client);
999 client_setup_decor_and_functions(client);
1000 } else if (msgtype == prop_atoms.net_wm_name ||
1001 msgtype == prop_atoms.wm_name ||
1002 msgtype == prop_atoms.net_wm_icon_name ||
1003 msgtype == prop_atoms.wm_icon_name) {
1004 client_update_title(client);
1005 } else if (msgtype == prop_atoms.wm_class) {
1006 client_update_class(client);
1007 } else if (msgtype == prop_atoms.wm_protocols) {
1008 client_update_protocols(client);
1009 client_setup_decor_and_functions(client);
1011 else if (msgtype == prop_atoms.net_wm_strut) {
1012 client_update_strut(client);
1014 else if (msgtype == prop_atoms.net_wm_icon ||
1015 msgtype == prop_atoms.kwm_win_icon) {
1016 client_update_icons(client);
1018 else if (msgtype == prop_atoms.sm_client_id) {
1019 client_update_sm_client_id(client);
1024 if (extensions_shape && e->type == extensions_shape_event_basep) {
1025 client->shaped = ((XShapeEvent*)e)->shaped;
1026 frame_adjust_shape(client->frame);
1032 static void event_handle_dock(ObDock *s, XEvent *e)
1036 stacking_raise(DOCK_AS_WINDOW(s));
1047 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1051 dock_app_drag(app, &e->xmotion);
1054 if (app->ignore_unmaps) {
1055 app->ignore_unmaps--;
1058 dock_remove(app, TRUE);
1061 dock_remove(app, FALSE);
1063 case ReparentNotify:
1064 dock_remove(app, FALSE);
1066 case ConfigureNotify:
1067 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1072 ObMenuFrame* find_active_menu()
1075 ObMenuFrame *ret = NULL;
1077 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1086 ObMenuFrame* find_active_or_last_menu()
1088 ObMenuFrame *ret = NULL;
1090 ret = find_active_menu();
1091 if (!ret && menu_frame_visible)
1092 ret = menu_frame_visible->data;
1096 static void event_handle_menu(XEvent *ev)
1099 ObMenuEntryFrame *e;
1103 if (menu_can_hide) {
1104 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1105 ev->xbutton.y_root)))
1106 menu_entry_frame_execute(e, ev->xbutton.state);
1108 menu_frame_hide_all();
1112 if ((f = menu_frame_under(ev->xmotion.x_root,
1113 ev->xmotion.y_root))) {
1114 menu_frame_move_on_screen(f);
1115 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1116 ev->xmotion.y_root)))
1117 menu_frame_select(f, e);
1122 a = find_active_menu();
1124 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1126 menu_frame_select(a, NULL);
1131 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1132 menu_frame_hide_all();
1133 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1135 if ((f = find_active_menu()))
1136 menu_entry_frame_execute(f->selected, ev->xkey.state);
1137 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1139 if ((f = find_active_or_last_menu()) && f->parent)
1140 menu_frame_select(f, NULL);
1141 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1143 if ((f = find_active_or_last_menu()) && f->child)
1144 menu_frame_select_next(f->child);
1145 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1147 if ((f = find_active_or_last_menu()))
1148 menu_frame_select_previous(f);
1149 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1151 if ((f = find_active_or_last_menu()))
1152 menu_frame_select_next(f);
1158 static gboolean menu_hide_delay_func(gpointer data)
1160 menu_can_hide = TRUE;
1161 return FALSE; /* no repeat */
1164 static gboolean focus_delay_func(gpointer data)
1168 if (focus_client != c) {
1170 if (config_focus_raise)
1173 return FALSE; /* no repeat */
1176 static void focus_delay_client_dest(ObClient *client, gpointer data)
1178 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1181 void event_ignore_queued_enters()
1183 GSList *saved = NULL, *it;
1186 XSync(ob_display, FALSE);
1188 /* count the events */
1190 e = g_new(XEvent, 1);
1191 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1192 saved = g_slist_append(saved, e);
1193 ++ignore_enter_focus;
1199 /* put the events back */
1200 for (it = saved; it; it = g_slist_next(it)) {
1201 XPutBackEvent(ob_display, it->data);
1204 g_slist_free(saved);