11 #include "menuframe.h"
15 #include "framerender.h"
17 #include "moveresize.h"
19 #include "extensions.h"
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
28 # include <libsn/sn.h>
31 #ifdef HAVE_SYS_SELECT_H
32 # include <sys/select.h>
39 #include <X11/ICE/ICElib.h>
42 static void event_process(const XEvent *e, gpointer data);
43 static void event_handle_root(XEvent *e);
44 static void event_handle_menu(XEvent *e);
45 static void event_handle_dock(ObDock *s, XEvent *e);
46 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
47 static void event_handle_client(ObClient *c, XEvent *e);
49 static gboolean focus_delay_func(gpointer data);
50 static void focus_delay_client_dest(gpointer data);
52 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
53 (e)->xfocus.detail == NotifyAncestor || \
54 (e)->xfocus.detail > NotifyNonlinearVirtual)
55 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
56 (e)->xfocus.detail == NotifyInferior || \
57 (e)->xfocus.detail == NotifyAncestor || \
58 (e)->xfocus.detail > NotifyNonlinearVirtual)
60 Time event_lasttime = 0;
62 /*! The value of the mask for the NumLock modifier */
63 unsigned int NumLockMask;
64 /*! The value of the mask for the ScrollLock modifier */
65 unsigned int ScrollLockMask;
66 /*! The key codes for the modifier keys */
67 static XModifierKeymap *modmap;
68 /*! Table of the constant modifier masks */
69 static const int mask_table[] = {
70 ShiftMask, LockMask, ControlMask, Mod1Mask,
71 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
73 static int mask_table_size;
75 static ObClient *focus_delay_client;
78 static void ice_handler(int fd, gpointer conn)
81 IceProcessMessages(conn, NULL, &b);
84 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
85 IcePointer *watch_data)
90 fd = IceConnectionNumber(conn);
91 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
93 ob_main_loop_fd_remove(ob_main_loop, fd);
100 static void sn_handler(const XEvent *e, gpointer display)
104 sn_display_process_event(display, &ec);
109 void event_startup(gboolean reconfig)
111 if (reconfig) return;
113 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
115 /* get lock masks that are defined by the display (not constant) */
116 modmap = XGetModifierMapping(ob_display);
118 if (modmap && modmap->max_keypermod > 0) {
120 const size_t size = mask_table_size * modmap->max_keypermod;
121 /* get the values of the keyboard lock modifiers
122 Note: Caps lock is not retrieved the same way as Scroll and Num
123 lock since it doesn't need to be. */
124 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
125 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
128 for (cnt = 0; cnt < size; ++cnt) {
129 if (! modmap->modifiermap[cnt]) continue;
131 if (num_lock == modmap->modifiermap[cnt])
132 NumLockMask = mask_table[cnt / modmap->max_keypermod];
133 if (scroll_lock == modmap->modifiermap[cnt])
134 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
138 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
141 IceAddConnectionWatch(ice_watch, NULL);
145 ob_main_loop_x_add(ob_main_loop, sn_handler, ob_sn_display, NULL);
148 client_add_destructor(focus_delay_client_dest);
151 void event_shutdown(gboolean reconfig)
153 if (reconfig) return;
155 client_remove_destructor(focus_delay_client_dest);
156 XFreeModifiermap(modmap);
159 static Window event_get_window(XEvent *e)
166 window = RootWindow(ob_display, ob_screen);
169 window = e->xmap.window;
172 window = e->xunmap.window;
175 window = e->xdestroywindow.window;
177 case ConfigureRequest:
178 window = e->xconfigurerequest.window;
180 case ConfigureNotify:
181 window = e->xconfigure.window;
185 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
186 switch (((XkbAnyEvent*)e)->xkb_type) {
188 window = ((XkbBellNotifyEvent*)e)->window;
194 window = e->xany.window;
199 static void event_set_lasttime(XEvent *e)
203 /* grab the lasttime and hack up the state */
219 t = e->xproperty.time;
223 t = e->xcrossing.time;
226 /* if more event types are anticipated, get their timestamp
231 if (t > event_lasttime)
235 #define STRIP_MODS(s) \
236 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
237 /* kill off the Button1Mask etc, only want the modifiers */ \
238 s &= (ControlMask | ShiftMask | Mod1Mask | \
239 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
241 static void event_hack_mods(XEvent *e)
249 STRIP_MODS(e->xbutton.state);
252 STRIP_MODS(e->xkey.state);
255 STRIP_MODS(e->xkey.state);
256 /* remove from the state the mask of the modifier being released, if
257 it is a modifier key being released (this is a little ugly..) */
258 kp = modmap->modifiermap;
259 for (i = 0; i < mask_table_size; ++i) {
260 for (k = 0; k < modmap->max_keypermod; ++k) {
261 if (*kp == e->xkey.keycode) { /* found the keycode */
262 /* remove the mask for it */
263 e->xkey.state &= ~mask_table[i];
264 /* cause the first loop to break; */
266 break; /* get outta here! */
273 STRIP_MODS(e->xmotion.state);
274 /* compress events */
277 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
279 e->xmotion.x_root = ce.xmotion.x_root;
280 e->xmotion.y_root = ce.xmotion.y_root;
287 static gboolean event_ignore(XEvent *e, ObClient *client)
291 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
292 because of RevertToPointerRoot. If the focus ends up reverting to
293 pointer root on a workspace change, then the FocusIn event that we
294 want will be of type NotifyAncestor. This situation does not occur
295 for FocusOut, so it is safely ignored there.
297 if (INVALID_FOCUSIN(e) ||
300 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
301 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
303 /* says a client was not found for the event (or a valid FocusIn
306 e->xfocus.window = None;
311 ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
312 e->xfocus.mode, e->xfocus.detail);
316 if (INVALID_FOCUSOUT(e)) {
318 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
319 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
325 ob_debug("FocusOut on %lx mode %d detail %d\n",
326 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
331 gboolean fallback = TRUE;
334 if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
336 if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
338 if (fe.type == FocusOut) {
340 ob_debug("found pending FocusOut");
342 if (!INVALID_FOCUSOUT(&fe)) {
343 /* if there is a VALID FocusOut still coming, don't
344 fallback focus yet, we'll deal with it then */
345 XPutBackEvent(ob_display, &fe);
351 ob_debug("found pending FocusIn");
353 /* is the focused window getting a FocusOut/In back to
356 if (fe.xfocus.window == e->xfocus.window &&
357 !event_ignore(&fe, client)) {
359 if focus_client is not set, then we can't do
360 this. we need the FocusIn. This happens in the
361 case when the set_focus_client(NULL) in the
362 focus_fallback function fires and then
363 focus_fallback picks the currently focused
364 window (such as on a SendToDesktop-esque action.
368 ob_debug("focused window got an Out/In back to "
369 "itself IGNORED both");
373 event_process(&fe, NULL);
375 ob_debug("focused window got an Out/In back to "
376 "itself but focus_client was null "
377 "IGNORED just the Out");
383 /* once all the FocusOut's have been dealt with, if there
384 is a FocusIn still left and it is valid, then use it */
385 event_process(&fe, NULL);
386 /* secret magic way of event_process telling us that no
387 client was found for the FocusIn event. ^_^ */
388 if (fe.xfocus.window != None) {
396 ob_debug("no valid FocusIn and no FocusOut events found, "
399 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
405 /* NotifyUngrab occurs when a mouse button is released and the event is
406 caused, like when lowering a window */
407 /* NotifyVirtual occurs when ungrabbing the pointer */
408 if (e->xcrossing.mode == NotifyGrab ||
409 e->xcrossing.detail == NotifyInferior ||
410 (e->xcrossing.mode == NotifyUngrab &&
411 e->xcrossing.detail == NotifyVirtual)) {
413 ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
414 (e->type == EnterNotify ? "Enter" : "Leave"),
416 e->xcrossing.detail, client?client->window:0);
421 ob_debug("%sNotify mode %d detail %d on %lx",
422 (e->type == EnterNotify ? "Enter" : "Leave"),
424 e->xcrossing.detail, client?client->window:0);
431 static void event_process(const XEvent *ec, gpointer data)
434 ObClient *client = NULL;
436 ObDockApp *dockapp = NULL;
437 ObWindow *obwin = NULL;
440 /* make a copy we can mangle */
444 window = event_get_window(e);
445 if ((obwin = g_hash_table_lookup(window_map, &window))) {
446 switch (obwin->type) {
448 dock = WINDOW_AS_DOCK(obwin);
451 dockapp = WINDOW_AS_DOCKAPP(obwin);
454 client = WINDOW_AS_CLIENT(obwin);
457 case Window_Internal:
458 /* not to be used for events */
459 g_assert_not_reached();
464 event_set_lasttime(e);
466 if (event_ignore(e, client))
469 /* deal with it in the kernel */
471 event_handle_client(client, e);
473 event_handle_dockapp(dockapp, e);
475 event_handle_dock(dock, e);
476 else if (window == RootWindow(ob_display, ob_screen))
477 event_handle_root(e);
478 else if (e->type == MapRequest)
479 client_manage(window);
480 else if (e->type == ConfigureRequest) {
481 /* unhandled configure requests must be used to configure the
485 xwc.x = e->xconfigurerequest.x;
486 xwc.y = e->xconfigurerequest.y;
487 xwc.width = e->xconfigurerequest.width;
488 xwc.height = e->xconfigurerequest.height;
489 xwc.border_width = e->xconfigurerequest.border_width;
490 xwc.sibling = e->xconfigurerequest.above;
491 xwc.stack_mode = e->xconfigurerequest.detail;
493 /* we are not to be held responsible if someone sends us an
495 xerror_set_ignore(TRUE);
496 XConfigureWindow(ob_display, window,
497 e->xconfigurerequest.value_mask, &xwc);
498 xerror_set_ignore(FALSE);
501 /* user input (action-bound) events */
502 if (e->type == ButtonPress || e->type == ButtonRelease ||
503 e->type == MotionNotify || e->type == KeyPress ||
504 e->type == KeyRelease)
506 if (menu_frame_visible)
507 event_handle_menu(e);
508 else if (moveresize_in_progress)
511 ObFrameContext context;
513 context = frame_context(client, e->xany.window);
515 if (!keyboard_process_interactive_grab(e, &client, &context)) {
516 if (e->type == ButtonPress || e->type == ButtonRelease ||
517 e->type == MotionNotify)
518 mouse_event(client, context, e);
519 else if (e->type == KeyPress)
520 keyboard_event(client, e);
526 static void event_handle_root(XEvent *e)
532 ob_debug("Another WM has requested to replace us. Exiting.\n");
537 if (e->xclient.format != 32) break;
539 msgtype = e->xclient.message_type;
540 if (msgtype == prop_atoms.net_current_desktop) {
541 unsigned int d = e->xclient.data.l[0];
542 if (d < screen_num_desktops)
543 screen_set_desktop(d);
544 } else if (msgtype == prop_atoms.net_number_of_desktops) {
545 unsigned int d = e->xclient.data.l[0];
547 screen_set_num_desktops(d);
548 } else if (msgtype == prop_atoms.net_showing_desktop) {
549 screen_show_desktop(e->xclient.data.l[0] != 0);
553 if (e->xproperty.atom == prop_atoms.net_desktop_names)
554 screen_update_desktop_names();
555 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
556 screen_update_layout();
558 case ConfigureNotify:
560 XRRUpdateConfiguration(e);
567 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
568 ob_debug("VIDMODE EVENT\n");
574 static void event_handle_client(ObClient *client, XEvent *e)
582 case VisibilityNotify:
583 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
587 /* Wheel buttons don't draw because they are an instant click, so it
588 is a waste of resources to go drawing it. */
589 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
590 switch (frame_context(client, e->xbutton.window)) {
591 case OB_FRAME_CONTEXT_MAXIMIZE:
592 client->frame->max_press = (e->type == ButtonPress);
593 framerender_frame(client->frame);
595 case OB_FRAME_CONTEXT_CLOSE:
596 client->frame->close_press = (e->type == ButtonPress);
597 framerender_frame(client->frame);
599 case OB_FRAME_CONTEXT_ICONIFY:
600 client->frame->iconify_press = (e->type == ButtonPress);
601 framerender_frame(client->frame);
603 case OB_FRAME_CONTEXT_ALLDESKTOPS:
604 client->frame->desk_press = (e->type == ButtonPress);
605 framerender_frame(client->frame);
607 case OB_FRAME_CONTEXT_SHADE:
608 client->frame->shade_press = (e->type == ButtonPress);
609 framerender_frame(client->frame);
612 /* nothing changes with clicks for any other contexts */
619 ob_debug("FocusIn on client for %lx\n", client->window);
621 if (client != focus_client) {
622 focus_set_client(client);
623 frame_adjust_focus(client->frame, TRUE);
628 ob_debug("FocusOut on client for %lx\n", client->window);
630 /* are we a fullscreen window or a transient of one? (checks layer)
631 if we are then we need to be iconified since we are losing focus
633 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
634 !client_search_focus_tree_full(client))
635 /* iconify fullscreen windows when they and their transients
637 client_iconify(client, TRUE, TRUE);
638 frame_adjust_focus(client->frame, FALSE);
641 con = frame_context(client, e->xcrossing.window);
643 case OB_FRAME_CONTEXT_MAXIMIZE:
644 client->frame->max_hover = FALSE;
645 frame_adjust_state(client->frame);
647 case OB_FRAME_CONTEXT_ALLDESKTOPS:
648 client->frame->desk_hover = FALSE;
649 frame_adjust_state(client->frame);
651 case OB_FRAME_CONTEXT_SHADE:
652 client->frame->shade_hover = FALSE;
653 frame_adjust_state(client->frame);
655 case OB_FRAME_CONTEXT_ICONIFY:
656 client->frame->iconify_hover = FALSE;
657 frame_adjust_state(client->frame);
659 case OB_FRAME_CONTEXT_CLOSE:
660 client->frame->close_hover = FALSE;
661 frame_adjust_state(client->frame);
663 case OB_FRAME_CONTEXT_FRAME:
664 /* XXX if doing a 'reconfigure' make sure you kill this timer,
665 maybe all timers.. */
666 if (config_focus_delay) {
667 focus_delay_client = NULL;
668 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
675 con = frame_context(client, e->xcrossing.window);
677 case OB_FRAME_CONTEXT_MAXIMIZE:
678 client->frame->max_hover = TRUE;
679 frame_adjust_state(client->frame);
681 case OB_FRAME_CONTEXT_ALLDESKTOPS:
682 client->frame->desk_hover = TRUE;
683 frame_adjust_state(client->frame);
685 case OB_FRAME_CONTEXT_SHADE:
686 client->frame->shade_hover = TRUE;
687 frame_adjust_state(client->frame);
689 case OB_FRAME_CONTEXT_ICONIFY:
690 client->frame->iconify_hover = TRUE;
691 frame_adjust_state(client->frame);
693 case OB_FRAME_CONTEXT_CLOSE:
694 client->frame->close_hover = TRUE;
695 frame_adjust_state(client->frame);
697 case OB_FRAME_CONTEXT_FRAME:
698 if (client_normal(client)) {
699 if (ob_state() == OB_STATE_STARTING) {
700 /* move it to the top of the focus order */
701 guint desktop = client->desktop;
702 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
703 focus_order[desktop] = g_list_remove(focus_order[desktop],
705 focus_order[desktop] = g_list_prepend(focus_order[desktop],
707 } else if (config_focus_follow) {
709 ob_debug("EnterNotify on %lx, focusing window\n",
712 if (config_focus_delay) {
713 ob_main_loop_timeout_add(ob_main_loop,
717 focus_delay_client = client;
719 client_focus(client);
727 case ConfigureRequest:
729 while (XCheckTypedWindowEvent(ob_display, client->window,
730 ConfigureRequest, &ce)) {
732 /* XXX if this causes bad things.. we can compress config req's
733 with the same mask. */
734 e->xconfigurerequest.value_mask |=
735 ce.xconfigurerequest.value_mask;
736 if (ce.xconfigurerequest.value_mask & CWX)
737 e->xconfigurerequest.x = ce.xconfigurerequest.x;
738 if (ce.xconfigurerequest.value_mask & CWY)
739 e->xconfigurerequest.y = ce.xconfigurerequest.y;
740 if (ce.xconfigurerequest.value_mask & CWWidth)
741 e->xconfigurerequest.width = ce.xconfigurerequest.width;
742 if (ce.xconfigurerequest.value_mask & CWHeight)
743 e->xconfigurerequest.height = ce.xconfigurerequest.height;
744 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
745 e->xconfigurerequest.border_width =
746 ce.xconfigurerequest.border_width;
747 if (ce.xconfigurerequest.value_mask & CWStackMode)
748 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
751 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
752 if (client->iconic || client->shaded) return;
754 /* resize, then move, as specified in the EWMH section 7.7 */
755 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
761 if (e->xconfigurerequest.value_mask & CWBorderWidth)
762 client->border_width = e->xconfigurerequest.border_width;
764 x = (e->xconfigurerequest.value_mask & CWX) ?
765 e->xconfigurerequest.x : client->area.x;
766 y = (e->xconfigurerequest.value_mask & CWY) ?
767 e->xconfigurerequest.y : client->area.y;
768 w = (e->xconfigurerequest.value_mask & CWWidth) ?
769 e->xconfigurerequest.width : client->area.width;
770 h = (e->xconfigurerequest.value_mask & CWHeight) ?
771 e->xconfigurerequest.height : client->area.height;
777 client->frame->size.left + client->frame->size.right;
779 client->frame->size.top + client->frame->size.bottom;
780 client_find_onscreen(client, &newx, &newy, fw, fh,
781 client_normal(client));
782 if (e->xconfigurerequest.value_mask & CWX)
784 if (e->xconfigurerequest.value_mask & CWY)
788 switch (client->gravity) {
789 case NorthEastGravity:
791 corner = OB_CORNER_TOPRIGHT;
793 case SouthWestGravity:
795 corner = OB_CORNER_BOTTOMLEFT;
797 case SouthEastGravity:
798 corner = OB_CORNER_BOTTOMRIGHT;
800 default: /* NorthWest, Static, etc */
801 corner = OB_CORNER_TOPLEFT;
804 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
808 if (e->xconfigurerequest.value_mask & CWStackMode) {
809 switch (e->xconfigurerequest.detail) {
812 stacking_lower(CLIENT_AS_WINDOW(client));
818 stacking_raise(CLIENT_AS_WINDOW(client));
824 if (client->ignore_unmaps) {
825 client->ignore_unmaps--;
828 client_unmanage(client);
831 client_unmanage(client);
834 /* this is when the client is first taken captive in the frame */
835 if (e->xreparent.parent == client->frame->plate) break;
838 This event is quite rare and is usually handled in unmapHandler.
839 However, if the window is unmapped when the reparent event occurs,
840 the window manager never sees it because an unmap event is not sent
841 to an already unmapped window.
844 /* we don't want the reparent event, put it back on the stack for the
845 X server to deal with after we unmanage the window */
846 XPutBackEvent(ob_display, e);
848 client_unmanage(client);
851 ob_debug("MapRequest for 0x%lx\n", client->window);
852 if (!client->iconic) break; /* this normally doesn't happen, but if it
853 does, we don't want it! */
854 if (screen_showing_desktop)
855 screen_show_desktop(FALSE);
856 client_iconify(client, FALSE, TRUE);
857 if (!client->frame->visible)
858 /* if its not visible still, then don't mess with it */
861 client_shade(client, FALSE);
862 client_focus(client);
863 stacking_raise(CLIENT_AS_WINDOW(client));
866 /* validate cuz we query stuff off the client here */
867 if (!client_validate(client)) break;
869 if (e->xclient.format != 32) return;
871 msgtype = e->xclient.message_type;
872 if (msgtype == prop_atoms.wm_change_state) {
873 /* compress changes into a single change */
874 while (XCheckTypedWindowEvent(ob_display, client->window,
876 /* XXX: it would be nice to compress ALL messages of a
877 type, not just messages in a row without other
878 message types between. */
879 if (ce.xclient.message_type != msgtype) {
880 XPutBackEvent(ob_display, &ce);
883 e->xclient = ce.xclient;
885 client_set_wm_state(client, e->xclient.data.l[0]);
886 } else if (msgtype == prop_atoms.net_wm_desktop) {
887 /* compress changes into a single change */
888 while (XCheckTypedWindowEvent(ob_display, client->window,
890 /* XXX: it would be nice to compress ALL messages of a
891 type, not just messages in a row without other
892 message types between. */
893 if (ce.xclient.message_type != msgtype) {
894 XPutBackEvent(ob_display, &ce);
897 e->xclient = ce.xclient;
899 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
900 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
901 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
903 } else if (msgtype == prop_atoms.net_wm_state) {
904 /* can't compress these */
905 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
906 (e->xclient.data.l[0] == 0 ? "Remove" :
907 e->xclient.data.l[0] == 1 ? "Add" :
908 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
909 e->xclient.data.l[1], e->xclient.data.l[2],
911 client_set_state(client, e->xclient.data.l[0],
912 e->xclient.data.l[1], e->xclient.data.l[2]);
913 } else if (msgtype == prop_atoms.net_close_window) {
914 ob_debug("net_close_window for 0x%lx\n", client->window);
915 client_close(client);
916 } else if (msgtype == prop_atoms.net_active_window) {
917 ob_debug("net_active_window for 0x%lx\n", client->window);
918 client_activate(client, FALSE);
919 } else if (msgtype == prop_atoms.net_wm_moveresize) {
920 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
921 if ((Atom)e->xclient.data.l[2] ==
922 prop_atoms.net_wm_moveresize_size_topleft ||
923 (Atom)e->xclient.data.l[2] ==
924 prop_atoms.net_wm_moveresize_size_top ||
925 (Atom)e->xclient.data.l[2] ==
926 prop_atoms.net_wm_moveresize_size_topright ||
927 (Atom)e->xclient.data.l[2] ==
928 prop_atoms.net_wm_moveresize_size_right ||
929 (Atom)e->xclient.data.l[2] ==
930 prop_atoms.net_wm_moveresize_size_right ||
931 (Atom)e->xclient.data.l[2] ==
932 prop_atoms.net_wm_moveresize_size_bottomright ||
933 (Atom)e->xclient.data.l[2] ==
934 prop_atoms.net_wm_moveresize_size_bottom ||
935 (Atom)e->xclient.data.l[2] ==
936 prop_atoms.net_wm_moveresize_size_bottomleft ||
937 (Atom)e->xclient.data.l[2] ==
938 prop_atoms.net_wm_moveresize_size_left ||
939 (Atom)e->xclient.data.l[2] ==
940 prop_atoms.net_wm_moveresize_move ||
941 (Atom)e->xclient.data.l[2] ==
942 prop_atoms.net_wm_moveresize_size_keyboard ||
943 (Atom)e->xclient.data.l[2] ==
944 prop_atoms.net_wm_moveresize_move_keyboard) {
946 moveresize_start(client, e->xclient.data.l[0],
947 e->xclient.data.l[1], e->xclient.data.l[3],
948 e->xclient.data.l[2]);
950 } else if (msgtype == prop_atoms.net_moveresize_window) {
951 int oldg = client->gravity;
952 int tmpg, x, y, w, h;
954 if (e->xclient.data.l[0] & 0xff)
955 tmpg = e->xclient.data.l[0] & 0xff;
959 if (e->xclient.data.l[0] & 1 << 8)
960 x = e->xclient.data.l[1];
963 if (e->xclient.data.l[0] & 1 << 9)
964 y = e->xclient.data.l[2];
967 if (e->xclient.data.l[0] & 1 << 10)
968 w = e->xclient.data.l[3];
970 w = client->area.width;
971 if (e->xclient.data.l[0] & 1 << 11)
972 h = e->xclient.data.l[4];
974 h = client->area.height;
975 client->gravity = tmpg;
981 client->frame->size.left + client->frame->size.right;
983 client->frame->size.top + client->frame->size.bottom;
984 client_find_onscreen(client, &newx, &newy, fw, fh,
985 client_normal(client));
986 if (e->xclient.data.l[0] & 1 << 8)
988 if (e->xclient.data.l[0] & 1 << 9)
992 client_configure(client, OB_CORNER_TOPLEFT,
993 x, y, w, h, FALSE, TRUE);
995 client->gravity = oldg;
999 /* validate cuz we query stuff off the client here */
1000 if (!client_validate(client)) break;
1002 /* compress changes to a single property into a single change */
1003 while (XCheckTypedWindowEvent(ob_display, client->window,
1007 /* XXX: it would be nice to compress ALL changes to a property,
1008 not just changes in a row without other props between. */
1010 a = ce.xproperty.atom;
1011 b = e->xproperty.atom;
1015 if ((a == prop_atoms.net_wm_name ||
1016 a == prop_atoms.wm_name ||
1017 a == prop_atoms.net_wm_icon_name ||
1018 a == prop_atoms.wm_icon_name)
1020 (b == prop_atoms.net_wm_name ||
1021 b == prop_atoms.wm_name ||
1022 b == prop_atoms.net_wm_icon_name ||
1023 b == prop_atoms.wm_icon_name)) {
1026 if ((a == prop_atoms.net_wm_icon ||
1027 a == prop_atoms.kwm_win_icon)
1029 (b == prop_atoms.net_wm_icon ||
1030 b == prop_atoms.kwm_win_icon))
1033 XPutBackEvent(ob_display, &ce);
1037 msgtype = e->xproperty.atom;
1038 if (msgtype == XA_WM_NORMAL_HINTS) {
1039 client_update_normal_hints(client);
1040 /* normal hints can make a window non-resizable */
1041 client_setup_decor_and_functions(client);
1042 } else if (msgtype == XA_WM_HINTS) {
1043 client_update_wmhints(client);
1044 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1045 client_update_transient_for(client);
1046 client_get_type(client);
1047 /* type may have changed, so update the layer */
1048 client_calc_layer(client);
1049 client_setup_decor_and_functions(client);
1050 } else if (msgtype == prop_atoms.net_wm_name ||
1051 msgtype == prop_atoms.wm_name ||
1052 msgtype == prop_atoms.net_wm_icon_name ||
1053 msgtype == prop_atoms.wm_icon_name) {
1054 client_update_title(client);
1055 } else if (msgtype == prop_atoms.wm_class) {
1056 client_update_class(client);
1057 } else if (msgtype == prop_atoms.wm_protocols) {
1058 client_update_protocols(client);
1059 client_setup_decor_and_functions(client);
1061 else if (msgtype == prop_atoms.net_wm_strut) {
1062 client_update_strut(client);
1064 else if (msgtype == prop_atoms.net_wm_icon ||
1065 msgtype == prop_atoms.kwm_win_icon) {
1066 client_update_icons(client);
1071 if (extensions_shape && e->type == extensions_shape_event_basep) {
1072 client->shaped = ((XShapeEvent*)e)->shaped;
1073 frame_adjust_shape(client->frame);
1079 static void event_handle_dock(ObDock *s, XEvent *e)
1083 stacking_raise(DOCK_AS_WINDOW(s));
1094 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1098 dock_app_drag(app, &e->xmotion);
1101 if (app->ignore_unmaps) {
1102 app->ignore_unmaps--;
1105 dock_remove(app, TRUE);
1108 dock_remove(app, FALSE);
1110 case ReparentNotify:
1111 dock_remove(app, FALSE);
1113 case ConfigureNotify:
1114 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1119 ObMenuFrame* find_active_menu()
1124 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1129 return it ? it->data : NULL;
1132 static void event_handle_menu(XEvent *ev)
1135 ObMenuEntryFrame *e;
1139 if (!(f = menu_frame_under(ev->xbutton.x_root,
1140 ev->xbutton.y_root)))
1141 menu_frame_hide_all();
1143 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1144 ev->xbutton.y_root)))
1145 menu_entry_frame_execute(e,
1146 !(ev->xbutton.state & ControlMask));
1150 if ((f = menu_frame_under(ev->xmotion.x_root,
1151 ev->xmotion.y_root))) {
1152 menu_frame_move_on_screen(f);
1153 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1154 ev->xmotion.y_root)))
1155 menu_frame_select(f, e);
1159 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1160 menu_frame_hide_all();
1161 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1163 if ((f = find_active_menu()))
1164 menu_entry_frame_execute(f->selected,
1165 !(ev->xkey.state & ControlMask));
1166 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1168 if ((f = find_active_menu()) && f->parent)
1169 menu_frame_select(f, NULL);
1170 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1172 if ((f = find_active_menu()) && f->child)
1173 menu_frame_select_next(f->child);
1174 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1176 if ((f = find_active_menu()))
1177 menu_frame_select_previous(f);
1178 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1180 if ((f = find_active_menu()))
1181 menu_frame_select_next(f);
1187 static gboolean focus_delay_func(gpointer data)
1189 client_focus(focus_delay_client);
1190 return FALSE; /* no repeat */
1193 static void focus_delay_client_dest(gpointer data)
1196 if (c == focus_delay_client) {
1197 focus_delay_client = NULL;
1198 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);