back_and_forth+.
To move a container to another xrandr output such as +LVDS1+ or +VGA1+, you can
-use the +move output+ command followed by the name of the target output. You
-may also use +left+, +right+, +up+, +down+ instead of the xrandr output name to
-move to the next output in the specified direction.
+use the +move container to output+ command followed by the name of the target
+output. You may also use +left+, +right+, +up+, +down+ instead of the xrandr
+output name to move to the next output in the specified direction.
+
+To move a whole workspace to another xrandr output such as +LVDS1+ or +VGA1+,
+you can use the +move workspace to output+ command followed by the name of the
+target output. You may also use +left+, +right+, +up+, +down+ instead of the
+xrandr output name to move to the next output in the specified direction.
*Examples*:
-------------------------
# switch between the current and the previously focused one
bindsym mod+b workspace back_and_forth
+
+# move the whole workspace to the next output
+bindsym mod+x move workspace to output right
-------------------------
==== Named workspaces
return json_output;
}
+static Output *get_output_from_string(Output *current_output, const char *output_str) {
+ Output *output;
+
+ if (strcasecmp(output_str, "left") == 0) {
+ output = get_output_next(D_LEFT, current_output);
+ if (!output)
+ output = get_output_most(D_RIGHT, current_output);
+ } else if (strcasecmp(output_str, "right") == 0) {
+ output = get_output_next(D_RIGHT, current_output);
+ if (!output)
+ output = get_output_most(D_LEFT, current_output);
+ } else if (strcasecmp(output_str, "up") == 0) {
+ output = get_output_next(D_UP, current_output);
+ if (!output)
+ output = get_output_most(D_DOWN, current_output);
+ } else if (strcasecmp(output_str, "down") == 0) {
+ output = get_output_next(D_DOWN, current_output);
+ if (!output)
+ output = get_output_most(D_UP, current_output);
+ } else output = get_output_by_name(output_str);
+
+ return output;
+}
+
+
/*
* Returns true if a is definitely greater than b (using the given epsilon)
*
current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
assert(current_output != NULL);
- if (strcasecmp($3, "left") == 0) {
- output = get_output_next(D_LEFT, current_output);
- if (!output)
- output = get_output_most(D_RIGHT, current_output);
- } else if (strcasecmp($3, "right") == 0) {
- output = get_output_next(D_RIGHT, current_output);
- if (!output)
- output = get_output_most(D_LEFT, current_output);
- } else if (strcasecmp($3, "up") == 0) {
- output = get_output_next(D_UP, current_output);
- if (!output)
- output = get_output_most(D_DOWN, current_output);
- } else if (strcasecmp($3, "down") == 0) {
- output = get_output_next(D_DOWN, current_output);
- if (!output)
- output = get_output_most(D_UP, current_output);
- } else output = get_output_by_name($3);
+ output = get_output_from_string(current_output, $3);
free($3);
tree_render();
}
+ | TOK_MOVE TOK_WORKSPACE TOK_TO TOK_OUTPUT STR
+ {
+ printf("should move workspace to output %s\n", $5);
+
+ HANDLE_EMPTY_MATCH;
+
+ owindow *current;
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ Output *current_output = get_output_containing(current->con->rect.x,
+ current->con->rect.y);
+ Output *output = get_output_from_string(current_output, $5);
+ if (!output) {
+ printf("No such output\n");
+ break;
+ }
+
+ Con *content = output_get_content(output->con);
+ LOG("got output %p with content %p\n", output, content);
+
+ Con *ws = con_get_workspace(current->con);
+ printf("should move workspace %p / %s\n", ws, ws->name);
+ if (con_num_children(ws->parent) == 1) {
+ printf("Not moving workspace \"%s\", it is the only workspace on its output.\n", ws->name);
+ continue;
+ }
+ bool workspace_was_visible = workspace_is_visible(ws);
+ Con *old_content = ws->parent;
+ con_detach(ws);
+ if (workspace_was_visible) {
+ /* The workspace which we just detached was visible, so focus
+ * the next one in the focus-stack. */
+ Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
+ printf("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
+ workspace_show(focus_ws);
+ }
+ con_attach(ws, content, false);
+ ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
+ if (workspace_was_visible) {
+ /* Focus the moved workspace on the destination output. */
+ workspace_show(ws);
+ }
+ }
+
+ tree_render();
+ }
;
append_layout:
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Tests whether the 'move workspace <ws> to [output] <output>' command works
+#
+use List::Util qw(first);
+use i3test;
+
+# TODO:
+# introduce 'move workspace 3 to output <output>' with synonym 'move workspace 3 to <output>'
+
+################################################################################
+# Setup workspaces so that they stay open (with an empty container).
+################################################################################
+
+is(focused_ws, '1', 'starting on workspace 1');
+# ensure workspace 1 stays open
+open_window;
+
+cmd 'focus output right';
+is(focused_ws, '2', 'workspace 2 on second output');
+# ensure workspace 2 stays open
+open_window;
+
+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 5 stays open
+open_window;
+
+################################################################################
+# Move a workspace over and verify that it is on the right output.
+################################################################################
+
+# The current order should be:
+# output 1: 1, 5
+# output 2: 2
+cmd 'workspace 5';
+is(focused_ws, '5', 'workspace 5 focused');
+
+my ($x0, $x1) = workspaces_per_screen();
+ok('5' ~~ @$x0, 'workspace 5 on xinerama-0');
+
+cmd 'move workspace to output xinerama-1';
+
+sub workspaces_per_screen {
+ my $i3 = i3(get_socket_path());
+ my $tree = $i3->get_tree->recv;
+ my @outputs = @{$tree->{nodes}};
+
+ my $xinerama0 = first { $_->{name} eq 'xinerama-0' } @outputs;
+ my $xinerama0_content = first { $_->{type} == 2 } @{$xinerama0->{nodes}};
+
+ my $xinerama1 = first { $_->{name} eq 'xinerama-1' } @outputs;
+ my $xinerama1_content = first { $_->{type} == 2 } @{$xinerama1->{nodes}};
+
+ my @xinerama0_workspaces = map { $_->{name} } @{$xinerama0_content->{nodes}};
+ my @xinerama1_workspaces = map { $_->{name} } @{$xinerama1_content->{nodes}};
+
+ return \@xinerama0_workspaces, \@xinerama1_workspaces;
+}
+
+($x0, $x1) = workspaces_per_screen();
+ok('5' ~~ @$x1, 'workspace 5 now on xinerama-1');
+
+################################################################################
+# Verify that the last workspace on an output cannot be moved.
+################################################################################
+
+cmd 'workspace 1';
+cmd 'move workspace to output xinerama-1';
+
+($x0, $x1) = workspaces_per_screen();
+ok('1' ~~ @$x0, 'workspace 1 still on xinerama-0');
+
+################################################################################
+# Verify that 'move workspace to output <direction>' works
+################################################################################
+
+cmd 'workspace 5';
+cmd 'move workspace to output left';
+
+($x0, $x1) = workspaces_per_screen();
+ok('5' ~~ @$x0, 'workspace 5 back on xinerama-0');
+
+
+done_testing;