*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
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
---------------------------
*/
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
-> WORKSPACE_OUTPUT_STR
state WORKSPACE_OUTPUT_STR:
- output = word
+ output = string
-> call cfg_workspace($workspace, $output)
# ipc-socket <path>
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;
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 */
}
}
- 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) {
/* 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;
/* 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);
* 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
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;
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;
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
'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;
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
##########################################################################
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;