From 12876932a948d7265745efaccafea509bdbaffe8 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 11 Nov 2018 11:22:38 -0500 Subject: [PATCH] Allow multiple outputs for workspace output `i3 4.16` allows users to list multiple outputs for a workspace and the first available will be used. The syntax is as follows: `workspace output ` Additionally when the workspace is created, the outputs get added to the output priority list in the order specified. This ensures that if a higher output gets connected, the workspace will move to the higher output. This works the same way as if the user had a workspace on an output, disconnected the output, and then later reconnected the output. --- include/sway/config.h | 2 +- sway/commands/workspace.c | 13 +++++---- sway/sway.5.scd | 8 +++-- sway/tree/output.c | 7 +++++ sway/tree/workspace.c | 61 ++++++++++++++++++++++++++++----------- 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index cd56c3dc..79c4359b 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -183,7 +183,7 @@ struct side_gaps { */ struct workspace_config { char *workspace; - char *output; + list_t *outputs; int gaps_inner; struct side_gaps gaps_outer; }; diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 168494d2..92118ecf 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -21,6 +21,7 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) { return NULL; } wsc->workspace = strdup(ws_name); + wsc->outputs = create_list(); wsc->gaps_inner = INT_MIN; wsc->gaps_outer.top = INT_MIN; wsc->gaps_outer.right = INT_MIN; @@ -32,7 +33,7 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) { void free_workspace_config(struct workspace_config *wsc) { free(wsc->workspace); - free(wsc->output); + free_flat_list(wsc->outputs); free(wsc); } @@ -141,18 +142,20 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } } if (output_location >= 0) { - if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { + if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, + output_location + 2))) { return error; } - char *ws_name = join_args(argv, argc - 2); + char *ws_name = join_args(argv, output_location); struct workspace_config *wsc = workspace_config_find_or_create(ws_name); free(ws_name); if (!wsc) { return cmd_results_new(CMD_FAILURE, "workspace output", "Unable to allocate workspace output"); } - free(wsc->output); - wsc->output = strdup(argv[output_location + 1]); + for (int i = output_location + 1; i < argc; ++i) { + list_add(wsc->outputs, strdup(argv[i])); + } } else if (gaps_location >= 0) { if ((error = cmd_workspace_gaps(argc, argv, gaps_location))) { return error; diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 8f6b35f1..1a11015f 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -573,8 +573,12 @@ The default colors are: Specifies that workspace _name_ should have the given gaps settings when it is created. -*workspace* output - Specifies that workspace _name_ should be shown on the specified _output_. +*workspace* output + Specifies that workspace _name_ should be shown on the specified _outputs_. + Multiple outputs can be listed and the first available will be used. If the + workspace gets placed on an output further down the list and an output that + is higher on the list becomes available, the workspace will be move to the + higher priority output. *workspace\_auto\_back\_and\_forth* yes|no When _yes_, repeating a workspace switch command will switch back to the diff --git a/sway/tree/output.c b/sway/tree/output.c index 2704920d..3c4614a8 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -31,6 +31,13 @@ static void restore_workspaces(struct sway_output *output) { j--; } } + + if (other->workspaces->length == 0) { + char *next = workspace_next_name(other->wlr_output->name); + struct sway_workspace *ws = workspace_create(other, next); + free(next); + ipc_event_workspace(NULL, ws, "init"); + } } // Saved workspaces diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 93ce50de..4be63311 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -33,14 +33,15 @@ struct workspace_config *workspace_find_config(const char *ws_name) { struct sway_output *workspace_get_initial_output(const char *name) { // Check workspace configs for a workspace<->output pair struct workspace_config *wsc = workspace_find_config(name); - if (wsc && wsc->output) { - struct sway_output *output = output_by_name(wsc->output); - if (!output) { - output = output_by_identifier(wsc->output); - } - - if (output) { - return output; + if (wsc) { + for (int i = 0; i < wsc->outputs->length; i++) { + struct sway_output *output = output_by_name(wsc->outputs->items[i]); + if (!output) { + output = output_by_identifier(wsc->outputs->items[i]); + } + if (output) { + return output; + } } } // Otherwise put it on the focused output @@ -85,7 +86,6 @@ struct sway_workspace *workspace_create(struct sway_output *output, ws->floating = create_list(); ws->tiling = create_list(); ws->output_priority = create_list(); - workspace_output_add_priority(ws, output); ws->gaps_outer = config->gaps_outer; ws->gaps_inner = config->gaps_inner; @@ -110,9 +110,17 @@ struct sway_workspace *workspace_create(struct sway_output *output, // Since default outer gaps can be smaller than the negation of // workspace specific inner gaps, check outer gaps again prevent_invalid_outer_gaps(ws); + + // Add output priorities + for (int i = 0; i < wsc->outputs->length; ++i) { + list_add(ws->output_priority, strdup(wsc->outputs->items[i])); + } } } + // If not already added, add the output to the lowest priority + workspace_output_add_priority(ws, output); + output_add_workspace(output, ws); output_sort_workspaces(output); @@ -134,8 +142,7 @@ void workspace_destroy(struct sway_workspace *workspace) { free(workspace->name); free(workspace->representation); - list_foreach(workspace->output_priority, free); - list_free(workspace->output_priority); + free_flat_list(workspace->output_priority); list_free(workspace->floating); list_free(workspace->tiling); list_free(workspace->current.floating); @@ -177,8 +184,19 @@ static bool workspace_valid_on_output(const char *output_name, char identifier[128]; struct sway_output *output = output_by_name(output_name); output_get_identifier(identifier, sizeof(identifier), output); - - return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0 || strcasecmp(identifier, output_name) == 0; + + if (!wsc) { + return true; + } + + for (int i = 0; i < wsc->outputs->length; i++) { + if (strcmp(wsc->outputs->items[i], output_name) == 0 || + strcmp(wsc->outputs->items[i], identifier) == 0) { + return true; + } + } + + return false; } static void workspace_name_from_binding(const struct sway_binding * binding, @@ -281,10 +299,19 @@ char *workspace_next_name(const char *output_name) { for (int i = 0; i < config->workspace_configs->length; ++i) { // Unlike with bindings, this does not guarantee order const struct workspace_config *wsc = config->workspace_configs->items[i]; - if (wsc->output && strcmp(wsc->output, output_name) == 0 - && workspace_by_name(wsc->workspace) == NULL) { - free(target); - target = strdup(wsc->workspace); + if (workspace_by_name(wsc->workspace)) { + continue; + } + bool found = false; + for (int j = 0; j < wsc->outputs->length; ++j) { + if (strcmp(wsc->outputs->items[j], output_name) == 0) { + found = true; + free(target); + target = strdup(wsc->workspace); + break; + } + } + if (found) { break; } }