From 415a48ac6387a62a59adb8ed1168e851509a0ce3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 29 Sep 2018 11:06:07 +1000 Subject: [PATCH 1/3] Make gaps implementation consistent with i3-gaps This changes our gaps implementation to behave like i3-gaps. Our previous implementation allowed you to set gaps on a per container basis. This isn't supported by i3-gaps and doesn't seem to have a practical use case. The gaps_outer and gaps_inner properties on containers are now removed as they just read the gaps_inner from the workspace. `gaps inner|outer ` no longer changes the gaps for all workspaces. It only sets defaults for new workspaces. `gaps inner|outer current|workspace|all set|plus|minus ` is now runtime only, and the workspace option is now removed. `current` now sets gaps for the current workspace as opposed to the current container. `workspace gaps inner|outer ` is now implemented. This sets defaults for a workspace. This also fixes a bug where changing the layout of a split container from linear to tabbed would cause gaps to not be applied to it until you switch to another workspace and back. --- include/sway/config.h | 2 + include/sway/tree/container.h | 3 - include/sway/tree/workspace.h | 7 +- sway/commands/gaps.c | 300 +++++++++++++++++----------------- sway/commands/layout.c | 3 +- sway/commands/workspace.c | 38 +++++ sway/tree/container.c | 2 +- sway/tree/workspace.c | 18 +- 8 files changed, 209 insertions(+), 164 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index af5c7a18..82be26ca 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -173,6 +173,8 @@ struct output_config { struct workspace_config { char *workspace; char *output; + int gaps_inner; + int gaps_outer; }; struct bar_config { diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 5e281a2f..b865a0f2 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -94,9 +94,6 @@ struct sway_container { // The gaps currently applied to the container. double current_gaps; - bool has_gaps; - double gaps_inner; - double gaps_outer; struct sway_workspace *workspace; // NULL when hidden in the scratchpad struct sway_container *parent; // NULL if container in root of workspace diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index c8220b39..efdae5a1 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -32,10 +32,9 @@ struct sway_workspace { enum sway_container_layout layout; enum sway_container_layout prev_split_layout; - double current_gaps; - bool has_gaps; - double gaps_inner; - double gaps_outer; + int current_gaps; + int gaps_inner; + int gaps_outer; struct sway_output *output; // NULL if no outputs are connected list_t *floating; // struct sway_container diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index d676e475..3791fcb4 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -1,4 +1,5 @@ #include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/tree/arrange.h" @@ -13,172 +14,167 @@ enum gaps_op { GAPS_OP_SUBTRACT }; -enum gaps_scope { - GAPS_SCOPE_ALL, - GAPS_SCOPE_WORKSPACE, - GAPS_SCOPE_CURRENT +struct gaps_data { + bool inner; + enum gaps_op operation; + int amount; }; -struct cmd_results *cmd_gaps(int argc, char **argv) { - struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1); - if (error) { +// gaps edge_gaps on|off|toggle +static struct cmd_results *gaps_edge_gaps(int argc, char **argv) { + struct cmd_results *error; + if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { return error; } - if (strcmp(argv[0], "edge_gaps") == 0) { - if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { - return error; - } - - if (strcmp(argv[1], "on") == 0) { - config->edge_gaps = true; - } else if (strcmp(argv[1], "off") == 0) { - config->edge_gaps = false; - } else if (strcmp(argv[1], "toggle") == 0) { - if (!config->active) { - return cmd_results_new(CMD_INVALID, "gaps", - "Cannot toggle gaps while not running."); - } - config->edge_gaps = !config->edge_gaps; - } else { + if (strcmp(argv[1], "on") == 0) { + config->edge_gaps = true; + } else if (strcmp(argv[1], "off") == 0) { + config->edge_gaps = false; + } else if (strcmp(argv[1], "toggle") == 0) { + if (!config->active) { return cmd_results_new(CMD_INVALID, "gaps", - "gaps edge_gaps on|off|toggle"); + "Cannot toggle gaps while not running."); } - arrange_root(); + config->edge_gaps = !config->edge_gaps; } else { - int amount_idx = 0; // the current index in argv - enum gaps_op op = GAPS_OP_SET; - enum gaps_scope scope = GAPS_SCOPE_ALL; - bool inner = true; - - if (strcmp(argv[0], "inner") == 0) { - amount_idx++; - inner = true; - } else if (strcmp(argv[0], "outer") == 0) { - amount_idx++; - inner = false; - } + return cmd_results_new(CMD_INVALID, "gaps", + "gaps edge_gaps on|off|toggle"); + } + arrange_root(); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} - // If one of the long variants of the gaps command is used - // (which starts with inner|outer) check the number of args - if (amount_idx > 0) { // if we've seen inner|outer - if (argc > 2) { // check the longest variant - error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); - if (error) { - return error; - } - } else { // check the next longest format - error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); - if (error) { - return error; - } - } - } else { - error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 1); - if (error) { - return error; - } - } +// gaps inner|outer +static struct cmd_results *gaps_set_defaults(int argc, char **argv) { + struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); + if (error) { + return error; + } - if (argc == 4) { - // Long format: all|workspace|current. - if (strcmp(argv[amount_idx], "all") == 0) { - amount_idx++; - scope = GAPS_SCOPE_ALL; - } else if (strcmp(argv[amount_idx], "workspace") == 0) { - amount_idx++; - scope = GAPS_SCOPE_WORKSPACE; - } else if (strcmp(argv[amount_idx], "current") == 0) { - amount_idx++; - scope = GAPS_SCOPE_CURRENT; - } - - // Long format: set|plus|minus - if (strcmp(argv[amount_idx], "set") == 0) { - amount_idx++; - op = GAPS_OP_SET; - } else if (strcmp(argv[amount_idx], "plus") == 0) { - amount_idx++; - op = GAPS_OP_ADD; - } else if (strcmp(argv[amount_idx], "minus") == 0) { - amount_idx++; - op = GAPS_OP_SUBTRACT; - } - } + bool inner; + if (strcasecmp(argv[0], "inner") == 0) { + inner = true; + } else if (strcasecmp(argv[0], "outer") == 0) { + inner = false; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer '"); + } - char *end; - double val = strtod(argv[amount_idx], &end); - - if (strlen(end) && val == 0.0) { // invalid - // guess which variant of the command was attempted - if (argc == 1) { - return cmd_results_new(CMD_INVALID, "gaps", "gaps "); - } - if (argc == 2) { - return cmd_results_new(CMD_INVALID, "gaps", - "gaps inner|outer "); - } - return cmd_results_new(CMD_INVALID, "gaps", - "gaps inner|outer all|workspace|current set|plus|minus "); - } + char *end; + int amount = strtol(argv[1], &end, 10); + if (strlen(end) && strcasecmp(end, "px") != 0) { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer '"); + } - if (amount_idx == 0) { // gaps - config->gaps_inner = val; - config->gaps_outer = val; - arrange_root(); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); - } - // Other variants. The middle-length variant (gaps inner|outer ) - // just defaults the scope to "all" and defaults the op to "set". - - double total; - switch (op) { - case GAPS_OP_SUBTRACT: { - total = (inner ? config->gaps_inner : config->gaps_outer) - val; - if (total < 0) { - total = 0; - } - break; - } - case GAPS_OP_ADD: { - total = (inner ? config->gaps_inner : config->gaps_outer) + val; - break; - } - case GAPS_OP_SET: { - total = val; - break; - } - } + if (inner) { + config->gaps_inner = amount; + } else { + config->gaps_outer = amount; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} - if (scope == GAPS_SCOPE_ALL) { - if (inner) { - config->gaps_inner = total; - } else { - config->gaps_outer = total; - } - arrange_root(); - } else { - if (scope == GAPS_SCOPE_WORKSPACE) { - struct sway_workspace *ws = config->handler_context.workspace; - ws->has_gaps = true; - if (inner) { - ws->gaps_inner = total; - } else { - ws->gaps_outer = total; - } - arrange_workspace(ws); - } else { - struct sway_container *c = config->handler_context.container; - c->has_gaps = true; - if (inner) { - c->gaps_inner = total; - } else { - c->gaps_outer = total; - } - arrange_workspace(c->workspace); - } - } +static void configure_gaps(struct sway_workspace *ws, void *_data) { + struct gaps_data *data = _data; + int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer; + + switch (data->operation) { + case GAPS_OP_SET: + *prop = data->amount; + break; + case GAPS_OP_ADD: + *prop += data->amount; + break; + case GAPS_OP_SUBTRACT: + *prop -= data->amount; + break; + } + arrange_workspace(ws); +} + +// gaps inner|outer current|all set|plus|minus +static struct cmd_results *gaps_set_runtime(int argc, char **argv) { + struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); + if (error) { + return error; + } + + struct gaps_data data; + + if (strcasecmp(argv[0], "inner") == 0) { + data.inner = true; + } else if (strcasecmp(argv[0], "outer") == 0) { + data.inner = false; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus '"); + } + + bool all; + if (strcasecmp(argv[1], "current") == 0) { + all = false; + } else if (strcasecmp(argv[1], "all") == 0) { + all = true; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus '"); + } + + if (strcasecmp(argv[2], "set") == 0) { + data.operation = GAPS_OP_SET; + } else if (strcasecmp(argv[2], "plus") == 0) { + data.operation = GAPS_OP_ADD; + } else if (strcasecmp(argv[2], "minus") == 0) { + data.operation = GAPS_OP_SUBTRACT; + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus '"); + } + + char *end; + data.amount = strtol(argv[3], &end, 10); + if (strlen(end) && strcasecmp(end, "px") != 0) { + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer current|all set|plus|minus '"); + } + + if (all) { + root_for_each_workspace(configure_gaps, &data); + } else { + configure_gaps(config->handler_context.workspace, &data); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } + +// gaps edge_gaps on|off|toggle +// gaps inner|outer - sets defaults for workspaces +// gaps inner|outer current|all set|plus|minus - runtime only +struct cmd_results *cmd_gaps(int argc, char **argv) { + struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); + if (error) { + return error; + } + + if (strcmp(argv[0], "edge_gaps") == 0) { + return gaps_edge_gaps(argc, argv); + } + + if (argc == 2) { + return gaps_set_defaults(argc, argv); + } + if (argc == 4) { + if (config->active) { + return gaps_set_runtime(argc, argv); + } else { + return cmd_results_new(CMD_INVALID, "gaps", + "This syntax can only be used when sway is running"); + } + } + return cmd_results_new(CMD_INVALID, "gaps", + "Expected 'gaps inner|outer ' or " + "'gaps inner|outer current|all set|plus|minus '"); +} diff --git a/sway/commands/layout.c b/sway/commands/layout.c index ef3ec1cb..c2ce2e78 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -138,15 +138,14 @@ struct cmd_results *cmd_layout(int argc, char **argv) { } container->layout = new_layout; container_update_representation(container); - arrange_container(container); } else { if (old_layout != L_TABBED && old_layout != L_STACKED) { workspace->prev_split_layout = old_layout; } workspace->layout = new_layout; workspace_update_representation(workspace); - arrange_workspace(workspace); } + arrange_workspace(workspace); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 2ce7872d..63f29641 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -20,6 +20,8 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) { return NULL; } wsc->workspace = strdup(ws_name); + wsc->gaps_inner = -1; + wsc->gaps_outer = -1; list_add(config->workspace_configs, wsc); return wsc; } @@ -37,6 +39,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } int output_location = -1; + int gaps_location = -1; for (int i = 0; i < argc; ++i) { if (strcasecmp(argv[i], "output") == 0) { @@ -44,6 +47,12 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { break; } } + for (int i = 0; i < argc; ++i) { + if (strcasecmp(argv[i], "gaps") == 0) { + gaps_location = i; + break; + } + } if (output_location >= 0) { if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { return error; @@ -57,6 +66,35 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } free(wsc->output); wsc->output = strdup(argv[output_location + 1]); + } else if (gaps_location >= 0) { + if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) { + return error; + } + char *ws_name = join_args(argv, argc - 3); + struct workspace_config *wsc = workspace_config_find_or_create(ws_name); + free(ws_name); + if (!wsc) { + return cmd_results_new(CMD_FAILURE, "workspace gaps", + "Unable to allocate workspace output"); + } + int *prop = NULL; + if (strcasecmp(argv[gaps_location + 1], "inner") == 0) { + prop = &wsc->gaps_inner; + } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) { + prop = &wsc->gaps_outer; + } else { + return cmd_results_new(CMD_FAILURE, "workspace gaps", + "Expected 'workspace gaps inner|outer '"); + } + char *end; + int val = strtol(argv[gaps_location + 2], &end, 10); + + if (strlen(end)) { + free(end); + return cmd_results_new(CMD_FAILURE, "workspace gaps", + "Expected 'workspace gaps inner|outer '"); + } + *prop = val >= 0 ? val : 0; } else { if (config->reading || !config->active) { return cmd_results_new(CMD_DEFER, "workspace", NULL); diff --git a/sway/tree/container.c b/sway/tree/container.c index baaa82fd..e329f835 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1022,7 +1022,7 @@ void container_add_gaps(struct sway_container *c) { struct sway_workspace *ws = c->workspace; - c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; + c->current_gaps = ws->gaps_inner; c->x += c->current_gaps; c->y += c->current_gaps; c->width -= 2 * c->current_gaps; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index d65a3e4c..9dd5c815 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -68,6 +68,20 @@ struct sway_workspace *workspace_create(struct sway_output *output, ws->output_priority = create_list(); workspace_output_add_priority(ws, output); + ws->gaps_outer = config->gaps_outer; + ws->gaps_inner = config->gaps_inner; + if (name) { + struct workspace_config *wsc = workspace_find_config(name); + if (wsc) { + if (wsc->gaps_outer != -1) { + ws->gaps_outer = wsc->gaps_outer; + } + if (wsc->gaps_inner != -1) { + ws->gaps_inner = wsc->gaps_inner; + } + } + } + output_add_workspace(output, ws); output_sort_workspaces(output); @@ -632,13 +646,13 @@ void workspace_add_gaps(struct sway_workspace *ws) { return; } - ws->current_gaps = ws->has_gaps ? ws->gaps_outer : config->gaps_outer; + ws->current_gaps = ws->gaps_outer; if (ws->layout == L_TABBED || ws->layout == L_STACKED) { // We have to add inner gaps for this, because children of tabbed and // stacked containers don't apply their own gaps - they assume the // tabbed/stacked container is using gaps. - ws->current_gaps += ws->has_gaps ? ws->gaps_inner : config->gaps_inner; + ws->current_gaps += ws->gaps_inner; } ws->x += ws->current_gaps; From bb708d0f82d6e418ada6b0b5798455c8213e5412 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 29 Sep 2018 12:58:54 +1000 Subject: [PATCH 2/3] Don't allow negative gaps --- sway/commands/gaps.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 3791fcb4..2e0876a9 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -68,6 +68,9 @@ static struct cmd_results *gaps_set_defaults(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps inner|outer '"); } + if (amount < 0) { + amount = 0; + } if (inner) { config->gaps_inner = amount; @@ -92,6 +95,9 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) { *prop -= data->amount; break; } + if (*prop < 0) { + *prop = 0; + } arrange_workspace(ws); } From 7661c71b3323f4bc6a8253b16cb12be8ef1a1d12 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 29 Sep 2018 12:59:02 +1000 Subject: [PATCH 3/3] Update gaps documentation --- sway/sway.5.scd | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1526eee3..aa5b38ab 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -419,19 +419,17 @@ The default colors are: inner gap is nonzero. When _off_, gaps will only be added between views. _toggle_ cannot be used in the configuration file. -*gaps* - Sets _amount_ pixels of gap between windows and around each workspace. - *gaps* inner|outer - Sets default _amount_ pixels of _inner_ or _outer_ gap, where the former - affects spacing between views and the latter affects the space around each - workspace. + Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner + affects spacing around each view and outer affects the spacing around each + workspace. Outer gaps are in addition to inner gaps. + + This affects new workspaces only, and is used when the workspace doesn't + have its own gaps settings (see: workspace gaps inner|outer ). -*gaps* inner|outer all|workspace|current set|plus|minus - Changes the gaps for the _inner_ or _outer_ gap. _all_ changes the gaps for - all views or workspace, _workspace_ changes gaps for all views in current - workspace (or current workspace), and _current_ changes gaps for the current - view or workspace. +*gaps* inner|outer all|current set|plus|minus + Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the + _current_ workspace. *hide\_edge\_borders* none|vertical|horizontal|both|smart Hides window borders adjacent to the screen edges. Default is _none_. @@ -580,6 +578,10 @@ match any output by using the output name "\*". *workspace* back_and_forth Switches to the previously focused workspace. +*workspace* gaps inner|outer + 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_.