]> git.sur5r.net Git - i3/i3/commitdiff
Allow multiple assignments of workspaces to outputs 3254/head
authorOrestis Floros <orestisf1993@gmail.com>
Tue, 27 Mar 2018 21:55:20 +0000 (00:55 +0300)
committerOrestis Floros <orestisf1993@gmail.com>
Thu, 12 Jul 2018 14:07:01 +0000 (17:07 +0300)
Also makes get_assigned_output work with the primary output:
    workspace X output primary
will now work.

Fixes #555.

docs/userguide
include/workspace.h
parser-specs/config.spec
src/commands.c
src/config_directives.c
src/randr.c
src/workspace.c
testcases/t/297-assign-workspace-to-output.t
testcases/t/518-interpret-workspace-numbers.t
testcases/t/522-rename-assigned-workspace.t

index d944bb3977ecdf36932ab97b8a0e043b172382cf..2746f24e346c5decdf83b6118d7b673a05e7dbb1 100644 (file)
@@ -888,7 +888,7 @@ the second screen and so on).
 
 *Syntax*:
 -------------------------------------
-workspace <workspace> output <output>
+workspace <workspace> output <output1> [output2]…
 -------------------------------------
 
 The 'output' is the name of the RandR output you attach your screen to. On a
@@ -907,12 +907,15 @@ monitor name is “Dell UP2414Q”.
 entire monitor, i3 will still use the entire area of the containing monitor
 rather than that of just the output's.)
 
+You can specify multiple outputs. The first available will be used.
+
 If you use named workspaces, they must be quoted:
 
 *Examples*:
 ---------------------------
 workspace 1 output LVDS1
-workspace 5 output VGA1
+workspace 2 output primary
+workspace 5 output VGA1 LVDS1
 workspace "2: vim" output VGA1
 ---------------------------
 
index ae2874227d0f78845b22c4d2e5c227f614b9c4e2..28d9eb66cd18f8179bd373e5b8362d6ce0a7bd2f 100644 (file)
@@ -38,6 +38,13 @@ Con *get_existing_workspace_by_name(const char *name);
  */
 Con *get_existing_workspace_by_num(int num);
 
+/**
+ * Returns true if the first output assigned to a workspace with the given
+ * workspace assignment is the same as the given output.
+ *
+ */
+bool output_triggers_assignment(Output *output, struct Workspace_Assignment *assignment);
+
 /**
  * Returns a pointer to the workspace with the given number (starting at 0),
  * creating the workspace if necessary (by allocating the necessary amount of
index c5c4651c0064516b1b6477742d5ee24c455d05c4..b15c9a80489f6189dc5a36e3d678a2e5dd4f7a50 100644 (file)
@@ -273,7 +273,7 @@ state WORKSPACE_OUTPUT:
       -> WORKSPACE_OUTPUT_STR
 
 state WORKSPACE_OUTPUT_STR:
-  output = word
+  output = string
       -> call cfg_workspace($workspace, $output)
 
 # ipc-socket <path>
index c7b57ab3571a94f89b5ebec0b39b2d23a3f9174d..a133daa6eddc2e740ca4413059cbfc19236563a2 100644 (file)
@@ -2019,6 +2019,9 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
             LOG("Could not get output named \"%s\"\n", assignment->output);
             continue;
         }
+        if (!output_triggers_assignment(target_output, assignment)) {
+            continue;
+        }
         workspace_move_to_output(workspace, target_output);
 
         bool can_restore_focus = previously_focused != NULL;
index 4a31f79e5ce8efed4bb501f32e8258ca6f53c078..17e25cded84f3d619761601c4b22980b2de6cf24 100644 (file)
@@ -322,8 +322,8 @@ CFGFUN(show_marks, const char *value) {
     config.show_marks = eval_boolstr(value);
 }
 
-CFGFUN(workspace, const char *workspace, const char *output) {
-    DLOG("Assigning workspace \"%s\" to output \"%s\"\n", workspace, output);
+CFGFUN(workspace, const char *workspace, const char *outputs) {
+    DLOG("Assigning workspace \"%s\" to outputs \"%s\"\n", workspace, outputs);
     /* Check for earlier assignments of the same workspace so that we
      * don’t have assignments of a single workspace to different
      * outputs */
@@ -336,10 +336,16 @@ CFGFUN(workspace, const char *workspace, const char *output) {
         }
     }
 
