diff --git a/include/tree.h b/include/tree.h index b9159e3b..8816b19a 100644 --- a/include/tree.h +++ b/include/tree.h @@ -39,16 +39,16 @@ Con *tree_open_con(Con *con, i3Window *window); void tree_split(Con *con, orientation_t orientation); /** - * Moves focus one level up. + * Moves focus one level up. Returns true if focus changed. * */ -void level_up(void); +bool level_up(void); /** - * Moves focus one level down. + * Moves focus one level down. Returns true if focus changed. * */ -void level_down(void); +bool level_down(void); /** * Renders the tree, that is rendering all outputs using render_con() and diff --git a/src/commands.c b/src/commands.c index b3c955e5..cf8187b7 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1213,23 +1213,26 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) { * */ void cmd_focus_level(I3_CMD, char *level) { - if (focused && - focused->type != CT_WORKSPACE && - focused->fullscreen_mode != CF_NONE) { - LOG("Cannot change focus while in fullscreen mode.\n"); - ysuccess(false); - return; + DLOG("level = %s\n", level); + bool success = false; + + /* Focusing the parent can only be allowed if the newly + * focused container won't escape the fullscreen container. */ + if (strcmp(level, "parent") == 0) { + if (focused && focused->parent) { + if (con_fullscreen_permits_focusing(focused->parent)) + success = level_up(); + else + LOG("Currently in fullscreen, not going up\n"); + } } - DLOG("level = %s\n", level); + /* Focusing a child should always be allowed. */ + else success = level_down(); - if (strcmp(level, "parent") == 0) - level_up(); - else level_down(); - - cmd_output->needs_tree_render = true; + cmd_output->needs_tree_render = success; // XXX: default reply for now, make this a better reply - ysuccess(true); + ysuccess(success); } /* diff --git a/src/con.c b/src/con.c index f969d056..a7ae642b 100644 --- a/src/con.c +++ b/src/con.c @@ -1170,6 +1170,10 @@ bool con_fullscreen_permits_focusing(Con *con) { if (fs->type == CT_WORKSPACE) return true; + /* Allow it if the container itself is the fullscreen container. */ + if (con == fs) + return true; + /* If fullscreen is per-output, the focus being in a different workspace is * sufficient to guarantee that change won't leave fullscreen in bad shape. */ if (fs->fullscreen_mode == CF_OUTPUT && diff --git a/src/tree.c b/src/tree.c index f29369c6..cb3d044a 100644 --- a/src/tree.c +++ b/src/tree.c @@ -375,38 +375,34 @@ void tree_split(Con *con, orientation_t orientation) { } /* - * Moves focus one level up. + * Moves focus one level up. Returns true if focus changed. * */ -void level_up(void) { - /* We cannot go up when we are in fullscreen mode at the moment, that would - * be totally not intuitive */ - if (focused->fullscreen_mode != CF_NONE) { - LOG("Currently in fullscreen, not going up\n"); - return; - } +bool level_up(void) { /* We can focus up to the workspace, but not any higher in the tree */ if ((focused->parent->type != CT_CON && focused->parent->type != CT_WORKSPACE) || focused->type == CT_WORKSPACE) { LOG("Cannot go up any further\n"); - return; + return false; } con_focus(focused->parent); + return true; } /* - * Moves focus one level down. + * Moves focus one level down. Returns true if focus changed. * */ -void level_down(void) { +bool level_down(void) { /* Go down the focus stack of the current node */ Con *next = TAILQ_FIRST(&(focused->focus_head)); if (next == TAILQ_END(&(focused->focus_head))) { printf("cannot go down\n"); - return; + return false; } con_focus(next); + return true; } static void mark_unmapped(Con *con) { diff --git a/testcases/t/156-fullscreen-focus.t b/testcases/t/156-fullscreen-focus.t index 3a27c9ff..65d23815 100644 --- a/testcases/t/156-fullscreen-focus.t +++ b/testcases/t/156-fullscreen-focus.t @@ -122,6 +122,12 @@ is($x->input_focus, $right1->id, 'upper right window focused'); cmd '[id="' . $right2->id . '"] focus'; is($x->input_focus, $right2->id, 'bottom right window focused'); +cmd 'focus parent'; +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'); @@ -129,26 +135,26 @@ 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. We toggle fullscreen on -# and off to avoid testing whether focus level works in fullscreen for now. It -# should now be possible to focus a container in a different workspace. +# Same tests when we're in non-global fullscreen mode. It should now be possible +# to focus a container in a different workspace. ################################################################################ +cmd 'focus parent'; cmd 'fullscreen global'; -cmd 'fullscreen global'; +cmd 'fullscreen'; cmd '[id="' . $right1->id . '"] focus'; is($x->input_focus, $right1->id, 'upper right window focused'); -cmd 'focus parent'; -cmd 'fullscreen'; - -cmd '[id="' . $right1->id . '"] focus'; -is($x->input_focus, $right1->id, 'upper right window still focused'); - cmd '[id="' . $right2->id . '"] focus'; is($x->input_focus, $right2->id, 'bottom right window focused'); +cmd 'focus parent'; +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'); diff --git a/testcases/t/157-regress-fullscreen-level-up.t b/testcases/t/157-regress-fullscreen-level-up.t deleted file mode 100644 index 316dbcaa..00000000 --- a/testcases/t/157-regress-fullscreen-level-up.t +++ /dev/null @@ -1,41 +0,0 @@ -#!perl -# vim:ts=4:sw=4:expandtab -# -# Regression test: level up should be a noop during fullscreen mode -# -use i3test; - -my $tmp = fresh_workspace; - -##################################################################### -# open a window, verify it’s not in fullscreen mode -##################################################################### - -my $win = open_window; - -my $nodes = get_ws_content $tmp; -is(@$nodes, 1, 'exactly one client'); -is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen'); - -##################################################################### -# make it fullscreen -##################################################################### - -cmd 'nop making fullscreen'; -cmd 'fullscreen'; - -$nodes = get_ws_content $tmp; -is($nodes->[0]->{fullscreen_mode}, 1, 'client fullscreen now'); - -##################################################################### -# send level up, try to un-fullscreen -##################################################################### -cmd 'focus parent'; -cmd 'fullscreen'; - -$nodes = get_ws_content $tmp; -is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen any longer'); - -does_i3_live; - -done_testing;