From 2403c43f7b72c0a5230ff7aa0dd67c687fc9ba5e Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sun, 17 Sep 2017 02:20:48 +0300 Subject: [PATCH] Make 'focus' disable blocking fullscreen windows The problem here is that con_fullscreen_permits_focusing() does not check if there is a blocking fullscreen container in the workspace that the container to be focused belongs. This makes it possible to focus a container behind a fullscreen window if it's in an unfocused workspace. This commit introduces a change in the 'focus' command behaviour. When focusing a container blocked by a fullscreen container, either CF_OUTPUT or CF_GLOBAL, the blocking container loses its fullscreen mode and the target container is focused like normal. This should not affect directional focus commands: left, right, up, down, parent, child. Fixes issue #1819. --- src/commands.c | 25 ++++--- testcases/t/156-fullscreen-focus.t | 110 ++++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 19 deletions(-) diff --git a/src/commands.c b/src/commands.c index 264cd04f..162f0892 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1250,6 +1250,20 @@ 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 = (focused && focused->fullscreen_mode == CF_GLOBAL) ? focused : con_get_fullscreen_con(ws, CF_OUTPUT); + if (fullscreen_on_ws && fullscreen_on_ws != con && !con_has_parent(con, fullscreen_on_ws)) { + con_disable_fullscreen(fullscreen_on_ws); + } + con_focus(con); +} + /* * Implementation of 'focus tiling|floating|mode_toggle'. * @@ -1274,7 +1288,7 @@ void cmd_focus_window_mode(I3_CMD, const char *window_mode) { (!to_floating && current->type == CT_FLOATING_CON)) continue; - con_focus(con_descend_focused(current)); + cmd_focus_force_focus(con_descend_focused(current)); success = true; break; } @@ -1341,13 +1355,6 @@ void cmd_focus(I3_CMD) { if (!ws) continue; - /* Check the fullscreen focus constraints. */ - if (!con_fullscreen_permits_focusing(current->con)) { - LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n"); - ysuccess(false); - return; - } - /* In case this is a scratchpad window, call scratchpad_show(). */ if (ws == __i3_scratch) { scratchpad_show(current->con); @@ -1371,7 +1378,7 @@ void cmd_focus(I3_CMD) { * So we focus 'current' to make it the currently focused window of * the target workspace, then revert focus. */ Con *currently_focused = focused; - con_focus(current->con); + cmd_focus_force_focus(current->con); con_focus(currently_focused); /* Now switch to the workspace, then focus */ diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index 7a5e38ca..cfa2405e 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -157,9 +157,6 @@ isnt($x->input_focus, $right2->id, 'bottom right window no longer focused'); cmd 'focus child'; is($x->input_focus, $right2->id, 'bottom right window focused again'); -cmd '[id="' . $left->id . '"] focus'; -is($x->input_focus, $right2->id, 'prevented focus change to left window'); - cmd 'focus up'; is($x->input_focus, $right1->id, 'allowed focus up'); @@ -178,9 +175,6 @@ is($x->input_focus, $right1->id, 'allowed focus wrap (down)'); cmd 'focus up'; is($x->input_focus, $right2->id, 'allowed focus wrap (up)'); -cmd '[id="' . $diff_ws->id . '"] focus'; -is($x->input_focus, $right2->id, 'prevented focus change to different ws'); - ################################################################################ # Same tests when we're in non-global fullscreen mode. It should now be possible # to focus a container in a different workspace. @@ -202,9 +196,6 @@ isnt($x->input_focus, $right2->id, 'bottom right window no longer focused'); cmd 'focus child'; is($x->input_focus, $right2->id, 'bottom right window focused again'); -cmd '[id="' . $left->id . '"] focus'; -is($x->input_focus, $right2->id, 'prevented focus change to left window'); - cmd 'focus up'; is($x->input_focus, $right1->id, 'allowed focus up'); @@ -323,6 +314,105 @@ verify_move(2, 'prevented move to workspace by name'); cmd "move to workspace prev"; verify_move(2, 'prevented move to workspace by position'); -# TODO: Tests for "move to output" and "move workspace to output". +################################################################################ +# Ensure it's possible to focus a window using the focus command despite +# fullscreen window blocking it. Fullscreen window should lose its fullscreen +# mode. +################################################################################ +# first & second tiling, focus using id +kill_all_windows; + +$tmp = fresh_workspace; +my $first = open_window; +my $second = open_window; +cmd 'fullscreen'; +is($x->input_focus, $second->id, 'fullscreen window focused'); +is_num_fullscreen($tmp, 1, '1 fullscreen window'); + +cmd '[id="'. $first->id .'"] focus'; +sync_with_i3; + +is($x->input_focus, $first->id, 'correctly focused using id'); +is_num_fullscreen($tmp, 0, 'no fullscreen windows'); + +# first floating, second tiling, focus using 'focus floating' +kill_all_windows; + +$tmp = fresh_workspace; +my $first = open_floating_window; +my $second = open_window; +cmd 'fullscreen'; +is($x->input_focus, $second->id, 'fullscreen window focused'); +is_num_fullscreen($tmp, 1, '1 fullscreen window'); + +cmd 'focus floating'; +sync_with_i3; + +is($x->input_focus, $first->id, 'correctly focused using focus floating'); +is_num_fullscreen($tmp, 0, 'no fullscreen windows'); + +# first tiling, second floating, focus using 'focus tiling' +kill_all_windows; + +$tmp = fresh_workspace; +my $first = open_window; +my $second = open_floating_window; +cmd 'fullscreen'; +is($x->input_focus, $second->id, 'fullscreen window focused'); +is_num_fullscreen($tmp, 1, '1 fullscreen window'); + +cmd 'focus tiling'; +sync_with_i3; + +is($x->input_focus, $first->id, 'correctly focused using focus tiling'); +is_num_fullscreen($tmp, 0, 'no fullscreen windows'); + +################################################################################ +# When the fullscreen window is in an other workspace it should maintain its +# fullscreen mode since it's not blocking the window to be focused. +################################################################################ + +kill_all_windows; + +$tmp = fresh_workspace; +my $first = open_window; + +$tmp2 = fresh_workspace; +my $second = open_window; +cmd 'fullscreen'; +is($x->input_focus, $second->id, 'fullscreen window focused'); +is_num_fullscreen($tmp2, 1, '1 fullscreen window'); + +cmd '[id="'. $first->id .'"] focus'; +sync_with_i3; + +is($x->input_focus, $first->id, 'correctly focused using focus id'); +is_num_fullscreen($tmp, 0, 'no fullscreen windows on first workspace'); +is_num_fullscreen($tmp2, 1, 'still one fullscreen window on second workspace'); + +################################################################################ +# But a global window in another workspace is blocking the window to be focused. +# Ensure that it loses its fullscreen mode. +################################################################################ + +kill_all_windows; + +$tmp = fresh_workspace; +$first = open_window; + +$tmp2 = fresh_workspace; +$second = open_window; +cmd 'fullscreen global'; +is($x->input_focus, $second->id, 'global window focused'); +is_num_fullscreen($tmp2, 1, '1 fullscreen window'); + +cmd '[id="'. $first->id .'"] focus'; +sync_with_i3; + +is($x->input_focus, $first->id, 'correctly focused using focus id'); +is_num_fullscreen($tmp2, 0, 'no fullscreen windows'); + + +# TODO: Tests for "move to output" and "move workspace to output". done_testing;