-    assignment = scalloc(1, sizeof(struct Workspace_Assignment));
-    assignment->name = sstrdup(workspace);
-    assignment->output = sstrdup(output);
-    TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments);
+    char *buf = sstrdup(outputs);
+    char *output = strtok(buf, " ");
+    while (output != NULL) {
+        assignment = scalloc(1, sizeof(struct Workspace_Assignment));
+        assignment->name = sstrdup(workspace);
+        assignment->output = sstrdup(output);
+        TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments);
+        output = strtok(NULL, " ");
+    }
+    free(buf);
 }
 
 CFGFUN(ipc_socket, const char *path) {
index d4d7402a704ccc1dec3f7efb4e56b86d1c6f8aa9..38f1ee972eaf370335dc53934bbd85282c298ee6 100644 (file)
@@ -424,9 +424,9 @@ void init_ws_for_output(Output *output, Con *content) {
     /* go through all assignments and move the existing workspaces to this output */
     struct Workspace_Assignment *assignment;
     TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
-        if (strcmp(assignment->output, output_primary_name(output)) != 0)
+        if (!output_triggers_assignment(output, assignment)) {
             continue;
-
+        }
         Con *workspace = get_existing_workspace_by_name(assignment->name);
         if (workspace == NULL)
             continue;
@@ -501,8 +501,9 @@ void init_ws_for_output(Output *output, Con *content) {
 
     /* otherwise, we create the first assigned ws for this output */
     TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
-        if (strcmp(assignment->output, output_primary_name(output)) != 0)
+        if (!output_triggers_assignment(output, assignment)) {
             continue;
+        }
 
         LOG("Initializing first assigned workspace \"%s\" for output \"%s\"\n",
             assignment->name, assignment->output);
index a27c6f4b1db3a37288b5c96a34e8ec72354a9cf8..e92aaa5f0474ca73616ee3bdee0d0be51fae633d 100644 (file)
@@ -72,28 +72,47 @@ static void _workspace_apply_default_orientation(Con *ws) {
  * given name or number or NULL if no such output exists. If there is a
  * workspace with a matching name and another workspace with a matching number,
  * the output assigned to the first one is returned.
+ * The order of the 'ws_assignments' queue is respected: if multiple assignments
+ * match the specified workspace, the first one is returned.
  * If 'name' is NULL it will be ignored.
  * If 'parsed_num' is -1 it will be ignored.
  *
  */
 static Con *get_assigned_output(const char *name, long parsed_num) {
     Con *output = NULL;
-
     struct Workspace_Assignment *assignment;
     TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
         if (name && strcmp(assignment->name, name) == 0) {
             DLOG("Found workspace name assignment to output \"%s\"\n", assignment->output);
-            GREP_FIRST(output, croot, !strcmp(child->name, assignment->output));
-            break;
-        } else if (parsed_num != -1 && name_is_digits(assignment->name) && ws_name_to_number(assignment->name) == parsed_num) {
+            Output *assigned_by_name = get_output_by_name(assignment->output, true);
+            if (assigned_by_name) {
+                /* When the name matches exactly, skip numbered assignments. */
+                return assigned_by_name->con;
+            }
+        } else if (!output && /* Only keep the first numbered assignment. */
+                   parsed_num != -1 &&
+                   name_is_digits(assignment->name) &&
+                   ws_name_to_number(assignment->name) == parsed_num) {
             DLOG("Found workspace number assignment to output \"%s\"\n", assignment->output);
-            GREP_FIRST(output, croot, !strcmp(child->name, assignment->output));
+            Output *assigned_by_num = get_output_by_name(assignment->output, true);
+            if (assigned_by_num) {
+                output = assigned_by_num->con;
+            }
         }
     }
 
     return output;
 }
 
+/*
+ * Returns true if the first output assigned to a workspace with the given
+ * workspace assignment is the same as the given output.
+ */
+bool output_triggers_assignment(Output *output, struct Workspace_Assignment *assignment) {
+    Con *assigned = get_assigned_output(assignment->name, -1);
+    return assigned && assigned == output->con;
+}
+
 /*
  * Returns a pointer to the workspace with the given number (starting at 0),
  * creating the workspace if necessary (by allocating the necessary amount of
@@ -957,8 +976,9 @@ bool workspace_move_to_output(Con *ws, Output *output) {
         bool used_assignment = false;
         struct Workspace_Assignment *assignment;
         TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
-            if (assignment->output == NULL || strcmp(assignment->output, output_primary_name(current_output)) != 0)
+            if (!output_triggers_assignment(current_output, assignment)) {
                 continue;
+            }
             /* check if this workspace is already attached to the tree */
             if (get_existing_workspace_by_name(assignment->name) != NULL) {
                 continue;
index 650efa12d5a6b052ef88e5b8b7d67aba844f4d7e..a7b75be9fdfa5a49987c63cb22b6448a547d82a5 100644 (file)
@@ -54,7 +54,49 @@ check_output('4', 'fake-3', 'First available number that is not assigned to exis
 check_output('dontusethisname', '', 'Named workspace that is assigned to output that does not exist is not used');
 check_output('donotoverride', '', 'Named workspace assigned to already occupied output is not used');
 
+exit_gracefully($pid);
+
+################################################################################
+# Test multiple assignments
+################################################################################
+
+sub do_test {
+    my ($workspace, $output, $msg) = @_;
+    cmd 'focus output ' . ($output eq 'fake-3' ? 'fake-0' : 'fake-3');
+
+    cmd "workspace $workspace";
+    check_output($workspace, $output, $msg)
+}
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+fake-outputs 1024x768+0+0P,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
+
+workspace 1 output fake-0
+workspace 1 output fake-1 fake-2
+workspace 2 output fake-3 fake-4 fake-0 fake-1
+workspace 3 output these outputs do not exist but these do: fake-0 fake-3
+workspace 4 output whitespace                    fake-0
+workspace special output doesnotexist1 doesnotexist2 doesnotexist3
+EOT
+
+$pid = launch_with_config($config);
+
+do_test('1', 'fake-0', 'Multiple assignments do not override a single one');
+do_test('2', 'fake-3', 'First output out of multiple assignments is used');
+do_test('3', 'fake-0', 'First existing output is used');
+do_test('4', 'fake-0', 'Excessive whitespace is ok');
+do_test('5', 'fake-1', 'Numbered initialization for fake-1');
+do_test('6', 'fake-2', 'Numbered initialization for fake-2');
+
+cmd 'focus output fake-0, workspace special';
+check_output('special', 'fake-0', 'Workspace with only non-existing assigned outputs opened in current output.');
 
+# Moving assigned workspaces.
+cmd 'workspace 2, move workspace to output left';
+check_output('2', 'fake-2', 'Moved assigned workspace up');
 
 exit_gracefully($pid);
 done_testing;
index 7fd86d724053c92c0a8bc6636a018478cc0c416b..c7ab8367c0dd9285110491ba718f4251a6bc6251 100644 (file)
@@ -26,6 +26,8 @@ workspace 1:override output fake-0
 workspace 2 output fake-0
 workspace 1 output fake-1
 workspace 2:override output fake-1
+workspace 3 output fake-0
+workspace 3:override output doesnotexist fake-1
 
 fake-outputs 1024x768+0+0,1024x768+1024+0
 EOT
@@ -54,4 +56,10 @@ is(get_output_for_workspace('1:override'), 'fake-0',
     'Assignment rules should not be affected by the order assignments are declared')
     or diag 'Since workspace "1:override" is assigned by name to fake-0, it should open on fake-0';
 
+cmd 'focus output fake-1';
+cmd 'workspace "3:override"';
+is(get_output_for_workspace('3:override'), 'fake-1',
+   'Assignment rules should not be affected by multiple output assignments')
+    or diag 'Since workspace "3:override" is assigned by name to fake-1, it should open on fake-1';
+
 done_testing;
index f6319729873209ed2d765ccf044c195f4b254a92..824f4a062ddba6be888842425c9fe69ed6e1f280 100644 (file)
@@ -29,6 +29,8 @@ workspace 2 output fake-1
 workspace 3:foo output fake-1
 workspace baz output fake-1
 workspace 5 output left
+workspace 6 output doesnotexist fake-0
+workspace 7 output fake-1 fake-0
 EOT
 
 ##########################################################################
@@ -91,4 +93,24 @@ cmd 'rename workspace 5 to 2';
 is(get_output_for_workspace('2'), 'fake-1',
     'Renaming a workspace so that it moves to the focused output which contains only an empty workspace should replace the empty workspace');
 
+##########################################################################
+# Renaming a workspace with multiple assignments, where the first output
+# doesn't exist.
+##########################################################################
+
+cmd 'focus output fake-1';
+cmd 'rename workspace to 6';
+is(get_output_for_workspace('6'), 'fake-0',
+   'Renaming the workspace while first target output doesn\'t exist moves it to the second assigned output');
+
+##########################################################################
+# Renaming a workspace with multiple assignments, where both outputs exist
+# moves it to the first output.
+##########################################################################
+
+cmd 'focus output fake-0';
+cmd 'rename workspace to 7';
+is(get_output_for_workspace('7'), 'fake-1',
+   'Renaming a workspace with multiple assignments, where both outputs exist moves it to the first output.');
+
 done_testing;