diff --git a/include/sway/commands.h b/include/sway/commands.h index 545b21e6..8e91c158 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -136,6 +136,7 @@ sway_cmd cmd_mark; sway_cmd cmd_mode; sway_cmd cmd_mouse_warping; sway_cmd cmd_move; +sway_cmd cmd_nop; sway_cmd cmd_opacity; sway_cmd cmd_new_float; sway_cmd cmd_new_window; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index b4ff7d49..7a1e547b 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -7,10 +7,11 @@ #include "tree/view.h" enum criteria_type { - CT_COMMAND = 1 << 0, - CT_ASSIGN_OUTPUT = 1 << 1, - CT_ASSIGN_WORKSPACE = 1 << 2, - CT_NO_FOCUS = 1 << 3, + CT_COMMAND = 1 << 0, + CT_ASSIGN_OUTPUT = 1 << 1, + CT_ASSIGN_WORKSPACE = 1 << 2, + CT_ASSIGN_WORKSPACE_NUMBER = 1 << 3, + CT_NO_FOCUS = 1 << 4, }; struct criteria { diff --git a/sway/commands.c b/sway/commands.c index 364c26da..d9c54adc 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -146,6 +146,7 @@ static struct cmd_handler command_handlers[] = { { "layout", cmd_layout }, { "mark", cmd_mark }, { "move", cmd_move }, + { "nop", cmd_nop }, { "opacity", cmd_opacity }, { "reload", cmd_reload }, { "rename", cmd_rename }, diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 0bc0929a..04582e88 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -22,27 +22,38 @@ struct cmd_results *cmd_assign(int argc, char **argv) { return error; } - ++argv; - int target_len = argc - 1; + --argc; ++argv; if (strncmp(*argv, "→", strlen("→")) == 0) { - if (argc < 3) { + if (argc < 2) { free(criteria); return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); } + --argc; ++argv; - --target_len; } if (strcmp(*argv, "output") == 0) { criteria->type = CT_ASSIGN_OUTPUT; - ++argv; - --target_len; + --argc; ++argv; } else { - criteria->type = CT_ASSIGN_WORKSPACE; + if (strcmp(*argv, "workspace") == 0) { + --argc; ++argv; + } + if (strcmp(*argv, "number") == 0) { + --argc; ++argv; + if (argv[0][0] < '0' || argv[0][0] > '9') { + free(criteria); + return cmd_results_new(CMD_INVALID, "assign", + "Invalid workspace number '%s'", argv[0]); + } + criteria->type = CT_ASSIGN_WORKSPACE_NUMBER; + } else { + criteria->type = CT_ASSIGN_WORKSPACE; + } } - criteria->target = join_args(argv, target_len); + criteria->target = join_args(argv, argc); list_add(config->criteria, criteria); wlr_log(WLR_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 00e39ae7..5ce7919b 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -26,7 +26,16 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { return error; } - tmp = join_args(argv + 1, argc - 1); + --argc; ++argv; + } + + if (argv[0][0] == '\'' || argv[0][0] == '"') { + if (argc > 0) { + return cmd_results_new(CMD_INVALID, "exec_always", + "command cannot be partially quoted"); + } + tmp = strdup(argv[0]); + strip_quotes(tmp); } else { tmp = join_args(argv, argc); } diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 135a2908..fe15b4c7 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -52,6 +52,10 @@ static struct cmd_results *focus_mode(struct sway_container *con, } if (new_focus) { seat_set_focus(seat, new_focus); + } else { + return cmd_results_new(CMD_FAILURE, "focus", + "Failed to find a %s container in workspace", + floating ? "floating" : "tiling"); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index acdc50b5..33d1ee4a 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include #include #include #include @@ -22,7 +23,7 @@ static const char *expected_syntax = "Expected 'move <[px] px>' or " "'move [--no-auto-back-and-forth] [to] workspace ' or " - "'move [--no-auto-back-and-forth] [to] output ' or " + "'move [to] output ' or " "'move [to] mark '"; static struct sway_container *output_in_direction(const char *direction, @@ -124,7 +125,11 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, return cmd_results_new(CMD_INVALID, "move", expected_syntax); } - ws_name = strdup(argv[3]); + if (!isdigit(argv[3][0])) { + return cmd_results_new(CMD_INVALID, "move", + "Invalid workspace number '%s'", argv[3]); + } + ws_name = join_args(argv + 3, argc - 3); ws = workspace_by_number(ws_name); } else { ws_name = join_args(argv + 2, argc - 2); diff --git a/sway/commands/nop.c b/sway/commands/nop.c new file mode 100644 index 00000000..c12fe15a --- /dev/null +++ b/sway/commands/nop.c @@ -0,0 +1,5 @@ +#include "sway/commands.h" + +struct cmd_results *cmd_nop(int argc, char **argv) { + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/rename.c b/sway/commands/rename.c index c69bbdac..21d2aa64 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include #include #include #include "log.h" @@ -34,6 +35,10 @@ struct cmd_results *cmd_rename(int argc, char **argv) { } } else if (strcasecmp(argv[1], "number") == 0) { // 'rename workspace number x to new_name' + if (!isdigit(argv[2][0])) { + return cmd_results_new(CMD_INVALID, "rename", + "Invalid workspace number '%s'", argv[2]); + } workspace = workspace_by_number(argv[2]); while (argn < argc && strcasecmp(argv[argn], "to") != 0) { ++argn; @@ -67,7 +72,8 @@ struct cmd_results *cmd_rename(int argc, char **argv) { strcasecmp(new_name, "next_on_output") == 0 || strcasecmp(new_name, "prev_on_output") == 0 || strcasecmp(new_name, "back_and_forth") == 0 || - strcasecmp(new_name, "current") == 0) { + strcasecmp(new_name, "current") == 0 || + strcasecmp(new_name, "number") == 0) { free(new_name); return cmd_results_new(CMD_INVALID, "rename", "Cannot use special workspace name '%s'", argv[argn]); diff --git a/sway/commands/set.c b/sway/commands/set.c index ea388d3b..be51230b 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -25,23 +25,13 @@ void free_sway_variable(struct sway_variable *var) { } struct cmd_results *cmd_set(int argc, char **argv) { - char *tmp; struct cmd_results *error = NULL; if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) { return error; } if (argv[0][0] != '$') { - wlr_log(WLR_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); - - size_t size = snprintf(NULL, 0, "$%s", argv[0]); - tmp = malloc(size + 1); - if (!tmp) { - return cmd_results_new(CMD_FAILURE, "set", "Not possible to create variable $'%s'", argv[0]); - } - snprintf(tmp, size+1, "$%s", argv[0]); - - argv[0] = tmp; + return cmd_results_new(CMD_INVALID, "set", "variable '%s' must start with $", argv[0]); } struct sway_variable *var = NULL; diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 732ccb98..a0dd7215 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c @@ -36,5 +36,21 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { container->is_sticky = wants_sticky; + if (wants_sticky) { + // move container to focused workspace + struct sway_container *output = container_parent(container, C_OUTPUT); + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus_inactive(seat, output); + struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); + struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); + if (current_workspace != focused_workspace) { + container_move_to(container, focused_workspace); + arrange_windows(focused_workspace); + if (!container_reap_empty(current_workspace)) { + arrange_windows(current_workspace); + } + } + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index f5558bb4..ceb4cd6e 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include #include #include #include "sway/commands.h" @@ -60,9 +61,13 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { struct sway_container *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { - cmd_results_new(CMD_INVALID, "workspace", + return cmd_results_new(CMD_INVALID, "workspace", "Expected workspace number"); } + if (!isdigit(argv[1][0])) { + return cmd_results_new(CMD_INVALID, "workspace", + "Invalid workspace number '%s'", argv[1]); + } if (!(ws = workspace_by_number(argv[1]))) { char *name = join_args(argv + 1, argc - 1); ws = workspace_create(NULL, name); diff --git a/sway/meson.build b/sway/meson.build index 2a457270..676422d0 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -64,6 +64,7 @@ sway_sources = files( 'commands/mouse_warping.c', 'commands/move.c', 'commands/no_focus.c', + 'commands/nop.c', 'commands/output.c', 'commands/reload.c', 'commands/rename.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 70b74a45..83188067 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -51,7 +51,7 @@ The following commands may only be used in the configuration file. *wordexp*(3) for details). The same include file can only be included once; subsequent attempts will be ignored. -*set* +*set* $ Sets variable $_name_ to _value_. You can use the new variable in the arguments of future commands. @@ -132,7 +132,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). If unspecified, the default is 10 pixels. Pixels are ignored when moving tiled containers. -*move* [absolute] position [px] [px] +*move* [absolute] position [px] [px] Moves the focused container to the specified position. *move* [absolute] position center|mouse @@ -154,7 +154,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). Moves the focused container to the previous or next workspace on this output, wrapping around if already at the first or last workspace. -*move* container|window [to] workspace back_and_forth +*move* container|window [to] workspace back\_and\_forth Moves the focused container to previously focused workspace. *move* container|window|workspace [to] output @@ -167,6 +167,10 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *move* [to] scratchpad Moves the focused window to the scratchpad. +*nop* + A no operation command that can be used to override default behaviour. The + optional comment argument is ignored, but logged for debugging purposes. + *reload* Reloads the sway config file and applies any changes. @@ -215,13 +219,20 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). The following commands may be used either in the configuration file or at runtime. -*assign* [→] +*assign* [→] [workspace] [number] Assigns views matching _criteria_ (see *CRITERIA* for details) to _workspace_. The → (U+2192) is optional and cosmetic. This command is equivalent to: for\_window move container to workspace +*assign* [→] output left|right|up|down| + Assigns views matching _criteria_ (see *CRITERIA* for details) to the + specified output. The → (U+2192) is optional and cosmetic. This command is + equivalent to: + + for\_window move container to output + *bindsym* [--release|--locked] Binds _key combo_ to execute the sway command _command_ when pressed. You may use XKB key names here (*xev*(1) is a good tool for discovering these). diff --git a/sway/tree/view.c b/sway/tree/view.c index 1c1fdb47..7a2c1950 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -450,12 +450,22 @@ static struct sway_container *select_workspace(struct sway_view *view) { // Check if there's any `assign` criteria for the view list_t *criterias = criteria_for_view(view, - CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); + CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); struct sway_container *ws = NULL; for (int i = 0; i < criterias->length; ++i) { struct criteria *criteria = criterias->items[i]; - if (criteria->type == CT_ASSIGN_WORKSPACE) { - ws = workspace_by_name(criteria->target); + if (criteria->type == CT_ASSIGN_OUTPUT) { + struct sway_container *output = output_by_name(criteria->target); + if (output) { + ws = seat_get_active_child(seat, output); + break; + } + } else { + // CT_ASSIGN_WORKSPACE(_NUMBER) + ws = criteria->type == CT_ASSIGN_WORKSPACE_NUMBER ? + workspace_by_number(criteria->target) : + workspace_by_name(criteria->target); + if (!ws) { if (strcasecmp(criteria->target, "back_and_forth") == 0) { if (prev_workspace_name) { @@ -466,13 +476,6 @@ static struct sway_container *select_workspace(struct sway_view *view) { } } break; - } else { - // CT_ASSIGN_OUTPUT - struct sway_container *output = output_by_name(criteria->target); - if (output) { - ws = seat_get_active_child(seat, output); - break; - } } } list_free(criterias); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index b7090de6..a6d1870c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -82,11 +82,6 @@ struct sway_container *workspace_create(struct sway_container *output, } char *prev_workspace_name = NULL; -struct workspace_by_number_data { - int len; - const char *cset; - const char *name; -}; void next_name_map(struct sway_container *ws, void *data) { int *count = data; @@ -154,7 +149,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding, wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); // Make sure the workspace number doesn't already exist - if (workspace_by_number(_target)) { + if (isdigit(_target[0]) && workspace_by_number(_target)) { free(_target); free(dup); return; @@ -233,18 +228,18 @@ static bool _workspace_by_number(struct sway_container *view, void *data) { if (view->type != C_WORKSPACE) { return false; } - struct workspace_by_number_data *wbnd = data; - int a = strspn(view->name, wbnd->cset); - return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0; + char *name = data; + char *view_name = view->name; + while (isdigit(*name)) { + if (*name++ != *view_name++) { + return false; + } + } + return !isdigit(*view_name); } struct sway_container *workspace_by_number(const char* name) { - struct workspace_by_number_data wbnd = {0, "1234567890", name}; - wbnd.len = strspn(name, wbnd.cset); - if (wbnd.len <= 0) { - return NULL; - } - return root_find_workspace(_workspace_by_number, (void *) &wbnd); + return root_find_workspace(_workspace_by_number, (void *) name); } static bool _workspace_by_name(struct sway_container *view, void *data) {