From bce088679a3a51d9a6ce2bcef6ddc7938368d312 Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Wed, 28 Mar 2018 00:55:20 +0300 Subject: [PATCH] Allow multiple assignments of workspaces to outputs Also makes get_assigned_output work with the primary output: workspace X output primary will now work. Fixes #555. --- docs/userguide | 7 +++- include/workspace.h | 7 ++++ parser-specs/config.spec | 2 +- src/commands.c | 3 ++ src/config_directives.c | 18 +++++--- src/randr.c | 7 ++-- src/workspace.c | 32 +++++++++++--- testcases/t/297-assign-workspace-to-output.t | 42 +++++++++++++++++++ testcases/t/518-interpret-workspace-numbers.t | 8 ++++ testcases/t/522-rename-assigned-workspace.t | 22 ++++++++++ 10 files changed, 130 insertions(+), 18 deletions(-) diff --git a/docs/userguide b/docs/userguide index d944bb39..2746f24e 100644 --- a/docs/userguide +++ b/docs/userguide @@ -888,7 +888,7 @@ the second screen and so on). *Syntax*: ------------------------------------- -workspace output +workspace output [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 --------------------------- diff --git a/include/workspace.h b/include/workspace.h index ae287422..28d9eb66 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -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 diff --git a/parser-specs/config.spec b/parser-specs/config.spec index c5c4651c..b15c9a80 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -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 diff --git a/src/commands.c b/src/commands.c index c7b57ab3..a133daa6 100644 --- a/src/commands.c +++ b/src/commands.c @@ -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; diff --git a/src/config_directives.c b/src/config_directives.c index 4a31f79e..17e25cde 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -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) { diff --git a/src/randr.c b/src/randr.c index d4d7402a..38f1ee97 100644 --- a/src/randr.c +++ b/src/randr.c @@ -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); diff --git a/src/workspace.c b/src/workspace.c index a27c6f4b..e92aaa5f 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -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; diff --git a/testcases/t/297-assign-workspace-to-output.t b/testcases/t/297-assign-workspace-to-output.t index 650efa12..a7b75be9 100644 --- a/testcases/t/297-assign-workspace-to-output.t +++ b/testcases/t/297-assign-workspace-to-output.t @@ -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 = <