new least-intrusive focus stealing prevention
This commit is contained in:
parent
074bd96696
commit
6593261f30
6 changed files with 76 additions and 30 deletions
|
@ -214,7 +214,7 @@ void client_manage(Window window)
|
||||||
ObAppSettings *settings;
|
ObAppSettings *settings;
|
||||||
gboolean transient = FALSE;
|
gboolean transient = FALSE;
|
||||||
Rect place, *monitor;
|
Rect place, *monitor;
|
||||||
Time map_time;
|
Time launch_time, map_time;
|
||||||
|
|
||||||
grab_server(TRUE);
|
grab_server(TRUE);
|
||||||
|
|
||||||
|
@ -253,6 +253,8 @@ void client_manage(Window window)
|
||||||
|
|
||||||
ob_debug("Managing window: 0x%lx\n", 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 */
|
/* choose the events we want to receive on the CLIENT window */
|
||||||
attrib_set.event_mask = CLIENT_EVENTMASK;
|
attrib_set.event_mask = CLIENT_EVENTMASK;
|
||||||
attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
|
attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
|
||||||
|
@ -300,7 +302,7 @@ void client_manage(Window window)
|
||||||
client_setup_decor_and_functions(self, FALSE);
|
client_setup_decor_and_functions(self, FALSE);
|
||||||
|
|
||||||
/* tell startup notification that this app started */
|
/* 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
|
/* do this after we have a frame.. it uses the frame to help determine the
|
||||||
WM_STATE to apply. */
|
WM_STATE to apply. */
|
||||||
|
@ -451,6 +453,13 @@ void client_manage(Window window)
|
||||||
if (activate) {
|
if (activate) {
|
||||||
gboolean raise = FALSE;
|
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) {
|
if (menu_frame_visible || moveresize_in_progress) {
|
||||||
activate = FALSE;
|
activate = FALSE;
|
||||||
raise = TRUE;
|
raise = TRUE;
|
||||||
|
@ -464,8 +473,8 @@ void client_manage(Window window)
|
||||||
else if (!(self->desktop == screen_desktop ||
|
else if (!(self->desktop == screen_desktop ||
|
||||||
self->desktop == DESKTOP_ALL) &&
|
self->desktop == DESKTOP_ALL) &&
|
||||||
/* the timestamp is from before you changed desktops */
|
/* the timestamp is from before you changed desktops */
|
||||||
map_time && screen_desktop_user_time &&
|
launch_time && screen_desktop_user_time &&
|
||||||
!event_time_after(map_time, screen_desktop_user_time))
|
!event_time_after(launch_time, screen_desktop_user_time))
|
||||||
{
|
{
|
||||||
activate = FALSE;
|
activate = FALSE;
|
||||||
raise = TRUE;
|
raise = TRUE;
|
||||||
|
@ -477,11 +486,21 @@ void client_manage(Window window)
|
||||||
else if (focus_client && client_search_focus_tree_full(self) == NULL &&
|
else if (focus_client && client_search_focus_tree_full(self) == NULL &&
|
||||||
client_search_focus_group_full(self) == NULL)
|
client_search_focus_group_full(self) == NULL)
|
||||||
{
|
{
|
||||||
/* If its a transient (and parents aren't focused) and the time
|
/* If the user is working in another window right now, then don't
|
||||||
is ambiguous (either the current focus target doesn't have
|
steal focus */
|
||||||
a timestamp, or they are the same (we probably inherited it
|
if (event_last_user_time && launch_time &&
|
||||||
from them) */
|
event_time_after(event_last_user_time, launch_time) &&
|
||||||
if (client_has_parent(self)) {
|
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;
|
activate = FALSE;
|
||||||
ob_debug_type(OB_DEBUG_FOCUS,
|
ob_debug_type(OB_DEBUG_FOCUS,
|
||||||
"Not focusing the window because it is a "
|
"Not focusing the window because it is a "
|
||||||
|
@ -510,6 +529,10 @@ void client_manage(Window window)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!activate) {
|
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
|
/* if the client isn't focused, then hilite it so the user
|
||||||
knows it is there */
|
knows it is there */
|
||||||
client_hilite(self, TRUE);
|
client_hilite(self, TRUE);
|
||||||
|
|
|
@ -97,8 +97,8 @@ static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
|
||||||
static gboolean focus_delay_func(gpointer data);
|
static gboolean focus_delay_func(gpointer data);
|
||||||
static void focus_delay_client_dest(ObClient *client, 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_curtime = CurrentTime;
|
||||||
|
Time event_last_user_time = CurrentTime;
|
||||||
|
|
||||||
static gboolean focus_left_screen = FALSE;
|
static gboolean focus_left_screen = FALSE;
|
||||||
/*! A list of ObSerialRanges which are to be ignored for mouse enter events */
|
/*! 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;
|
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;
|
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) {
|
else if (msgtype == prop_atoms.net_wm_icon_geometry) {
|
||||||
client_update_icon_geometry(client);
|
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
|
#ifdef SYNC
|
||||||
else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
|
else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
|
||||||
client_update_sync_request_counter(client);
|
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 */
|
/* t2 is in the first half so t1 has to come after it */
|
||||||
return t1 >= t2 && t1 < (t2 + TIME_HALF);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -24,8 +24,14 @@
|
||||||
|
|
||||||
struct _ObClient;
|
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. */
|
/*! Time at which the last event with a timestamp occured. */
|
||||||
extern Time event_curtime;
|
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 */
|
/*! The value of the mask for the NumLock modifier */
|
||||||
extern guint NumLockMask;
|
extern guint NumLockMask;
|
||||||
|
@ -55,4 +61,6 @@ void event_halt_focus_delay();
|
||||||
comes at the same time or later than t2. */
|
comes at the same time or later than t2. */
|
||||||
gboolean event_time_after(Time t1, Time t2);
|
gboolean event_time_after(Time t1, Time t2);
|
||||||
|
|
||||||
|
Time event_get_server_time();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -93,7 +93,7 @@ void prop_startup()
|
||||||
CREATE(net_wm_icon_geometry, "_NET_WM_ICON_GEOMETRY");
|
CREATE(net_wm_icon_geometry, "_NET_WM_ICON_GEOMETRY");
|
||||||
/* CREATE(net_wm_pid, "_NET_WM_PID"); */
|
/* CREATE(net_wm_pid, "_NET_WM_PID"); */
|
||||||
CREATE(net_wm_allowed_actions, "_NET_WM_ALLOWED_ACTIONS");
|
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(net_wm_user_time_window, "_NET_WM_USER_TIME_WINDOW"); */
|
||||||
CREATE(kde_net_wm_frame_strut, "_KDE_NET_WM_FRAME_STRUT");
|
CREATE(kde_net_wm_frame_strut, "_KDE_NET_WM_FRAME_STRUT");
|
||||||
CREATE(net_frame_extents, "_NET_FRAME_EXTENTS");
|
CREATE(net_frame_extents, "_NET_FRAME_EXTENTS");
|
||||||
|
|
|
@ -131,7 +131,7 @@ typedef struct Atoms {
|
||||||
Atom net_wm_icon_geometry;
|
Atom net_wm_icon_geometry;
|
||||||
/* Atom net_wm_pid; */
|
/* Atom net_wm_pid; */
|
||||||
Atom net_wm_allowed_actions;
|
Atom net_wm_allowed_actions;
|
||||||
/* Atom net_wm_user_time; */
|
Atom net_wm_user_time;
|
||||||
/* Atom net_wm_user_time_window; */
|
/* Atom net_wm_user_time_window; */
|
||||||
Atom net_frame_extents;
|
Atom net_frame_extents;
|
||||||
|
|
||||||
|
|
|
@ -107,22 +107,7 @@ static gboolean replace_wm()
|
||||||
current_wm_sn_owner = None;
|
current_wm_sn_owner = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
timestamp = event_get_server_time();
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win,
|
XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win,
|
||||||
timestamp);
|
timestamp);
|
||||||
|
@ -172,12 +157,14 @@ gboolean screen_annex()
|
||||||
|
|
||||||
/* create the netwm support window */
|
/* create the netwm support window */
|
||||||
attrib.override_redirect = TRUE;
|
attrib.override_redirect = TRUE;
|
||||||
|
attrib.event_mask = PropertyChangeMask;
|
||||||
screen_support_win = XCreateWindow(ob_display,
|
screen_support_win = XCreateWindow(ob_display,
|
||||||
RootWindow(ob_display, ob_screen),
|
RootWindow(ob_display, ob_screen),
|
||||||
-100, -100, 1, 1, 0,
|
-100, -100, 1, 1, 0,
|
||||||
CopyFromParent, InputOutput,
|
CopyFromParent, InputOutput,
|
||||||
CopyFromParent,
|
CopyFromParent,
|
||||||
CWOverrideRedirect, &attrib);
|
CWEventMask | CWOverrideRedirect,
|
||||||
|
&attrib);
|
||||||
XMapWindow(ob_display, screen_support_win);
|
XMapWindow(ob_display, screen_support_win);
|
||||||
XLowerWindow(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_wm_state_demands_attention;
|
||||||
supported[i++] = prop_atoms.net_moveresize_window;
|
supported[i++] = prop_atoms.net_moveresize_window;
|
||||||
supported[i++] = prop_atoms.net_wm_moveresize;
|
supported[i++] = prop_atoms.net_wm_moveresize;
|
||||||
/*
|
|
||||||
supported[i++] = prop_atoms.net_wm_user_time;
|
supported[i++] = prop_atoms.net_wm_user_time;
|
||||||
|
/*
|
||||||
supported[i++] = prop_atoms.net_wm_user_time_window;
|
supported[i++] = prop_atoms.net_wm_user_time_window;
|
||||||
*/
|
*/
|
||||||
supported[i++] = prop_atoms.net_frame_extents;
|
supported[i++] = prop_atoms.net_frame_extents;
|
||||||
|
|
Loading…
Reference in a new issue