From 34c217acc878f200614ea756501863daba6da600 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Wed, 9 Oct 2019 02:31:52 +0300 Subject: [PATCH 1/2] Introduce con_activate_unblock --- include/con.h | 7 +++++++ src/commands.c | 36 ++---------------------------------- src/con.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/include/con.h b/include/con.h index 2d843eeb2..d83300986 100644 --- a/include/con.h +++ b/include/con.h @@ -45,6 +45,13 @@ void con_focus(Con *con); */ void con_activate(Con *con); +/** + * Activates the container like in con_activate but removes fullscreen + * restrictions and properly warps the pointer if needed. + * + */ +void con_activate_unblock(Con *con); + /** * Closes the given container. * diff --git a/src/commands.c b/src/commands.c index 5b11fce1c..b4f2ee19c 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1238,20 +1238,6 @@ void cmd_focus_direction(I3_CMD, const char *direction) { ysuccess(true); } -/* - * Focus a container and disable any other fullscreen container not permitting the focus. - * - */ -static void cmd_focus_force_focus(Con *con) { - /* Disable fullscreen container in workspace with container to be focused. */ - Con *ws = con_get_workspace(con); - Con *fullscreen_on_ws = con_get_fullscreen_covering_ws(ws); - if (fullscreen_on_ws && fullscreen_on_ws != con && !con_has_parent(con, fullscreen_on_ws)) { - con_disable_fullscreen(fullscreen_on_ws); - } - con_activate(con); -} - /* * Implementation of 'focus tiling|floating|mode_toggle'. * @@ -1276,7 +1262,7 @@ void cmd_focus_window_mode(I3_CMD, const char *window_mode) { (!to_floating && current->type == CT_FLOATING_CON)) continue; - cmd_focus_force_focus(con_descend_focused(current)); + con_activate_unblock(con_descend_focused(current)); success = true; break; } @@ -1353,26 +1339,8 @@ void cmd_focus(I3_CMD) { break; } - /* If the container is not on the current workspace, - * workspace_show() will switch to a different workspace and (if - * enabled) trigger a mouse pointer warp to the currently focused - * container (!) on the target workspace. - * - * Therefore, before calling workspace_show(), we make sure that - * 'current' will be focused on the workspace. However, we cannot - * just con_focus(current) because then the pointer will not be - * warped at all (the code thinks we are already there). - * - * So we focus 'current' to make it the currently focused window of - * the target workspace, then revert focus. */ - Con *currently_focused = focused; - cmd_focus_force_focus(current->con); - con_activate(currently_focused); - - /* Now switch to the workspace, then focus */ - workspace_show(ws); LOG("focusing %p / %s\n", current->con, current->con->name); - con_activate(current->con); + con_activate_unblock(current->con); count++; } diff --git a/src/con.c b/src/con.c index e9f82cf4f..e0993aded 100644 --- a/src/con.c +++ b/src/con.c @@ -265,6 +265,41 @@ void con_activate(Con *con) { con_raise(con); } +/* + * Activates the container like in con_activate but removes fullscreen + * restrictions and properly warps the pointer if needed. + * + */ +void con_activate_unblock(Con *con) { + Con *ws = con_get_workspace(con); + Con *previous_focus = focused; + Con *fullscreen_on_ws = con_get_fullscreen_covering_ws(ws); + + if (fullscreen_on_ws && fullscreen_on_ws != con && !con_has_parent(con, fullscreen_on_ws)) { + con_disable_fullscreen(fullscreen_on_ws); + } + + con_activate(con); + + /* If the container is not on the current workspace, workspace_show() will + * switch to a different workspace and (if enabled) trigger a mouse pointer + * warp to the currently focused container (!) on the target workspace. + * + * Therefore, before calling workspace_show(), we make sure that 'con' will + * be focused on the workspace. However, we cannot just con_focus(con) + * because then the pointer will not be warped at all (the code thinks we + * are already there). + * + * So we focus 'con' to make it the currently focused window of the target + * workspace, then revert focus. */ + if (ws != con_get_workspace(previous_focus)) { + con_activate(previous_focus); + /* Now switch to the workspace, then focus */ + workspace_show(ws); + con_activate(con); + } +} + /* * Closes the given container. * From 6f82d21c39f06c75812e7363d0b21255d1349414 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Wed, 9 Oct 2019 02:32:22 +0300 Subject: [PATCH 2/2] handlers.c: new focus should not end up behind fullscreen This was raised here: https://www.reddit.com/r/i3wm/comments/df18aa/popup_during_fullscreen_not_behaving_the_way_i/ With this commit, _NET_ACTIVE_WINDOW requests are more similar to focusing with cmd_focus. --- src/handlers.c | 15 ++++----------- testcases/t/195-net-active-window.t | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/handlers.c b/src/handlers.c index 7a3349780..3a83d6a41 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -412,7 +412,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(workspace))) { DLOG("Focusing con = %p\n", con); workspace_show(workspace); - con_activate(con); + con_activate_unblock(con); tree_render(); } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(workspace))) { DLOG("Marking con = %p urgent\n", con); @@ -758,7 +758,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { workspace_show(ws); /* Re-set focus, even if unchanged from i3’s perspective. */ focused_id = XCB_NONE; - con_activate(con); + con_activate_unblock(con); } } else { /* Request is from an application. */ @@ -769,8 +769,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) { DLOG("Focusing con = %p\n", con); - workspace_show(ws); - con_activate(con); + con_activate_unblock(con); } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) { DLOG("Marking con = %p urgent\n", con); con_set_urgency(con, true); @@ -1115,14 +1114,8 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { DLOG("focus is different / refocusing floating window: updating decorations\n"); - /* Get the currently focused workspace to check if the focus change also - * involves changing workspaces. If so, we need to call workspace_show() to - * correctly update state and send the IPC event. */ - Con *ws = con_get_workspace(con); - if (ws != con_get_workspace(focused)) - workspace_show(ws); + con_activate_unblock(con); - con_activate(con); /* We update focused_id because we don’t need to set focus again */ focused_id = event->event; tree_render(); diff --git a/testcases/t/195-net-active-window.t b/testcases/t/195-net-active-window.t index f9f883cbd..4ce120893 100644 --- a/testcases/t/195-net-active-window.t +++ b/testcases/t/195-net-active-window.t @@ -149,6 +149,22 @@ send_net_active_window($scratch->id, 'pager'); is($x->input_focus, $scratch->id, 'scratchpad window is shown'); +################################################################################ +# Send a _NET_ACTIVE_WINDOW ClientMessage for a window behind a fullscreen +# window +################################################################################ + +$ws1 = fresh_workspace; +$win1 = open_window; +$win2 = open_window; +cmd 'fullscreen enable'; +is_num_fullscreen($ws1, 1, '1 fullscreen window in workspace'); + +send_net_active_window($win1->id); + +is($x->input_focus, $win1->id, 'window behind fullscreen window is now focused'); +is_num_fullscreen($ws1, 0, 'no fullscreen windows in workspace'); + ################################################################################ # Verify that the _NET_ACTIVE_WINDOW property is updated on the root window # correctly.