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_client_dest(ObClient *client, gpointer data);
61 static void event_handle_root(XEvent *e);
62 static void event_handle_menu(XEvent *e);
63 static void event_handle_dock(ObDock *s, XEvent *e);
64 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
65 static void event_handle_client(ObClient *c, XEvent *e);
66 static void event_handle_group(ObGroup *g, XEvent *e);
68 static gboolean focus_delay_func(gpointer data);
69 static void focus_delay_client_dest(ObClient *client, gpointer data);
71 static gboolean menu_hide_delay_func(gpointer data);
73 Time event_lasttime = 0;
75 /*! The value of the mask for the NumLock modifier */
76 unsigned int NumLockMask;
77 /*! The value of the mask for the ScrollLock modifier */
78 unsigned int ScrollLockMask;
79 /*! The key codes for the modifier keys */
80 static XModifierKeymap *modmap;
81 /*! Table of the constant modifier masks */
82 static const int mask_table[] = {
83 ShiftMask, LockMask, ControlMask, Mod1Mask,
84 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
86 static int mask_table_size;
88 static guint ignore_enter_focus = 0;
90 static gboolean menu_can_hide;
92 static ObClient *focus_in, *focus_out;
95 static void ice_handler(int fd, gpointer conn)
98 IceProcessMessages(conn, NULL, &b);
101 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
102 IcePointer *watch_data)
107 fd = IceConnectionNumber(conn);
108 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
110 ob_main_loop_fd_remove(ob_main_loop, fd);
116 void event_startup(gboolean reconfig)
118 if (reconfig) return;
120 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
122 /* get lock masks that are defined by the display (not constant) */
123 modmap = XGetModifierMapping(ob_display);
125 if (modmap && modmap->max_keypermod > 0) {
127 const size_t size = mask_table_size * modmap->max_keypermod;
128 /* get the values of the keyboard lock modifiers
129 Note: Caps lock is not retrieved the same way as Scroll and Num
130 lock since it doesn't need to be. */
131 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
132 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
135 for (cnt = 0; cnt < size; ++cnt) {
136 if (! modmap->modifiermap[cnt]) continue;
138 if (num_lock == modmap->modifiermap[cnt])
139 NumLockMask = mask_table[cnt / modmap->max_keypermod];
140 if (scroll_lock == modmap->modifiermap[cnt])
141 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
145 ob_main_loop_x_add(ob_main_loop, event_process, event_done, NULL, NULL);
148 IceAddConnectionWatch(ice_watch, NULL);
151 client_add_destructor(focus_delay_client_dest, NULL);
152 client_add_destructor(event_client_dest, NULL);
155 void event_shutdown(gboolean reconfig)
157 if (reconfig) return;
160 IceRemoveConnectionWatch(ice_watch, NULL);
163 client_remove_destructor(focus_delay_client_dest);
164 XFreeModifiermap(modmap);
167 static Window event_get_window(XEvent *e)
174 window = RootWindow(ob_display, ob_screen);
177 window = e->xmap.window;
180 window = e->xunmap.window;
183 window = e->xdestroywindow.window;
185 case ConfigureRequest:
186 window = e->xconfigurerequest.window;
188 case ConfigureNotify:
189 window = e->xconfigure.window;
193 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
194 switch (((XkbAnyEvent*)e)->xkb_type) {
196 window = ((XkbBellNotifyEvent*)e)->window;
202 window = e->xany.window;
207 static void event_set_lasttime(XEvent *e)
211 /* grab the lasttime and hack up the state */
227 t = e->xproperty.time;
231 t = e->xcrossing.time;
234 /* if more event types are anticipated, get their timestamp
239 if (t > event_lasttime)
243 #define STRIP_MODS(s) \
244 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
245 /* kill off the Button1Mask etc, only want the modifiers */ \
246 s &= (ControlMask | ShiftMask | Mod1Mask | \
247 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
249 static void event_hack_mods(XEvent *e)
257 STRIP_MODS(e->xbutton.state);
260 STRIP_MODS(e->xkey.state);
263 STRIP_MODS(e->xkey.state);
264 /* remove from the state the mask of the modifier being released, if
265 it is a modifier key being released (this is a little ugly..) */
266 kp = modmap->modifiermap;
267 for (i = 0; i < mask_table_size; ++i) {
268 for (k = 0; k < modmap->max_keypermod; ++k) {
269 if (*kp == e->xkey.keycode) { /* found the keycode */
270 /* remove the mask for it */
271 e->xkey.state &= ~mask_table[i];
272 /* cause the first loop to break; */
274 break; /* get outta here! */
281 STRIP_MODS(e->xmotion.state);
282 /* compress events */
285 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
287 e->xmotion.x_root = ce.xmotion.x_root;
288 e->xmotion.y_root = ce.xmotion.y_root;
295 static gboolean event_ignore(XEvent *e, ObClient *client)
300 if (e->xcrossing.detail == NotifyInferior)
304 if (e->xfocus.detail > NotifyNonlinearVirtual)
308 if (e->xfocus.detail > NotifyNonlinearVirtual)
310 if (e->xfocus.detail == NotifyInferior ||
311 e->xfocus.mode == NotifyGrab)
318 static void event_client_dest(ObClient *client, gpointer data)
320 if (client == focus_in)
322 if (client == focus_out)
326 static void event_done(gpointer data)
328 static ObClient *last = NULL;
330 /* sometimes focus_hilite can be on an unfocused window, this make sure
331 it loses its focus hilite when focus moves */
333 (focus_in && focus_hilite != focus_in) &&
334 (focus_out && focus_hilite != focus_out))
336 frame_adjust_focus(focus_hilite->frame, FALSE);
340 if (focus_in != focus_client) {
341 focus_set_client(focus_in);
342 frame_adjust_focus(focus_in->frame, TRUE);
343 client_calc_layer(focus_in);
347 if (focus_out == focus_client)
348 focus_set_client(NULL);
349 frame_adjust_focus(focus_out->frame, FALSE);
350 client_calc_layer(focus_out);
353 focus_hilite = focus_in;
355 if (focus_client != last) {
357 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
361 focus_in = focus_out = NULL;
364 static void event_process(const XEvent *ec, gpointer data)
367 ObGroup *group = NULL;
368 ObClient *client = NULL;
370 ObDockApp *dockapp = NULL;
371 ObWindow *obwin = NULL;
374 /* make a copy we can mangle */
378 window = event_get_window(e);
379 if (!(e->type == PropertyNotify &&
380 (group = g_hash_table_lookup(group_map, &window))))
381 if ((obwin = g_hash_table_lookup(window_map, &window))) {
382 switch (obwin->type) {
384 dock = WINDOW_AS_DOCK(obwin);
387 dockapp = WINDOW_AS_DOCKAPP(obwin);
390 client = WINDOW_AS_CLIENT(obwin);
393 case Window_Internal:
394 /* not to be used for events */
395 g_assert_not_reached();
400 event_set_lasttime(e);
402 if (event_ignore(e, client))
405 /* deal with it in the kernel */
407 event_handle_group(group, e);
409 event_handle_client(client, e);
411 event_handle_dockapp(dockapp, e);
413 event_handle_dock(dock, e);
414 else if (window == RootWindow(ob_display, ob_screen))
415 event_handle_root(e);
416 else if (e->type == MapRequest)
417 client_manage(window);
418 else if (e->type == ConfigureRequest) {
419 /* unhandled configure requests must be used to configure the
423 xwc.x = e->xconfigurerequest.x;
424 xwc.y = e->xconfigurerequest.y;
425 xwc.width = e->xconfigurerequest.width;
426 xwc.height = e->xconfigurerequest.height;
427 xwc.border_width = e->xconfigurerequest.border_width;
428 xwc.sibling = e->xconfigurerequest.above;
429 xwc.stack_mode = e->xconfigurerequest.detail;
431 /* we are not to be held responsible if someone sends us an
433 xerror_set_ignore(TRUE);
434 XConfigureWindow(ob_display, window,
435 e->xconfigurerequest.value_mask, &xwc);
436 xerror_set_ignore(FALSE);
439 /* user input (action-bound) events */
440 if (e->type == ButtonPress || e->type == ButtonRelease ||
441 e->type == MotionNotify || e->type == KeyPress ||
442 e->type == KeyRelease)
444 if (menu_frame_visible)
445 event_handle_menu(e);
447 if (!keyboard_process_interactive_grab(e, &client)) {
448 if (moveresize_in_progress) {
451 /* make further actions work on the client being
453 client = moveresize_client;
456 menu_can_hide = FALSE;
457 ob_main_loop_timeout_add(ob_main_loop,
459 menu_hide_delay_func,
462 if (e->type == ButtonPress || e->type == ButtonRelease ||
463 e->type == MotionNotify)
464 mouse_event(client, e);
465 else if (e->type == KeyPress)
466 /* when in the middle of a focus cycling action, this
467 causes the window which appears to be focused to be
468 the one on which the actions will be executed */
469 keyboard_event((focus_cycle_target ?
470 focus_cycle_target : client), e);
476 static void event_handle_root(XEvent *e)
482 ob_debug("Another WM has requested to replace us. Exiting.\n");
487 if (e->xclient.format != 32) break;
489 msgtype = e->xclient.message_type;
490 if (msgtype == prop_atoms.net_current_desktop) {
491 unsigned int d = e->xclient.data.l[0];
492 if (d < screen_num_desktops)
493 screen_set_desktop(d);
494 } else if (msgtype == prop_atoms.net_number_of_desktops) {
495 unsigned int d = e->xclient.data.l[0];
497 screen_set_num_desktops(d);
498 } else if (msgtype == prop_atoms.net_showing_desktop) {
499 screen_show_desktop(e->xclient.data.l[0] != 0);
503 if (e->xproperty.atom == prop_atoms.net_desktop_names)
504 screen_update_desktop_names();
505 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
506 screen_update_layout();
508 case ConfigureNotify:
510 XRRUpdateConfiguration(e);
517 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
518 ob_debug("VIDMODE EVENT\n");
524 static void event_handle_group(ObGroup *group, XEvent *e)
528 g_assert(e->type == PropertyNotify);
530 for (it = group->members; it; it = g_slist_next(it))
531 event_handle_client(it->data, e);
534 void event_enter_client(ObClient *client)
536 g_assert(config_focus_follow);
538 if (client_normal(client) && client_can_focus(client)) {
539 if (config_focus_delay) {
540 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
541 ob_main_loop_timeout_add(ob_main_loop,
546 focus_delay_func(client);
550 static void event_handle_client(ObClient *client, XEvent *e)
558 case VisibilityNotify:
559 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
563 /* Wheel buttons don't draw because they are an instant click, so it
564 is a waste of resources to go drawing it. */
565 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
566 con = frame_context(client, e->xbutton.window);
567 con = mouse_button_frame_context(con, e->xbutton.button);
569 case OB_FRAME_CONTEXT_MAXIMIZE:
570 client->frame->max_press = (e->type == ButtonPress);
571 framerender_frame(client->frame);
573 case OB_FRAME_CONTEXT_CLOSE:
574 client->frame->close_press = (e->type == ButtonPress);
575 framerender_frame(client->frame);
577 case OB_FRAME_CONTEXT_ICONIFY:
578 client->frame->iconify_press = (e->type == ButtonPress);
579 framerender_frame(client->frame);
581 case OB_FRAME_CONTEXT_ALLDESKTOPS:
582 client->frame->desk_press = (e->type == ButtonPress);
583 framerender_frame(client->frame);
585 case OB_FRAME_CONTEXT_SHADE:
586 client->frame->shade_press = (e->type == ButtonPress);
587 framerender_frame(client->frame);
590 /* nothing changes with clicks for any other contexts */
597 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
598 e->xfocus.window, client->window,
599 e->xfocus.mode, e->xfocus.detail);
602 if (focus_out == client)
607 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
608 e->xfocus.window, client->window,
609 e->xfocus.mode, e->xfocus.detail);
612 if (focus_in == client)
616 con = frame_context(client, e->xcrossing.window);
618 case OB_FRAME_CONTEXT_MAXIMIZE:
619 client->frame->max_hover = FALSE;
620 frame_adjust_state(client->frame);
622 case OB_FRAME_CONTEXT_ALLDESKTOPS:
623 client->frame->desk_hover = FALSE;
624 frame_adjust_state(client->frame);
626 case OB_FRAME_CONTEXT_SHADE:
627 client->frame->shade_hover = FALSE;
628 frame_adjust_state(client->frame);
630 case OB_FRAME_CONTEXT_ICONIFY:
631 client->frame->iconify_hover = FALSE;
632 frame_adjust_state(client->frame);
634 case OB_FRAME_CONTEXT_CLOSE:
635 client->frame->close_hover = FALSE;
636 frame_adjust_state(client->frame);
638 case OB_FRAME_CONTEXT_FRAME:
640 if (config_focus_follow && config_focus_delay)
641 ob_main_loop_timeout_remove_data(ob_main_loop,
652 gboolean nofocus = FALSE;
654 if (ignore_enter_focus) {
655 ignore_enter_focus--;
659 con = frame_context(client, e->xcrossing.window);
661 case OB_FRAME_CONTEXT_MAXIMIZE:
662 client->frame->max_hover = TRUE;
663 frame_adjust_state(client->frame);
665 case OB_FRAME_CONTEXT_ALLDESKTOPS:
666 client->frame->desk_hover = TRUE;
667 frame_adjust_state(client->frame);
669 case OB_FRAME_CONTEXT_SHADE:
670 client->frame->shade_hover = TRUE;
671 frame_adjust_state(client->frame);
673 case OB_FRAME_CONTEXT_ICONIFY:
674 client->frame->iconify_hover = TRUE;
675 frame_adjust_state(client->frame);
677 case OB_FRAME_CONTEXT_CLOSE:
678 client->frame->close_hover = TRUE;
679 frame_adjust_state(client->frame);
681 case OB_FRAME_CONTEXT_FRAME:
682 if (e->xcrossing.mode == NotifyGrab ||
683 e->xcrossing.mode == NotifyUngrab)
686 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
687 (e->type == EnterNotify ? "Enter" : "Leave"),
689 e->xcrossing.detail, client?client->window:0);
693 ob_debug("%sNotify mode %d detail %d on %lx, "
694 "focusing window: %d\n",
695 (e->type == EnterNotify ? "Enter" : "Leave"),
697 e->xcrossing.detail, (client?client->window:0),
700 if (!nofocus && config_focus_follow)
701 event_enter_client(client);
709 case ConfigureRequest:
711 while (XCheckTypedWindowEvent(ob_display, client->window,
712 ConfigureRequest, &ce)) {
714 /* XXX if this causes bad things.. we can compress config req's
715 with the same mask. */
716 e->xconfigurerequest.value_mask |=
717 ce.xconfigurerequest.value_mask;
718 if (ce.xconfigurerequest.value_mask & CWX)
719 e->xconfigurerequest.x = ce.xconfigurerequest.x;
720 if (ce.xconfigurerequest.value_mask & CWY)
721 e->xconfigurerequest.y = ce.xconfigurerequest.y;
722 if (ce.xconfigurerequest.value_mask & CWWidth)
723 e->xconfigurerequest.width = ce.xconfigurerequest.width;
724 if (ce.xconfigurerequest.value_mask & CWHeight)
725 e->xconfigurerequest.height = ce.xconfigurerequest.height;
726 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
727 e->xconfigurerequest.border_width =
728 ce.xconfigurerequest.border_width;
729 if (ce.xconfigurerequest.value_mask & CWStackMode)
730 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
733 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
734 if (client->iconic || client->shaded) return;
736 /* resize, then move, as specified in the EWMH section 7.7 */
737 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
743 if (e->xconfigurerequest.value_mask & CWBorderWidth)
744 client->border_width = e->xconfigurerequest.border_width;
746 x = (e->xconfigurerequest.value_mask & CWX) ?
747 e->xconfigurerequest.x : client->area.x;
748 y = (e->xconfigurerequest.value_mask & CWY) ?
749 e->xconfigurerequest.y : client->area.y;
750 w = (e->xconfigurerequest.value_mask & CWWidth) ?
751 e->xconfigurerequest.width : client->area.width;
752 h = (e->xconfigurerequest.value_mask & CWHeight) ?
753 e->xconfigurerequest.height : client->area.height;
759 client->frame->size.left + client->frame->size.right;
761 client->frame->size.top + client->frame->size.bottom;
762 client_find_onscreen(client, &newx, &newy, fw, fh,
763 client_normal(client));
764 if (e->xconfigurerequest.value_mask & CWX)
766 if (e->xconfigurerequest.value_mask & CWY)
770 switch (client->gravity) {
771 case NorthEastGravity:
773 corner = OB_CORNER_TOPRIGHT;
775 case SouthWestGravity:
777 corner = OB_CORNER_BOTTOMLEFT;
779 case SouthEastGravity:
780 corner = OB_CORNER_BOTTOMRIGHT;
782 default: /* NorthWest, Static, etc */
783 corner = OB_CORNER_TOPLEFT;
786 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
790 if (e->xconfigurerequest.value_mask & CWStackMode) {
791 switch (e->xconfigurerequest.detail) {
794 client_lower(client);
800 client_raise(client);
806 if (client->ignore_unmaps) {
807 client->ignore_unmaps--;
810 client_unmanage(client);
813 client_unmanage(client);
816 /* this is when the client is first taken captive in the frame */
817 if (e->xreparent.parent == client->frame->plate) break;
820 This event is quite rare and is usually handled in unmapHandler.
821 However, if the window is unmapped when the reparent event occurs,
822 the window manager never sees it because an unmap event is not sent
823 to an already unmapped window.
826 /* we don't want the reparent event, put it back on the stack for the
827 X server to deal with after we unmanage the window */
828 XPutBackEvent(ob_display, e);
830 client_unmanage(client);
833 ob_debug("MapRequest for 0x%lx\n", client->window);
834 if (!client->iconic) break; /* this normally doesn't happen, but if it
835 does, we don't want it! */
836 client_activate(client, FALSE);
839 /* validate cuz we query stuff off the client here */
840 if (!client_validate(client)) break;
842 if (e->xclient.format != 32) return;
844 msgtype = e->xclient.message_type;
845 if (msgtype == prop_atoms.wm_change_state) {
846 /* compress changes into a single change */
847 while (XCheckTypedWindowEvent(ob_display, client->window,
849 /* XXX: it would be nice to compress ALL messages of a
850 type, not just messages in a row without other
851 message types between. */
852 if (ce.xclient.message_type != msgtype) {
853 XPutBackEvent(ob_display, &ce);
856 e->xclient = ce.xclient;
858 client_set_wm_state(client, e->xclient.data.l[0]);
859 } else if (msgtype == prop_atoms.net_wm_desktop) {
860 /* compress changes into a single change */
861 while (XCheckTypedWindowEvent(ob_display, client->window,
863 /* XXX: it would be nice to compress ALL messages of a
864 type, not just messages in a row without other
865 message types between. */
866 if (ce.xclient.message_type != msgtype) {
867 XPutBackEvent(ob_display, &ce);
870 e->xclient = ce.xclient;
872 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
873 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
874 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
876 } else if (msgtype == prop_atoms.net_wm_state) {
877 /* can't compress these */
878 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
879 (e->xclient.data.l[0] == 0 ? "Remove" :
880 e->xclient.data.l[0] == 1 ? "Add" :
881 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
882 e->xclient.data.l[1], e->xclient.data.l[2],
884 client_set_state(client, e->xclient.data.l[0],
885 e->xclient.data.l[1], e->xclient.data.l[2]);
886 } else if (msgtype == prop_atoms.net_close_window) {
887 ob_debug("net_close_window for 0x%lx\n", client->window);
888 client_close(client);
889 } else if (msgtype == prop_atoms.net_active_window) {
890 ob_debug("net_active_window for 0x%lx\n", client->window);
891 client_activate(client, FALSE);
892 } else if (msgtype == prop_atoms.net_wm_moveresize) {
893 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
894 if ((Atom)e->xclient.data.l[2] ==
895 prop_atoms.net_wm_moveresize_size_topleft ||
896 (Atom)e->xclient.data.l[2] ==
897 prop_atoms.net_wm_moveresize_size_top ||
898 (Atom)e->xclient.data.l[2] ==
899 prop_atoms.net_wm_moveresize_size_topright ||
900 (Atom)e->xclient.data.l[2] ==
901 prop_atoms.net_wm_moveresize_size_right ||
902 (Atom)e->xclient.data.l[2] ==
903 prop_atoms.net_wm_moveresize_size_right ||
904 (Atom)e->xclient.data.l[2] ==
905 prop_atoms.net_wm_moveresize_size_bottomright ||
906 (Atom)e->xclient.data.l[2] ==
907 prop_atoms.net_wm_moveresize_size_bottom ||
908 (Atom)e->xclient.data.l[2] ==
909 prop_atoms.net_wm_moveresize_size_bottomleft ||
910 (Atom)e->xclient.data.l[2] ==
911 prop_atoms.net_wm_moveresize_size_left ||
912 (Atom)e->xclient.data.l[2] ==
913 prop_atoms.net_wm_moveresize_move ||
914 (Atom)e->xclient.data.l[2] ==
915 prop_atoms.net_wm_moveresize_size_keyboard ||
916 (Atom)e->xclient.data.l[2] ==
917 prop_atoms.net_wm_moveresize_move_keyboard) {
919 moveresize_start(client, e->xclient.data.l[0],
920 e->xclient.data.l[1], e->xclient.data.l[3],
921 e->xclient.data.l[2]);
923 } else if (msgtype == prop_atoms.net_moveresize_window) {
924 int oldg = client->gravity;
925 int tmpg, x, y, w, h;
927 if (e->xclient.data.l[0] & 0xff)
928 tmpg = e->xclient.data.l[0] & 0xff;
932 if (e->xclient.data.l[0] & 1 << 8)
933 x = e->xclient.data.l[1];
936 if (e->xclient.data.l[0] & 1 << 9)
937 y = e->xclient.data.l[2];
940 if (e->xclient.data.l[0] & 1 << 10)
941 w = e->xclient.data.l[3];
943 w = client->area.width;
944 if (e->xclient.data.l[0] & 1 << 11)
945 h = e->xclient.data.l[4];
947 h = client->area.height;
948 client->gravity = tmpg;
954 client->frame->size.left + client->frame->size.right;
956 client->frame->size.top + client->frame->size.bottom;
957 client_find_onscreen(client, &newx, &newy, fw, fh,
958 client_normal(client));
959 if (e->xclient.data.l[0] & 1 << 8)
961 if (e->xclient.data.l[0] & 1 << 9)
965 client_configure(client, OB_CORNER_TOPLEFT,
966 x, y, w, h, FALSE, TRUE);
968 client->gravity = oldg;
972 /* validate cuz we query stuff off the client here */
973 if (!client_validate(client)) break;
975 /* compress changes to a single property into a single change */
976 while (XCheckTypedWindowEvent(ob_display, client->window,
980 /* XXX: it would be nice to compress ALL changes to a property,
981 not just changes in a row without other props between. */
983 a = ce.xproperty.atom;
984 b = e->xproperty.atom;
988 if ((a == prop_atoms.net_wm_name ||
989 a == prop_atoms.wm_name ||
990 a == prop_atoms.net_wm_icon_name ||
991 a == prop_atoms.wm_icon_name)
993 (b == prop_atoms.net_wm_name ||
994 b == prop_atoms.wm_name ||
995 b == prop_atoms.net_wm_icon_name ||
996 b == prop_atoms.wm_icon_name)) {
999 if ((a == prop_atoms.net_wm_icon ||
1000 a == prop_atoms.kwm_win_icon)
1002 (b == prop_atoms.net_wm_icon ||
1003 b == prop_atoms.kwm_win_icon))
1006 XPutBackEvent(ob_display, &ce);
1010 msgtype = e->xproperty.atom;
1011 if (msgtype == XA_WM_NORMAL_HINTS) {
1012 client_update_normal_hints(client);
1013 /* normal hints can make a window non-resizable */
1014 client_setup_decor_and_functions(client);
1015 } else if (msgtype == XA_WM_HINTS) {
1016 client_update_wmhints(client);
1017 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1018 client_update_transient_for(client);
1019 client_get_type(client);
1020 /* type may have changed, so update the layer */
1021 client_calc_layer(client);
1022 client_setup_decor_and_functions(client);
1023 } else if (msgtype == prop_atoms.net_wm_name ||
1024 msgtype == prop_atoms.wm_name ||
1025 msgtype == prop_atoms.net_wm_icon_name ||
1026 msgtype == prop_atoms.wm_icon_name) {
1027 client_update_title(client);
1028 } else if (msgtype == prop_atoms.wm_class) {
1029 client_update_class(client);
1030 } else if (msgtype == prop_atoms.wm_protocols) {
1031 client_update_protocols(client);
1032 client_setup_decor_and_functions(client);
1034 else if (msgtype == prop_atoms.net_wm_strut) {
1035 client_update_strut(client);
1037 else if (msgtype == prop_atoms.net_wm_icon ||
1038 msgtype == prop_atoms.kwm_win_icon) {
1039 client_update_icons(client);
1041 else if (msgtype == prop_atoms.sm_client_id) {
1042 client_update_sm_client_id(client);
1047 if (extensions_shape && e->type == extensions_shape_event_basep) {
1048 client->shaped = ((XShapeEvent*)e)->shaped;
1049 frame_adjust_shape(client->frame);
1055 static void event_handle_dock(ObDock *s, XEvent *e)
1059 stacking_raise(DOCK_AS_WINDOW(s));
1070 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1074 dock_app_drag(app, &e->xmotion);
1077 if (app->ignore_unmaps) {
1078 app->ignore_unmaps--;
1081 dock_remove(app, TRUE);
1084 dock_remove(app, FALSE);
1086 case ReparentNotify:
1087 dock_remove(app, FALSE);
1089 case ConfigureNotify:
1090 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1095 ObMenuFrame* find_active_menu()
1098 ObMenuFrame *ret = NULL;
1100 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1109 ObMenuFrame* find_active_or_last_menu()
1111 ObMenuFrame *ret = NULL;
1113 ret = find_active_menu();
1114 if (!ret && menu_frame_visible)
1115 ret = menu_frame_visible->data;
1119 static void event_handle_menu(XEvent *ev)
1122 ObMenuEntryFrame *e;
1126 if (menu_can_hide) {
1127 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1128 ev->xbutton.y_root)))
1129 menu_entry_frame_execute(e, ev->xbutton.state);
1131 menu_frame_hide_all();
1135 if ((f = menu_frame_under(ev->xmotion.x_root,
1136 ev->xmotion.y_root))) {
1137 menu_frame_move_on_screen(f);
1138 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1139 ev->xmotion.y_root)))
1140 menu_frame_select(f, e);
1145 a = find_active_menu();
1147 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1149 menu_frame_select(a, NULL);
1154 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1155 menu_frame_hide_all();
1156 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1158 if ((f = find_active_menu()))
1159 menu_entry_frame_execute(f->selected, ev->xkey.state);
1160 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1162 if ((f = find_active_or_last_menu()) && f->parent)
1163 menu_frame_select(f, NULL);
1164 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1166 if ((f = find_active_or_last_menu()) && f->child)
1167 menu_frame_select_next(f->child);
1168 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1170 if ((f = find_active_or_last_menu()))
1171 menu_frame_select_previous(f);
1172 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1174 if ((f = find_active_or_last_menu()))
1175 menu_frame_select_next(f);
1181 static gboolean menu_hide_delay_func(gpointer data)
1183 menu_can_hide = TRUE;
1184 return FALSE; /* no repeat */
1187 static gboolean focus_delay_func(gpointer data)
1191 if (focus_client != c) {
1193 if (config_focus_raise)
1196 return FALSE; /* no repeat */
1199 static void focus_delay_client_dest(ObClient *client, gpointer data)
1201 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1204 void event_ignore_queued_enters()
1206 GSList *saved = NULL, *it;
1209 XSync(ob_display, FALSE);
1211 /* count the events */
1213 e = g_new(XEvent, 1);
1214 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1217 win = g_hash_table_lookup(window_map, &e->xany.window);
1218 if (win && WINDOW_IS_CLIENT(win))
1219 ++ignore_enter_focus;
1221 saved = g_slist_append(saved, e);
1227 /* put the events back */
1228 for (it = saved; it; it = g_slist_next(it)) {
1229 XPutBackEvent(ob_display, it->data);
1232 g_slist_free(saved);