]> git.sur5r.net Git - i3/i3/commitdiff
Implement 'move workspace to output <output>'
authorMichael Stapelberg <michael@stapelberg.de>
Tue, 10 Jan 2012 22:10:59 +0000 (22:10 +0000)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 10 Jan 2012 22:16:50 +0000 (22:16 +0000)
Fixes: #541
docs/userguide
src/cmdparse.y
testcases/t/504-move-workspace-to-output.t [new file with mode: 0644]

index 0cdea1962d8a52f451baa0774e58f38c97f755f1..0ba705a626e1fa8d139d45a0b1f805d9190a2093 100644 (file)
@@ -1256,9 +1256,14 @@ To switch back to the previously focused workspace, use +workspace
 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*:
 -------------------------
@@ -1272,6 +1277,9 @@ bindsym mod+Shift+2 move container to workspace 2
 
 # 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
index 9aff815a244599d82b1e260dece942b713f4fab5..9ae17ff9a556ae81f82b52d3cb2e422b4ab3a201 100644 (file)
@@ -105,6 +105,31 @@ char *parse_cmd(const char *new) {
     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)
  *
@@ -546,23 +571,7 @@ focus:
             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);
 
@@ -1019,6 +1028,51 @@ move:
 
         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:
diff --git a/testcases/t/504-move-workspace-to-output.t b/testcases/t/504-move-workspace-to-output.t
new file mode 100644 (file)
index 0000000..7395c2e
--- /dev/null
@@ -0,0 +1,90 @@
+#!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;