X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fevent.c;h=759c2e22695412926f9b5eeffa45f0acb9ec585b;hb=5316bc6c3e3329dc620cfd15d472ed5d376100f8;hp=c0e6f96c39578a2ebb9d43eec136d20a3d219111;hpb=335e8acbfc5d2824f1fd2cb67a5add8e1ed40b06;p=chaz%2Fopenbox diff --git a/openbox/event.c b/openbox/event.c index c0e6f96c..759c2e22 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -1,19 +1,20 @@ /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- -event.c for the Openbox window manager -Copyright (c) 2003 Ben Jansens + event.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -See the COPYING file for a copy of the GNU General Public License. + See the COPYING file for a copy of the GNU General Public License. */ #include "event.h" @@ -50,6 +51,9 @@ See the COPYING file for a copy of the GNU General Public License. #ifdef HAVE_SIGNAL_H # include #endif +#ifdef XKB +# include +#endif #ifdef USE_SM #include @@ -60,8 +64,13 @@ typedef struct gboolean ignored; } ObEventData; +typedef struct +{ + ObClient *client; + Time time; +} ObFocusDelayData; + static void event_process(const XEvent *e, gpointer data); -static void event_client_dest(ObClient *client, gpointer data); static void event_handle_root(XEvent *e); static void event_handle_menu(XEvent *e); static void event_handle_dock(ObDock *s, XEvent *e); @@ -69,20 +78,15 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); static void event_handle_group(ObGroup *g, XEvent *e); +static void focus_delay_dest(gpointer data); +static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2); static gboolean focus_delay_func(gpointer data); static void focus_delay_client_dest(ObClient *client, gpointer data); static gboolean menu_hide_delay_func(gpointer data); -#define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \ - (e)->xfocus.detail == NotifyAncestor || \ - (e)->xfocus.detail > NotifyNonlinearVirtual) -#define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \ - (e)->xfocus.detail == NotifyInferior || \ - (e)->xfocus.detail == NotifyAncestor || \ - (e)->xfocus.detail > NotifyNonlinearVirtual) - -Time event_lasttime = 0; +/* The time for the current event being processed */ +Time event_curtime = CurrentTime; /*! The value of the mask for the NumLock modifier */ guint NumLockMask; @@ -159,7 +163,6 @@ void event_startup(gboolean reconfig) #endif client_add_destructor(focus_delay_client_dest, NULL); - client_add_destructor(event_client_dest, NULL); } void event_shutdown(gboolean reconfig) @@ -214,9 +217,9 @@ static Window event_get_window(XEvent *e) return window; } -static void event_set_lasttime(XEvent *e) +static void event_set_curtime(XEvent *e) { - Time t = 0; + Time t = CurrentTime; /* grab the lasttime and hack up the state */ switch (e->type) { @@ -246,8 +249,7 @@ static void event_set_lasttime(XEvent *e) break; } - if (t > event_lasttime) - event_lasttime = t; + event_curtime = t; } #define STRIP_MODS(s) \ @@ -258,6 +260,9 @@ static void event_set_lasttime(XEvent *e) static void event_hack_mods(XEvent *e) { +#ifdef XKB + XkbStateRec xkb_state; +#endif KeyCode *kp; gint i, k; @@ -273,6 +278,12 @@ static void event_hack_mods(XEvent *e) STRIP_MODS(e->xkey.state); /* remove from the state the mask of the modifier being released, if it is a modifier key being released (this is a little ugly..) */ +#ifdef XKB + if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) { + e->xkey.state = xkb_state.compat_state; + break; + } +#endif kp = modmap->modifiermap; for (i = 0; i < mask_table_size; ++i) { for (k = 0; k < modmap->max_keypermod; ++k) { @@ -302,131 +313,89 @@ static void event_hack_mods(XEvent *e) } } +static gboolean wanted_focusevent(XEvent *e) +{ + gint mode = e->xfocus.mode; + gint detail = e->xfocus.detail; + Window win = e->xany.window; + + if (e->type == FocusIn) { + + /* These are ones we never want.. */ + + /* This means focus was given by a keyboard/mouse grab. */ + if (mode == NotifyGrab) + return FALSE; + /* This means focus was given back from a keyboard/mouse grab. */ + if (mode == NotifyUngrab) + return FALSE; + + /* These are the ones we want.. */ + + if (win == RootWindow(ob_display, ob_screen)) { + /* This means focus reverted off of a client */ + if (detail == NotifyPointerRoot || detail == NotifyDetailNone || + detail == NotifyInferior) + return TRUE; + else + return FALSE; + } + + /* This means focus moved from the root window to a client */ + if (detail == NotifyVirtual) + return TRUE; + /* This means focus moved from one client to another */ + if (detail == NotifyNonlinearVirtual) + return TRUE; + + /* Otherwise.. */ + return FALSE; + } else { + g_assert(e->type == FocusOut); + + + /* These are ones we never want.. */ + + /* This means focus was taken by a keyboard/mouse grab. */ + if (mode == NotifyGrab) + return FALSE; + + /* Focus left the root window revertedto state */ + if (win == RootWindow(ob_display, ob_screen)) + return FALSE; + + /* These are the ones we want.. */ + + /* This means focus moved from a client to the root window */ + if (detail == NotifyVirtual) + return TRUE; + /* This means focus moved from one client to another */ + if (detail == NotifyNonlinearVirtual) + return TRUE; + /* This means focus had moved to our frame window and now moved off */ + if (detail == NotifyNonlinear) + return TRUE; + + /* Otherwise.. */ + return FALSE; + } +} + +static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg) +{ + return e->type == FocusIn && wanted_focusevent(e); +} + static gboolean event_ignore(XEvent *e, ObClient *client) { switch(e->type) { - case EnterNotify: - case LeaveNotify: - if (e->xcrossing.detail == NotifyInferior) - return TRUE; - break; case FocusIn: - /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut - because of RevertToPointerRoot. If the focus ends up reverting to - pointer root on a workspace change, then the FocusIn event that we - want will be of type NotifyAncestor. This situation does not occur - for FocusOut, so it is safely ignored there. - */ - if (INVALID_FOCUSIN(e) || - client == NULL) { -#ifdef DEBUG_FOCUS - ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n", - e->xfocus.window, e->xfocus.mode, e->xfocus.detail); -#endif - /* says a client was not found for the event (or a valid FocusIn - event was not found. - */ - e->xfocus.window = None; + if (!wanted_focusevent(e)) return TRUE; - } - -#ifdef DEBUG_FOCUS - ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window, - e->xfocus.mode, e->xfocus.detail); -#endif break; case FocusOut: - if (INVALID_FOCUSOUT(e)) { -#ifdef DEBUG_FOCUS - ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n", - e->xfocus.window, e->xfocus.mode, e->xfocus.detail); -#endif + if (!wanted_focusevent(e)) return TRUE; - } - -#ifdef DEBUG_FOCUS - ob_debug("FocusOut on %lx mode %d detail %d\n", - e->xfocus.window, e->xfocus.mode, e->xfocus.detail); -#endif - { - XEvent fe; - gboolean fallback = TRUE; - - while (TRUE) { - if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window, - FocusOut, &fe)) - if (!XCheckTypedEvent(ob_display, FocusIn, &fe)) - break; - if (fe.type == FocusOut) { -#ifdef DEBUG_FOCUS - ob_debug("found pending FocusOut\n"); -#endif - if (!INVALID_FOCUSOUT(&fe)) { - /* if there is a VALID FocusOut still coming, don't - fallback focus yet, we'll deal with it then */ - XPutBackEvent(ob_display, &fe); - fallback = FALSE; - break; - } - } else { -#ifdef DEBUG_FOCUS - ob_debug("found pending FocusIn\n"); -#endif - /* is the focused window getting a FocusOut/In back to - itself? - */ - if (fe.xfocus.window == e->xfocus.window && - !event_ignore(&fe, client)) { - /* - if focus_client is not set, then we can't do - this. we need the FocusIn. This happens in the - case when the set_focus_client(NULL) in the - focus_fallback function fires and then - focus_fallback picks the currently focused - window (such as on a SendToDesktop-esque action. - */ - if (focus_client) { -#ifdef DEBUG_FOCUS - ob_debug("focused window got an Out/In back to " - "itself IGNORED both\n"); -#endif - return TRUE; - } else { - event_process(&fe, NULL); -#ifdef DEBUG_FOCUS - ob_debug("focused window got an Out/In back to " - "itself but focus_client was null " - "IGNORED just the Out\n"); -#endif - return TRUE; - } - } - - { - ObEventData d; - - /* once all the FocusOut's have been dealt with, if - there is a FocusIn still left and it is valid, then - use it */ - event_process(&fe, &d); - if (!d.ignored) { -#ifdef DEBUG_FOCUS - ob_debug("FocusIn was OK, so don't fallback\n"); -#endif - fallback = FALSE; - break; - } - } - } - } - if (fallback) { -#ifdef DEBUG_FOCUS - ob_debug("no valid FocusIn and no FocusOut events found, " - "falling back\n"); -#endif - focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS); - } - } break; } return FALSE; @@ -469,7 +438,7 @@ static void event_process(const XEvent *ec, gpointer data) } } - event_set_lasttime(e); + event_set_curtime(e); event_hack_mods(e); if (event_ignore(e, client)) { if (ed) @@ -479,7 +448,64 @@ static void event_process(const XEvent *ec, gpointer data) ed->ignored = FALSE; /* deal with it in the kernel */ - if (group) + + if (menu_frame_visible && + (e->type == EnterNotify || e->type == LeaveNotify)) + { + /* crossing events for menu */ + event_handle_menu(e); + } else if (e->type == FocusIn) { + if (client && client != focus_client) { + frame_adjust_focus(client->frame, TRUE); + focus_set_client(client); + client_calc_layer(client); + } + } else if (e->type == FocusOut) { + gboolean nomove = FALSE; + XEvent ce; + + ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n"); + + /* Look for the followup FocusIn */ + if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) { + /* There is no FocusIn, this means focus went to a window that + is not being managed, or a window on another screen. */ + ob_debug_type(OB_DEBUG_FOCUS, "Focus went to a black hole !\n"); + /* nothing is focused */ + focus_set_client(NULL); + } else if (ce.xany.window == e->xany.window) { + ob_debug_type(OB_DEBUG_FOCUS, "Focus didn't go anywhere\n"); + /* If focus didn't actually move anywhere, there is nothing to do*/ + nomove = TRUE; + } else if (ce.xfocus.detail == NotifyPointerRoot || + ce.xfocus.detail == NotifyDetailNone || + ce.xfocus.detail == NotifyInferior) { + ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root\n"); + /* Focus has been reverted to the root window or nothing + FocusOut events come after UnmapNotify, so we don't need to + worry about focusing an invalid window + */ + focus_fallback(TRUE); + } else { + /* Focus did move, so process the FocusIn event */ + ObEventData ed = { .ignored = FALSE }; + event_process(&ce, &ed); + if (ed.ignored) { + /* The FocusIn was ignored, this means it was on a window + that isn't a client. */ + ob_debug_type(OB_DEBUG_FOCUS, + "Focus went to an unmanaged window 0x%x !\n", + ce.xfocus.window); + focus_fallback(TRUE); + } + } + + if (client && !nomove) { + frame_adjust_focus(client->frame, FALSE); + /* focus_set_client has already been called for sure */ + client_calc_layer(client); + } + } else if (group) event_handle_group(group, e); else if (client) event_handle_client(client, e); @@ -533,18 +559,21 @@ static void event_process(const XEvent *ec, gpointer data) ob_main_loop_timeout_add(ob_main_loop, config_menu_hide_delay * 1000, menu_hide_delay_func, - NULL, NULL); + NULL, g_direct_equal, NULL); if (e->type == ButtonPress || e->type == ButtonRelease || - e->type == MotionNotify) + e->type == MotionNotify) { mouse_event(client, e); - else if (e->type == KeyPress) + } else if (e->type == KeyPress) { keyboard_event((focus_cycle_target ? focus_cycle_target : - (focus_hilite ? focus_hilite : client)), - e); + client), e); + } } } } + /* if something happens and it's not from an XEvent, then we don't know + the time */ + event_curtime = CurrentTime; } static void event_handle_root(XEvent *e) @@ -554,7 +583,7 @@ static void event_handle_root(XEvent *e) switch(e->type) { case SelectionClear: ob_debug("Another WM has requested to replace us. Exiting.\n"); - ob_exit(0); + ob_exit_replace(); break; case ClientMessage: @@ -563,14 +592,22 @@ static void event_handle_root(XEvent *e) msgtype = e->xclient.message_type; if (msgtype == prop_atoms.net_current_desktop) { guint d = e->xclient.data.l[0]; - if (d < screen_num_desktops) + if (d < screen_num_desktops) { + event_curtime = e->xclient.data.l[1]; + ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime); screen_set_desktop(d); + } } else if (msgtype == prop_atoms.net_number_of_desktops) { guint d = e->xclient.data.l[0]; if (d > 0) screen_set_num_desktops(d); } else if (msgtype == prop_atoms.net_showing_desktop) { screen_show_desktop(e->xclient.data.l[0] != 0); + } else if (msgtype == prop_atoms.ob_control) { + if (e->xclient.data.l[0] == 1) + ob_reconfigure(); + else if (e->xclient.data.l[0] == 2) + ob_restart(); } break; case PropertyNotify: @@ -587,11 +624,6 @@ static void event_handle_root(XEvent *e) break; default: ; -#ifdef VIDMODE - if (extensions_vidmode && e->type == extensions_vidmode_event_basep) { - ob_debug("VIDMODE EVENT\n"); - } -#endif } } @@ -611,13 +643,24 @@ void event_enter_client(ObClient *client) if (client_normal(client) && client_can_focus(client)) { if (config_focus_delay) { + ObFocusDelayData *data; + ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func); + + data = g_new(ObFocusDelayData, 1); + data->client = client; + data->time = event_curtime; + ob_main_loop_timeout_add(ob_main_loop, config_focus_delay, focus_delay_func, - client, NULL); - } else - focus_delay_func(client); + data, focus_delay_cmp, focus_delay_dest); + } else { + ObFocusDelayData data; + data.client = client; + data.time = event_curtime; + focus_delay_func(&data); + } } } @@ -629,9 +672,6 @@ static void event_handle_client(ObClient *client, XEvent *e) ObFrameContext con; switch (e->type) { - case VisibilityNotify: - client->frame->obscured = e->xvisibility.state != VisibilityUnobscured; - break; case ButtonPress: case ButtonRelease: /* Wheel buttons don't draw because they are an instant click, so it @@ -666,28 +706,6 @@ static void event_handle_client(ObClient *client, XEvent *e) } } break; - case FocusIn: -#ifdef DEBUG_FOCUS - ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n", - e->xfocus.window, client->window, - e->xfocus.mode, e->xfocus.detail); -#endif - if (client != focus_client) { - focus_set_client(client); - frame_adjust_focus(client->frame, TRUE); - client_calc_layer(client); - } - break; - case FocusOut: -#ifdef DEBUG_FOCUS - ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n", - e->xfocus.window, client->window, - e->xfocus.mode, e->xfocus.detail); -#endif - focus_hilite = NULL; - frame_adjust_focus(client->frame, FALSE); - client_calc_layer(client); - break; case LeaveNotify: con = frame_context(client, e->xcrossing.window); switch (con) { @@ -712,10 +730,23 @@ static void event_handle_client(ObClient *client, XEvent *e) frame_adjust_state(client->frame); break; case OB_FRAME_CONTEXT_FRAME: - if (config_focus_follow && config_focus_delay) + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d on %lx\n", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, (client?client->window:0)); + if (keyboard_interactively_grabbed()) + break; + if (config_focus_follow && config_focus_delay && + /* leaveinferior events can happen when the mouse goes onto the + window's border and then into the window before the delay + is up */ + e->xcrossing.detail != NotifyInferior) + { ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, - client); + client, FALSE); + } break; default: break; @@ -753,24 +784,24 @@ static void event_handle_client(ObClient *client, XEvent *e) frame_adjust_state(client->frame); break; case OB_FRAME_CONTEXT_FRAME: + if (keyboard_interactively_grabbed()) + break; if (e->xcrossing.mode == NotifyGrab || e->xcrossing.mode == NotifyUngrab) { -#ifdef DEBUG_FOCUS - ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n", - (e->type == EnterNotify ? "Enter" : "Leave"), - e->xcrossing.mode, - e->xcrossing.detail, client?client->window:0); -#endif + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d on %lx IGNORED\n", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, client?client->window:0); } else { -#ifdef DEBUG_FOCUS - ob_debug("%sNotify mode %d detail %d on %lx, " - "focusing window: %d\n", - (e->type == EnterNotify ? "Enter" : "Leave"), - e->xcrossing.mode, - e->xcrossing.detail, (client?client->window:0), - !nofocus); -#endif + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d on %lx, " + "focusing window: %d\n", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, (client?client->window:0), + !nofocus); if (!nofocus && config_focus_follow) event_enter_client(client); } @@ -833,8 +864,13 @@ static void event_handle_client(ObClient *client, XEvent *e) client->frame->size.left + client->frame->size.right; gint fh = h + client->frame->size.top + client->frame->size.bottom; + /* make this rude for size-only changes but not for position + changes.. */ + gboolean moving = ((e->xconfigurerequest.value_mask & CWX) || + (e->xconfigurerequest.value_mask & CWY)); + client_find_onscreen(client, &newx, &newy, fw, fh, - client_normal(client)); + !moving); if (e->xconfigurerequest.value_mask & CWX) x = newx; if (e->xconfigurerequest.value_mask & CWY) @@ -865,13 +901,17 @@ static void event_handle_client(ObClient *client, XEvent *e) switch (e->xconfigurerequest.detail) { case Below: case BottomIf: - client_lower(client); + /* Apps are so rude. And this is totally disconnected from + activation/focus. Bleh. */ + /*client_lower(client);*/ break; case Above: case TopIf: default: - client_raise(client); + /* Apps are so rude. And this is totally disconnected from + activation/focus. Bleh. */ + /*client_raise(client);*/ break; } } @@ -881,9 +921,14 @@ static void event_handle_client(ObClient *client, XEvent *e) client->ignore_unmaps--; break; } + ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d " + "ignores left %d\n", + client->window, e->xunmap.event, e->xunmap.from_configure, + client->ignore_unmaps); client_unmanage(client); break; case DestroyNotify: + ob_debug("DestroyNotify for window 0x%x\n", client->window); client_unmanage(client); break; case ReparentNotify: @@ -901,6 +946,7 @@ static void event_handle_client(ObClient *client, XEvent *e) X server to deal with after we unmanage the window */ XPutBackEvent(ob_display, e); + ob_debug("ReparentNotify for window 0x%x\n", client->window); client_unmanage(client); break; case MapRequest: @@ -910,7 +956,7 @@ static void event_handle_client(ObClient *client, XEvent *e) it can happen now when the window is on another desktop, but we still don't want it! */ - client_activate(client, FALSE); + client_activate(client, FALSE, TRUE); break; case ClientMessage: /* validate cuz we query stuff off the client here */ @@ -964,10 +1010,19 @@ static void event_handle_client(ObClient *client, XEvent *e) ob_debug("net_close_window for 0x%lx\n", client->window); client_close(client); } else if (msgtype == prop_atoms.net_active_window) { - ob_debug("net_active_window for 0x%lx\n", client->window); - client_activate(client, FALSE); + ob_debug("net_active_window for 0x%lx source=%s\n", + client->window, + (e->xclient.data.l[0] == 0 ? "unknown" : + (e->xclient.data.l[0] == 1 ? "application" : + (e->xclient.data.l[0] == 2 ? "user" : "INVALID")))); + /* XXX make use of data.l[2] ! */ + event_curtime = e->xclient.data.l[1]; + client_activate(client, FALSE, + (e->xclient.data.l[0] == 0 || + e->xclient.data.l[0] == 2)); } else if (msgtype == prop_atoms.net_wm_moveresize) { - ob_debug("net_wm_moveresize for 0x%lx\n", client->window); + ob_debug("net_wm_moveresize for 0x%lx direction %d\n", + client->window, e->xclient.data.l[2]); if ((Atom)e->xclient.data.l[2] == prop_atoms.net_wm_moveresize_size_topleft || (Atom)e->xclient.data.l[2] == @@ -997,6 +1052,9 @@ static void event_handle_client(ObClient *client, XEvent *e) e->xclient.data.l[1], e->xclient.data.l[3], e->xclient.data.l[2]); } + else if ((Atom)e->xclient.data.l[2] == + prop_atoms.net_wm_moveresize_cancel) + moveresize_end(TRUE); } else if (msgtype == prop_atoms.net_moveresize_window) { gint oldg = client->gravity; gint tmpg, x, y, w, h; @@ -1073,11 +1131,8 @@ static void event_handle_client(ObClient *client, XEvent *e) b == prop_atoms.wm_icon_name)) { continue; } - if ((a == prop_atoms.net_wm_icon || - a == prop_atoms.kwm_win_icon) - && - (b == prop_atoms.net_wm_icon || - b == prop_atoms.kwm_win_icon)) + if (a == prop_atoms.net_wm_icon && + b == prop_atoms.net_wm_icon) continue; XPutBackEvent(ob_display, &ce); @@ -1111,10 +1166,12 @@ static void event_handle_client(ObClient *client, XEvent *e) else if (msgtype == prop_atoms.net_wm_strut) { client_update_strut(client); } - else if (msgtype == prop_atoms.net_wm_icon || - msgtype == prop_atoms.kwm_win_icon) { + else if (msgtype == prop_atoms.net_wm_icon) { client_update_icons(client); } + else if (msgtype == prop_atoms.net_wm_user_time) { + client_update_user_time(client); + } else if (msgtype == prop_atoms.sm_client_id) { client_update_sm_client_id(client); } @@ -1133,7 +1190,10 @@ static void event_handle_dock(ObDock *s, XEvent *e) { switch (e->type) { case ButtonPress: - stacking_raise(DOCK_AS_WINDOW(s)); + if (e->xbutton.button == 1) + stacking_raise(DOCK_AS_WINDOW(s)); + else if (e->xbutton.button == 2) + stacking_lower(DOCK_AS_WINDOW(s)); break; case EnterNotify: dock_hide(FALSE); @@ -1203,29 +1263,31 @@ static void event_handle_menu(XEvent *ev) if (menu_can_hide) { if ((e = menu_entry_frame_under(ev->xbutton.x_root, ev->xbutton.y_root))) - menu_entry_frame_execute(e, ev->xbutton.state); + menu_entry_frame_execute(e, ev->xbutton.state, + ev->xbutton.time); else menu_frame_hide_all(); } break; - case MotionNotify: - if ((f = menu_frame_under(ev->xmotion.x_root, - ev->xmotion.y_root))) { - menu_frame_move_on_screen(f); - if ((e = menu_entry_frame_under(ev->xmotion.x_root, - ev->xmotion.y_root))) - menu_frame_select(f, e); + case EnterNotify: + if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) { + if (e->ignore_enters) + --e->ignore_enters; + else + menu_frame_select(e->frame, e); } + break; + case LeaveNotify: + if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) && + (f = find_active_menu()) && f->selected == e && + e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU) { - ObMenuFrame *a; - - a = find_active_menu(); - if (a && a != f && - a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU) - { - menu_frame_select(a, NULL); - } + menu_frame_select(e->frame, NULL); } + case MotionNotify: + if ((e = menu_entry_frame_under(ev->xmotion.x_root, + ev->xmotion.y_root))) + menu_frame_select(e->frame, e); break; case KeyPress: if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) @@ -1233,7 +1295,8 @@ static void event_handle_menu(XEvent *ev) else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) { ObMenuFrame *f; if ((f = find_active_menu())) - menu_entry_frame_execute(f->selected, ev->xkey.state); + menu_entry_frame_execute(f->selected, ev->xkey.state, + ev->xkey.time); } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) { ObMenuFrame *f; if ((f = find_active_or_last_menu()) && f->parent) @@ -1261,27 +1324,35 @@ static gboolean menu_hide_delay_func(gpointer data) return FALSE; /* no repeat */ } +static void focus_delay_dest(gpointer data) +{ + g_free(data); +} + +static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2) +{ + const ObFocusDelayData *f1 = d1; + return f1->client == d2; +} + static gboolean focus_delay_func(gpointer data) { - ObClient *c = data; + ObFocusDelayData *d = data; + Time old = event_curtime; - if (focus_client != c) { - client_focus(c); - if (config_focus_raise) - client_raise(c); + event_curtime = d->time; + if (focus_client != d->client) { + if (client_focus(d->client) && config_focus_raise) + client_raise(d->client); } + event_curtime = old; return FALSE; /* no repeat */ } static void focus_delay_client_dest(ObClient *client, gpointer data) { - ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client); -} - -static void event_client_dest(ObClient *client, gpointer data) -{ - if (client == focus_hilite) - focus_hilite = NULL; + ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, + client, FALSE); } void event_halt_focus_delay() @@ -1319,3 +1390,28 @@ void event_ignore_queued_enters() } g_slist_free(saved); } + +gboolean event_time_after(Time t1, Time t2) +{ + g_assert(t1 != CurrentTime); + g_assert(t2 != CurrentTime); + + /* + Timestamp values wrap around (after about 49.7 days). The server, given + its current time is represented by timestamp T, always interprets + timestamps from clients by treating half of the timestamp space as being + later in time than T. + - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html + */ + + /* TIME_HALF is half of the number space of a Time type variable */ +#define TIME_HALF (Time)(1 << (sizeof(Time)*8-1)) + + if (t2 >= TIME_HALF) + /* t2 is in the second half so t1 might wrap around and be smaller than + t2 */ + return t1 >= t2 || t1 < (t2 + TIME_HALF); + else + /* t2 is in the first half so t1 has to come after it */ + return t1 >= t2 && t1 < (t2 + TIME_HALF); +}