From: Dana Jansens Date: Fri, 3 Aug 2007 22:06:10 +0000 (-0400) Subject: new least-intrusive focus stealing prevention X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=6593261f30d611ff3b71abdb9fd043851fdd2ca9;p=chaz%2Fopenbox new least-intrusive focus stealing prevention --- diff --git a/openbox/client.c b/openbox/client.c index 1931d87e..cc721830 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -214,7 +214,7 @@ void client_manage(Window window) ObAppSettings *settings; gboolean transient = FALSE; Rect place, *monitor; - Time map_time; + Time launch_time, map_time; grab_server(TRUE); @@ -253,6 +253,8 @@ void client_manage(Window window) ob_debug("Managing window: 0x%lx\n", window); + map_time = event_get_server_time(); + /* choose the events we want to receive on the CLIENT window */ attrib_set.event_mask = CLIENT_EVENTMASK; attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; @@ -300,7 +302,7 @@ void client_manage(Window window) client_setup_decor_and_functions(self, FALSE); /* tell startup notification that this app started */ - map_time = sn_app_started(self->startup_id, self->class); + launch_time = sn_app_started(self->startup_id, self->class); /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ @@ -451,6 +453,13 @@ void client_manage(Window window) if (activate) { gboolean raise = FALSE; + /* This is focus stealing prevention */ + ob_debug_type(OB_DEBUG_FOCUS, + "Want to focus new window 0x%x at time %u " + "launched at %u (last user interaction time %u)\n", + self->window, map_time, launch_time, + event_last_user_time); + if (menu_frame_visible || moveresize_in_progress) { activate = FALSE; raise = TRUE; @@ -464,8 +473,8 @@ void client_manage(Window window) else if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) && /* the timestamp is from before you changed desktops */ - map_time && screen_desktop_user_time && - !event_time_after(map_time, screen_desktop_user_time)) + launch_time && screen_desktop_user_time && + !event_time_after(launch_time, screen_desktop_user_time)) { activate = FALSE; raise = TRUE; @@ -477,11 +486,21 @@ void client_manage(Window window) else if (focus_client && client_search_focus_tree_full(self) == NULL && client_search_focus_group_full(self) == NULL) { - /* If its a transient (and parents aren't focused) and the time - is ambiguous (either the current focus target doesn't have - a timestamp, or they are the same (we probably inherited it - from them) */ - if (client_has_parent(self)) { + /* If the user is working in another window right now, then don't + steal focus */ + if (event_last_user_time && launch_time && + event_time_after(event_last_user_time, launch_time) && + event_last_user_time != launch_time && + event_time_after(event_last_user_time, + map_time - OB_EVENT_USER_TIME_DELAY)) + { + activate = FALSE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because the user is " + "working in another window\n"); + } + /* If its a transient (and its parents aren't focused) */ + else if (client_has_parent(self)) { activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because it is a " @@ -510,6 +529,10 @@ void client_manage(Window window) } if (!activate) { + ob_debug_type(OB_DEBUG_FOCUS, + "Focus stealing prevention activated for %s at " + "time %u (last user interactioon time %u)\n", + self->title, map_time, event_last_user_time); /* if the client isn't focused, then hilite it so the user knows it is there */ client_hilite(self, TRUE); diff --git a/openbox/event.c b/openbox/event.c index 5d01a0f6..62778bce 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -97,8 +97,8 @@ 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); -/* The time for the current event being processed */ Time event_curtime = CurrentTime; +Time event_last_user_time = CurrentTime; static gboolean focus_left_screen = FALSE; /*! A list of ObSerialRanges which are to be ignored for mouse enter events */ @@ -236,6 +236,12 @@ static void event_set_curtime(XEvent *e) break; } + /* watch that if we get an event earlier than the last specified user_time, + which can happen if the clock goes backwards, we erase the last + specified user_time */ + if (t && event_last_user_time && event_time_after(event_last_user_time, t)) + event_last_user_time = CurrentTime; + event_curtime = t; } @@ -1496,6 +1502,16 @@ static void event_handle_client(ObClient *client, XEvent *e) else if (msgtype == prop_atoms.net_wm_icon_geometry) { client_update_icon_geometry(client); } + else if (msgtype == prop_atoms.net_wm_user_time) { + guint32 t; + if (PROP_GET32(client->window, net_wm_user_time, cardinal, &t) && + t && !event_time_after(t, e->xproperty.time) && + (!event_last_user_time || + event_time_after(t, event_last_user_time))) + { + event_last_user_time = t; + } + } #ifdef SYNC else if (msgtype == prop_atoms.net_wm_sync_request_counter) { client_update_sync_request_counter(client); @@ -1934,3 +1950,15 @@ gboolean event_time_after(Time t1, Time t2) /* t2 is in the first half so t1 has to come after it */ return t1 >= t2 && t1 < (t2 + TIME_HALF); } + +Time event_get_server_time() +{ + /* Generate a timestamp */ + XEvent event; + + XChangeProperty(ob_display, screen_support_win, + prop_atoms.wm_class, prop_atoms.string, + 8, PropModeAppend, NULL, 0); + XWindowEvent(ob_display, screen_support_win, PropertyChangeMask, &event); + return event.xproperty.time; +} diff --git a/openbox/event.h b/openbox/event.h index 5c5290f0..a8d1aeaf 100644 --- a/openbox/event.h +++ b/openbox/event.h @@ -24,8 +24,14 @@ struct _ObClient; +/*! The amount of time before a window appears that is checked for user input + to determine if the user is working in another window */ +#define OB_EVENT_USER_TIME_DELAY (500) /* 0.5 seconds */ + /*! Time at which the last event with a timestamp occured. */ extern Time event_curtime; +/*! The last user-interaction time, as given by the clients */ +extern Time event_last_user_time; /*! The value of the mask for the NumLock modifier */ extern guint NumLockMask; @@ -55,4 +61,6 @@ void event_halt_focus_delay(); comes at the same time or later than t2. */ gboolean event_time_after(Time t1, Time t2); +Time event_get_server_time(); + #endif diff --git a/openbox/prop.c b/openbox/prop.c index ed094d9b..40ae6ef2 100644 --- a/openbox/prop.c +++ b/openbox/prop.c @@ -93,7 +93,7 @@ void prop_startup() CREATE(net_wm_icon_geometry, "_NET_WM_ICON_GEOMETRY"); /* CREATE(net_wm_pid, "_NET_WM_PID"); */ CREATE(net_wm_allowed_actions, "_NET_WM_ALLOWED_ACTIONS"); -/* CREATE(net_wm_user_time, "_NET_WM_USER_TIME"); */ + CREATE(net_wm_user_time, "_NET_WM_USER_TIME"); /* CREATE(net_wm_user_time_window, "_NET_WM_USER_TIME_WINDOW"); */ CREATE(kde_net_wm_frame_strut, "_KDE_NET_WM_FRAME_STRUT"); CREATE(net_frame_extents, "_NET_FRAME_EXTENTS"); diff --git a/openbox/prop.h b/openbox/prop.h index 9aff28ce..f0c4f5e9 100644 --- a/openbox/prop.h +++ b/openbox/prop.h @@ -131,7 +131,7 @@ typedef struct Atoms { Atom net_wm_icon_geometry; /* Atom net_wm_pid; */ Atom net_wm_allowed_actions; -/* Atom net_wm_user_time; */ + Atom net_wm_user_time; /* Atom net_wm_user_time_window; */ Atom net_frame_extents; diff --git a/openbox/screen.c b/openbox/screen.c index 8bab662f..79008e75 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -107,22 +107,7 @@ static gboolean replace_wm() current_wm_sn_owner = None; } - { - /* Generate a timestamp */ - XEvent event; - - XSelectInput(ob_display, screen_support_win, PropertyChangeMask); - - XChangeProperty(ob_display, screen_support_win, - prop_atoms.wm_class, prop_atoms.string, - 8, PropModeAppend, NULL, 0); - XWindowEvent(ob_display, screen_support_win, - PropertyChangeMask, &event); - - XSelectInput(ob_display, screen_support_win, NoEventMask); - - timestamp = event.xproperty.time; - } + timestamp = event_get_server_time(); XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win, timestamp); @@ -172,12 +157,14 @@ gboolean screen_annex() /* create the netwm support window */ attrib.override_redirect = TRUE; + attrib.event_mask = PropertyChangeMask; screen_support_win = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen), -100, -100, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, - CWOverrideRedirect, &attrib); + CWEventMask | CWOverrideRedirect, + &attrib); XMapWindow(ob_display, screen_support_win); XLowerWindow(ob_display, screen_support_win); @@ -282,8 +269,8 @@ gboolean screen_annex() supported[i++] = prop_atoms.net_wm_state_demands_attention; supported[i++] = prop_atoms.net_moveresize_window; supported[i++] = prop_atoms.net_wm_moveresize; -/* supported[i++] = prop_atoms.net_wm_user_time; +/* supported[i++] = prop_atoms.net_wm_user_time_window; */ supported[i++] = prop_atoms.net_frame_extents;