From fba2582b2ef090b917cd5aea7d3482728818337a Mon Sep 17 00:00:00 2001 From: Max Alexander Busse Date: Sun, 25 Dec 2011 03:30:10 +0100 Subject: [PATCH 1/5] Switch and Move to next workspace on the same Output. As requested in \#554 --- include/workspace.h | 12 +++++ src/cmdparse.l | 2 + src/cmdparse.y | 44 +++++++++++++++++ src/workspace.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+) diff --git a/include/workspace.h b/include/workspace.h index 995499f2..21b733a6 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -69,6 +69,18 @@ Con* workspace_next(); */ Con* workspace_prev(); +/** + * Returns the next workspace on the same output + * + */ +Con* workspace_next_on_output(); + +/** + * Returns the previous workspace on the same output + * + */ +Con* workspace_prev_on_output(); + /** * Focuses the previously focused workspace. * diff --git a/src/cmdparse.l b/src/cmdparse.l index 1431ec35..d382c8db 100644 --- a/src/cmdparse.l +++ b/src/cmdparse.l @@ -78,6 +78,8 @@ EOL (\r?\n) * handling strings ('workspace' command) */ next { BEGIN(INITIAL); return TOK_NEXT; } prev { BEGIN(INITIAL); return TOK_PREV; } +next_on_output { BEGIN(INITIAL); return TOK_NEXT_ON_OUTPUT; } +prev_on_output { BEGIN(INITIAL); return TOK_PREV_ON_OUTPUT; } back_and_forth { BEGIN(INITIAL); return TOK_BACK_AND_FORTH; } \"[^\"]+\" { diff --git a/src/cmdparse.y b/src/cmdparse.y index 3f13c72b..28aa564e 100644 --- a/src/cmdparse.y +++ b/src/cmdparse.y @@ -155,6 +155,8 @@ bool definitelyGreaterThan(float a, float b, float epsilon) { %token TOK_OPEN "open" %token TOK_NEXT "next" %token TOK_PREV "prev" +%token TOK_NEXT_ON_OUTPUT "next_on_output" +%token TOK_PREV_ON_OUTPUT "prev_on_output" %token TOK_SCRATCHPAD "scratchpad" %token TOK_SHOW "show" %token TOK_SPLIT "split" @@ -680,6 +682,16 @@ workspace: workspace_show(workspace_prev()); tree_render(); } + | TOK_WORKSPACE TOK_NEXT_ON_OUTPUT + { + workspace_show(workspace_next_on_output()); + tree_render(); + } + | TOK_WORKSPACE TOK_PREV_ON_OUTPUT + { + workspace_show(workspace_prev_on_output()); + tree_render(); + } | TOK_WORKSPACE TOK_BACK_AND_FORTH { workspace_back_and_forth(); @@ -912,6 +924,38 @@ move: tree_render(); } + | TOK_MOVE TOK_WORKSPACE TOK_NEXT_ON_OUTPUT + { + owindow *current; + + /* get the workspace */ + Con *ws = workspace_next_on_output(); + + HANDLE_EMPTY_MATCH; + + TAILQ_FOREACH(current, &owindows, owindows) { + printf("matching: %p / %s\n", current->con, current->con->name); + con_move_to_workspace(current->con, ws, true, false); + } + + tree_render(); + } + | TOK_MOVE TOK_WORKSPACE TOK_PREV_ON_OUTPUT + { + owindow *current; + + /* get the workspace */ + Con *ws = workspace_prev_on_output(); + + HANDLE_EMPTY_MATCH; + + TAILQ_FOREACH(current, &owindows, owindows) { + printf("matching: %p / %s\n", current->con, current->con->name); + con_move_to_workspace(current->con, ws, true, false); + } + + tree_render(); + } | TOK_MOVE TOK_OUTPUT STR { owindow *current; diff --git a/src/workspace.c b/src/workspace.c index b8fb73a6..55d4af61 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -409,6 +409,118 @@ workspace_prev_end: return prev; } + +/* + * Focuses the next workspace on the same output. + * + */ +Con* workspace_next_on_output() { + Con *current = con_get_workspace(focused); + Con *next = NULL; + Con *output = con_get_output(focused); + + if (current->num == -1) { + /* If currently a named workspace, find next named workspace. */ + next = TAILQ_NEXT(current, nodes); + } else { + /* If currently a numbered workspace, find next numbered workspace. */ + NODES_FOREACH(output_get_content(output)) { + if (child->type != CT_WORKSPACE) + continue; + if (child->num == -1) + break; + /* Need to check child against current and next because we are + * traversing multiple lists and thus are not guaranteed the + * relative order between the list of workspaces. */ + if (current->num < child->num && (!next || child->num < next->num)) + next = child; + } + } + + /* Find next named workspace. */ + if (!next) { + bool found_current = false; + NODES_FOREACH(output_get_content(output)) { + if (child->type != CT_WORKSPACE) + continue; + if (child == current) { + found_current = 1; + } else if (child->num == -1 && (current->num != -1 || found_current)) { + next = child; + goto workspace_next_on_output_end; + } + } + } + + /* Find first workspace. */ + if (!next) { + NODES_FOREACH(output_get_content(output)) { + if (child->type != CT_WORKSPACE) + continue; + if (!next || (child->num != -1 && child->num < next->num)) + next = child; + } + } +workspace_next_on_output_end: + return next; +} + +/* + * Focuses the previous workspace on same output. + * + */ +Con* workspace_prev_on_output() { + Con *current = con_get_workspace(focused); + Con *prev = NULL; + Con *output = con_get_output(focused); + + if (current->num == -1) { + /* If named workspace, find previous named workspace. */ + prev = TAILQ_PREV(current, nodes_head, nodes); + if (prev && prev->num != -1) + prev = NULL; + } else { + /* If numbered workspace, find previous numbered workspace. */ + NODES_FOREACH_REVERSE(output_get_content(output)) { + if (child->type != CT_WORKSPACE || child->num == -1) + continue; + /* Need to check child against current and previous because we + * are traversing multiple lists and thus are not guaranteed + * the relative order between the list of workspaces. */ + if (current->num > child->num && (!prev || child->num > prev->num)) + prev = child; + } + } + + /* Find previous named workspace. */ + if (!prev) { + bool found_current = false; + NODES_FOREACH_REVERSE(output_get_content(output)) { + if (child->type != CT_WORKSPACE) + continue; + if (child == current) { + found_current = true; + } else if (child->num == -1 && (current->num != -1 || found_current)) { + prev = child; + goto workspace_prev_on_output_end; + } + } + } + + /* Find last workspace. */ + if (!prev) { + NODES_FOREACH_REVERSE(output_get_content(output)) { + if (child->type != CT_WORKSPACE) + continue; + if (!prev || child->num > prev->num) + prev = child; + } + } + +workspace_prev_on_output_end: + return prev; +} + /* * Focuses the previously focused workspace. * From 58f331c50a35a90d025162e2820d3367a7e63154 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 8 Jan 2012 16:30:48 +0000 Subject: [PATCH 2/5] userguide: document workspace next_on_output/prev_on_output --- docs/userguide | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/userguide b/docs/userguide index dce0371f..f1aba9da 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1246,8 +1246,10 @@ number or name of the workspace. To move containers to specific workspaces, use You can also switch to the next and previous workspace with the commands +workspace next+ and +workspace prev+, which is handy, for example, if you have workspace 1, 3, 4 and 9 and you want to cycle through them with a single key -combination. Similarly, you can use +move workspace next+ and +move workspace -prev+ to move a container to the next/previous workspace. +combination. To restrict those to the current output, use +workspace +next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move +workspace next+ and +move workspace prev+ to move a container to the +next/previous workspace. [[back_and_forth]] To switch back to the previously focused workspace, use +workspace From 6df971d5d5851aa808da4c6c20327ac83b6da4b6 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 8 Jan 2012 16:31:07 +0000 Subject: [PATCH 3/5] Ignore next_on_output/prev_on_output when looking for the first workspace --- src/randr.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/randr.c b/src/randr.c index d1683e9f..161c8886 100644 --- a/src/randr.c +++ b/src/randr.c @@ -420,11 +420,15 @@ void init_ws_for_output(Output *output, Con *content) { continue; DLOG("relevant command = %s\n", bind->command); char *target = bind->command + strlen("workspace "); - /* We check if this is the workspace next/prev/back_and_forth command. - * Beware: The workspace names "next", "prev" and "back_and_forth" are - * OK, so we check before stripping the double quotes */ + /* We check if this is the workspace + * next/prev/next_on_output/prev_on_output/back_and_forth command. + * Beware: The workspace names "next", "prev", "next_on_output", + * "prev_on_output" and "back_and_forth" are OK, so we check before + * stripping the double quotes */ if (strncasecmp(target, "next", strlen("next")) == 0 || strncasecmp(target, "prev", strlen("prev")) == 0 || + strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 || + strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 || strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0) continue; if (*target == '"') From 6e4a86b7d175bfe08043da9c5189cec5baafbef3 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 8 Jan 2012 16:32:58 +0000 Subject: [PATCH 4/5] tests: make focused_ws multi-monitor-safe, document it --- testcases/lib/i3test.pm | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index f9cec64e..1e70bf52 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -383,18 +383,19 @@ sub workspace_exists { ($name ~~ @{get_workspace_names()}) } +=head2 focused_ws + +Returns the name of the currently focused workspace. + +=cut sub focused_ws { my $i3 = i3(get_socket_path()); my $tree = $i3->get_tree->recv; - my @outputs = @{$tree->{nodes}}; - my @cons; - for my $output (@outputs) { - next if $output->{name} eq '__i3'; - # get the first CT_CON of each output - my $content = first { $_->{type} == 2 } @{$output->{nodes}}; - my $first = first { $_->{fullscreen_mode} == 1 } @{$content->{nodes}}; - return $first->{name} - } + my $focused = $tree->{focus}->[0]; + my $output = first { $_->{id} == $focused } @{$tree->{nodes}}; + my $content = first { $_->{type} == 2 } @{$output->{nodes}}; + my $first = first { $_->{fullscreen_mode} == 1 } @{$content->{nodes}}; + return $first->{name} } # From 6585d289cf5156dc6d233cbdfc9d37b9ff6bb065 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 8 Jan 2012 16:33:16 +0000 Subject: [PATCH 5/5] add testcase for workspace next_on_output/prev_on_output --- testcases/t/503-workspace.t | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 testcases/t/503-workspace.t diff --git a/testcases/t/503-workspace.t b/testcases/t/503-workspace.t new file mode 100644 index 00000000..f42717a2 --- /dev/null +++ b/testcases/t/503-workspace.t @@ -0,0 +1,64 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Tests whether 'workspace next_on_output' and the like work correctly. +# +use List::Util qw(first); +use i3test; + +################################################################################ +# Setup workspaces so that they stay open (with an empty container). +################################################################################ + +is(focused_ws, '1', 'starting on workspace 1'); +# ensure workspace 1 stays open +cmd 'open'; + +cmd 'focus output right'; +is(focused_ws, '2', 'workspace 2 on second output'); +# ensure workspace 2 stays open +cmd 'open'; + +cmd 'focus output right'; +is(focused_ws, '1', 'back on workspace 1'); + +# We don’t use fresh_workspace with named workspaces here since they come last +# when using 'workspace next'. +cmd 'workspace 5'; +# ensure workspace $tmp stays open +cmd 'open'; + +################################################################################ +# Use workspace next and verify the correct order. +################################################################################ + +# The current order should be: +# output 1: 1, 5 +# output 2: 2 +cmd 'workspace 1'; +cmd 'workspace next'; +is(focused_ws, '2', 'workspace 2 focused'); +cmd 'workspace next'; +is(focused_ws, '5', 'workspace 5 focused'); + +################################################################################ +# Now try the same with workspace next_on_output. +################################################################################ + +cmd 'workspace 1'; +cmd 'workspace next_on_output'; +is(focused_ws, '5', 'workspace 5 focused'); +cmd 'workspace next_on_output'; +is(focused_ws, '1', 'workspace 1 focused'); + +cmd 'workspace prev_on_output'; +is(focused_ws, '5', 'workspace 5 focused'); +cmd 'workspace prev_on_output'; +is(focused_ws, '1', 'workspace 1 focused'); + +cmd 'workspace 2'; + +cmd 'workspace prev_on_output'; +is(focused_ws, '2', 'workspace 2 focused'); + +done_testing;