From 59e308480eb04c32ce9e803da06b0dde3edbefc9 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 23 Dec 2019 13:36:55 +0100 Subject: [PATCH] WIP Output-namespaced workspaces --- include/sway/commands.h | 1 + include/sway/config.h | 6 +++ include/sway/tree/workspace.h | 6 +-- sway/commands.c | 1 + sway/commands/move.c | 19 +++++---- sway/commands/rename.c | 12 ++++-- sway/commands/workspace.c | 14 +++---- sway/commands/workspace_namespace.c | 27 +++++++++++++ sway/meson.build | 1 + sway/sway.5.scd | 3 ++ sway/tree/output.c | 12 ++++-- sway/tree/root.c | 2 +- sway/tree/view.c | 13 ++++-- sway/tree/workspace.c | 61 ++++++++++++++++++----------- 14 files changed, 127 insertions(+), 51 deletions(-) create mode 100644 sway/commands/workspace_namespace.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 982125c19..898750b27 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -190,6 +190,7 @@ sway_cmd cmd_unmark; sway_cmd cmd_urgent; sway_cmd cmd_workspace; sway_cmd cmd_workspace_layout; +sway_cmd cmd_workspace_namespace; sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_xwayland; diff --git a/include/sway/config.h b/include/sway/config.h index aef6694d6..82a211a7c 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -471,6 +471,11 @@ enum xwayland_mode { XWAYLAND_MODE_IMMEDIATE }; +enum sway_workspace_namespace { + WORKSPACE_NAMESPACE_GLOBAL, + WORKSPACE_NAMESPACE_OUTPUT, +}; + /** * The configuration struct. The result of loading a config file. */ @@ -512,6 +517,7 @@ struct sway_config { enum sway_fowa focus_on_window_activation; enum sway_popup_during_fullscreen popup_during_fullscreen; enum xwayland_mode xwayland; + enum sway_workspace_namespace workspace_namespace; // swaybg char *swaybg_command; diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 41b597963..7c8ef2bd7 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -58,14 +58,14 @@ void workspace_begin_destroy(struct sway_workspace *workspace); void workspace_consider_destroy(struct sway_workspace *ws); -char *workspace_next_name(const char *output_name); +char *workspace_next_name(struct sway_output *output); bool workspace_switch(struct sway_workspace *workspace, bool no_auto_back_and_forth); -struct sway_workspace *workspace_by_number(const char* name); +struct sway_workspace *workspace_by_number(struct sway_output *output, const char* name); -struct sway_workspace *workspace_by_name(const char*); +struct sway_workspace *workspace_by_name(struct sway_output *output, const char* name); struct sway_workspace *workspace_output_next( struct sway_workspace *current, bool create); diff --git a/sway/commands.c b/sway/commands.c index e7f1eafe7..e6071caa0 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -105,6 +105,7 @@ static struct cmd_handler config_handlers[] = { { "swaybg_command", cmd_swaybg_command }, { "swaynag_command", cmd_swaynag_command }, { "workspace_layout", cmd_workspace_layout }, + { "workspace_namespace", cmd_workspace_namespace }, { "xwayland", cmd_xwayland }, }; diff --git a/sway/commands/move.c b/sway/commands/move.c index 6ad1c7ca0..e3cf85211 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -430,9 +430,9 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, strcasecmp(argv[1], "next_on_output") == 0 || strcasecmp(argv[1], "prev_on_output") == 0 || strcasecmp(argv[1], "current") == 0) { - ws = workspace_by_name(argv[1]); + ws = workspace_by_name(old_output, argv[1]); } else if (strcasecmp(argv[1], "back_and_forth") == 0) { - if (!(ws = workspace_by_name(argv[1]))) { + if (!(ws = workspace_by_name(old_output, argv[1]))) { if (seat->prev_workspace_name) { ws_name = strdup(seat->prev_workspace_name); } else { @@ -451,10 +451,10 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, "Invalid workspace number '%s'", argv[2]); } ws_name = join_args(argv + 2, argc - 2); - ws = workspace_by_number(ws_name); + ws = workspace_by_number(old_output, ws_name); } else { ws_name = join_args(argv + 1, argc - 1); - ws = workspace_by_name(ws_name); + ws = workspace_by_name(old_output, ws_name); } if (!no_auto_back_and_forth && config->auto_back_and_forth && @@ -465,7 +465,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, // if target workspace is the current one free(ws_name); ws_name = strdup(seat->prev_workspace_name); - ws = workspace_by_name(ws_name); + ws = workspace_by_name(old_output, ws_name); } } } @@ -483,7 +483,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, "on the same output"); } } - ws = workspace_create(NULL, ws_name); + ws = workspace_create(old_output, ws_name); } free(ws_name); struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); @@ -614,7 +614,7 @@ static void workspace_move_to_output(struct sway_workspace *workspace, // on the old output struct sway_seat *seat = config->handler_context.seat; if (old_output->workspaces->length == 0) { - char *ws_name = workspace_next_name(old_output->wlr_output->name); + char *ws_name = workspace_next_name(old_output); struct sway_workspace *ws = workspace_create(old_output, ws_name); free(ws_name); seat_set_raw_focus(seat, &ws->node); @@ -658,6 +658,11 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Can't find output with name/direction '%s'", argv[0]); } + if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT && + workspace_by_name(new_output, workspace->name) != NULL) { + free(workspace->name); + workspace->name = workspace_next_name(new_output); + } workspace_move_to_output(workspace, new_output); arrange_output(old_output); diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 3b855fdf7..c421703f9 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -31,6 +31,12 @@ struct cmd_results *cmd_rename(int argc, char **argv) { int argn = 1; struct sway_workspace *workspace = NULL; + struct sway_seat *seat = config->handler_context.seat; + struct sway_workspace *current = seat_get_focused_workspace(seat); + if (!current) { + return cmd_results_new(CMD_FAILURE, "No workspace available"); + } + if (strcasecmp(argv[1], "to") == 0) { // 'rename workspace to new_name' workspace = config->handler_context.workspace; @@ -40,7 +46,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Invalid workspace number '%s'", argv[2]); } - workspace = workspace_by_number(argv[2]); + workspace = workspace_by_number(current->output, argv[2]); while (argn < argc && strcasecmp(argv[argn], "to") != 0) { ++argn; } @@ -51,7 +57,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { ++end; } char *old_name = join_args(argv + argn, end - argn); - workspace = workspace_by_name(old_name); + workspace = workspace_by_name(current->output, old_name); free(old_name); argn = end; } @@ -79,7 +85,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Cannot use special workspace name '%s'", argv[argn]); } - struct sway_workspace *tmp_workspace = workspace_by_name(new_name); + struct sway_workspace *tmp_workspace = workspace_by_name(current->output, new_name); if (tmp_workspace) { free(new_name); if (tmp_workspace == workspace) { diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index b911b2f61..5a399c663 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -198,15 +198,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Invalid workspace number '%s'", argv[1]); } - if (!(ws = workspace_by_number(argv[1]))) { + if (!(ws = workspace_by_number(current->output, argv[1]))) { char *name = join_args(argv + 1, argc - 1); - ws = workspace_create(NULL, name); + ws = workspace_create(current->output, name); free(name); } } else if (strcasecmp(argv[0], "next") == 0 || strcasecmp(argv[0], "prev") == 0 || strcasecmp(argv[0], "current") == 0) { - ws = workspace_by_name(argv[0]); + ws = workspace_by_name(current->output, argv[0]); } else if (strcasecmp(argv[0], "next_on_output") == 0) { ws = workspace_output_next(current, create); } else if (strcasecmp(argv[0], "prev_on_output") == 0) { @@ -216,13 +216,13 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "There is no previous workspace"); } - if (!(ws = workspace_by_name(argv[0]))) { - ws = workspace_create(NULL, seat->prev_workspace_name); + if (!(ws = workspace_by_name(current->output, argv[0]))) { + ws = workspace_create(current->output, seat->prev_workspace_name); } } else { char *name = join_args(argv, argc); - if (!(ws = workspace_by_name(name))) { - ws = workspace_create(NULL, name); + if (!(ws = workspace_by_name(current->output, name))) { + ws = workspace_create(current->output, name); } free(name); } diff --git a/sway/commands/workspace_namespace.c b/sway/commands/workspace_namespace.c new file mode 100644 index 000000000..85e251b21 --- /dev/null +++ b/sway/commands/workspace_namespace.c @@ -0,0 +1,27 @@ +#include +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" +#include "util.h" + +// workspace_namespace [global|output] +struct cmd_results *cmd_workspace_namespace(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "workspace_namespace", EXPECTED_EQUAL_TO, 1))) { + return error; + } + if (strcasecmp(argv[0], "global") == 0 || + strcasecmp(argv[0], "default") == 0) { + config->workspace_namespace = WORKSPACE_NAMESPACE_GLOBAL; + } else if (strcasecmp(argv[0], "output") == 0) { + config->workspace_namespace = WORKSPACE_NAMESPACE_OUTPUT; + } else { + return cmd_results_new(CMD_FAILURE, + "Expected 'workspace_namespace '"); + } + return cmd_results_new(CMD_SUCCESS, NULL); +} \ No newline at end of file diff --git a/sway/meson.build b/sway/meson.build index 20fe02fb3..c032314d5 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -114,6 +114,7 @@ sway_sources = files( 'commands/urgent.c', 'commands/workspace.c', 'commands/workspace_layout.c', + 'commands/workspace_namespace.c', 'commands/ws_auto_back_and_forth.c', 'commands/xwayland.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 52ee9d28e..413a8119f 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -88,6 +88,9 @@ The following commands may only be used in the configuration file. *workspace_layout* default|stacking|tabbed Specifies the initial layout for new workspaces. +*workspace_namespace* default|global|output + Specifies the workspace namespacing. + *xwayland* enable|disable|force Enables or disables Xwayland support, which allows X11 applications to be used. _enable_ will lazily load Xwayland so Xwayland will not be launched diff --git a/sway/tree/output.c b/sway/tree/output.c index d2ede1f26..a77c2d50b 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -48,7 +48,7 @@ static void restore_workspaces(struct sway_output *output) { } if (other->workspaces->length == 0) { - char *next = workspace_next_name(other->wlr_output->name); + char *next = workspace_next_name(other); struct sway_workspace *ws = workspace_create(other, next); free(next); ipc_event_workspace(NULL, ws, "init"); @@ -109,7 +109,6 @@ bool output_enable(struct sway_output *output, struct output_config *oc) { if (!sway_assert(!output->enabled, "output is already enabled")) { return false; } - struct wlr_output *wlr_output = output->wlr_output; size_t len = sizeof(output->layers) / sizeof(output->layers[0]); for (size_t i = 0; i < len; ++i) { wl_list_init(&output->layers[i]); @@ -129,7 +128,7 @@ bool output_enable(struct sway_output *output, struct output_config *oc) { struct sway_workspace *ws = NULL; if (!output->workspaces->length) { // Create workspace - char *ws_name = workspace_next_name(wlr_output->name); + char *ws_name = workspace_next_name(output); sway_log(SWAY_DEBUG, "Creating default workspace %s", ws_name); ws = workspace_create(output, ws_name); // Set each seat's focus if not already set @@ -212,6 +211,13 @@ static void output_evacuate(struct sway_output *output) { continue; } + if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT && + workspace_by_name(new_output, workspace->name) != NULL) { + // Rename the workspace to avoid name collision + free(workspace->name); + workspace->name = workspace_next_name(new_output); + } + struct sway_workspace *new_output_ws = output_get_active_workspace(new_output); diff --git a/sway/tree/root.c b/sway/tree/root.c index a3830976a..f19d4c87d 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -252,7 +252,7 @@ struct sway_workspace *root_workspace_for_pid(pid_t pid) { found: if (pw && pw->workspace) { - ws = workspace_by_name(pw->workspace); + ws = workspace_by_name(pw->output, pw->workspace); if (!ws) { sway_log(SWAY_DEBUG, diff --git a/sway/tree/view.c b/sway/tree/view.c index 93d4fefce..642ca36d7 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -470,6 +470,11 @@ static void view_populate_pid(struct sway_view *view) { static struct sway_workspace *select_workspace(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(); + struct sway_workspace *current = seat_get_focused_workspace(seat); + if (!sway_assert(current != NULL, "no workspace is focused")) { + return NULL; + } + struct sway_output *output = current->output; // Check if there's any `assign` criteria for the view list_t *criterias = criteria_for_view(view, @@ -486,16 +491,16 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { } else { // CT_ASSIGN_WORKSPACE(_NUMBER) ws = criteria->type == CT_ASSIGN_WORKSPACE_NUMBER ? - workspace_by_number(criteria->target) : - workspace_by_name(criteria->target); + workspace_by_number(output, criteria->target) : + workspace_by_name(output, criteria->target); if (!ws) { if (strcasecmp(criteria->target, "back_and_forth") == 0) { if (seat->prev_workspace_name) { - ws = workspace_create(NULL, seat->prev_workspace_name); + ws = workspace_create(output, seat->prev_workspace_name); } } else { - ws = workspace_create(NULL, criteria->target); + ws = workspace_create(output, criteria->target); } } break; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 5568d1f56..286ae7533 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -171,15 +171,14 @@ void workspace_consider_destroy(struct sway_workspace *ws) { workspace_begin_destroy(ws); } -static bool workspace_valid_on_output(const char *output_name, +static bool workspace_valid_on_output(struct sway_output *output, const char *ws_name) { struct workspace_config *wsc = workspace_find_config(ws_name); char identifier[128]; - struct sway_output *output = output_by_name_or_id(output_name); if (!output) { return false; } - output_name = output->wlr_output->name; + char *output_name = output->wlr_output->name; output_get_identifier(identifier, sizeof(identifier), output); if (!wsc) { @@ -198,7 +197,7 @@ static bool workspace_valid_on_output(const char *output_name, } static void workspace_name_from_binding(const struct sway_binding * binding, - const char* output_name, int *min_order, char **earliest_name) { + struct sway_output* output, int *min_order, char **earliest_name) { char *cmdlist = strdup(binding->command); char *dup = cmdlist; char *name = NULL; @@ -245,7 +244,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding, sway_log(SWAY_DEBUG, "Isolated name from workspace number: '%s'", _target); // Make sure the workspace number doesn't already exist - if (isdigit(_target[0]) && workspace_by_number(_target)) { + if (isdigit(_target[0]) && workspace_by_number(output, _target)) { free(_target); free(dup); return; @@ -253,7 +252,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding, } // Make sure that the workspace doesn't already exist - if (workspace_by_name(_target)) { + if (workspace_by_name(output, _target)) { free(_target); free(dup); return; @@ -261,7 +260,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding, // make sure that the workspace can appear on the given // output - if (!workspace_valid_on_output(output_name, _target)) { + if (!workspace_valid_on_output(output, _target)) { free(_target); free(dup); return; @@ -279,7 +278,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding, free(dup); } -char *workspace_next_name(const char *output_name) { +char *workspace_next_name(struct sway_output* output) { + char *output_name = output->wlr_output->name; sway_log(SWAY_DEBUG, "Workspace: Generating new workspace name for output %s", output_name); // Scan for available workspace names by looking through output-workspace @@ -287,27 +287,22 @@ char *workspace_next_name(const char *output_name) { struct sway_mode *mode = config->current_mode; char identifier[128]; - struct sway_output *output = output_by_name_or_id(output_name); - if (!output) { - return NULL; - } - output_name = output->wlr_output->name; output_get_identifier(identifier, sizeof(identifier), output); int order = INT_MAX; char *target = NULL; for (int i = 0; i < mode->keysym_bindings->length; ++i) { workspace_name_from_binding(mode->keysym_bindings->items[i], - output_name, &order, &target); + output, &order, &target); } for (int i = 0; i < mode->keycode_bindings->length; ++i) { workspace_name_from_binding(mode->keycode_bindings->items[i], - output_name, &order, &target); + output, &order, &target); } 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 (workspace_by_name(wsc->workspace)) { + if (workspace_by_name(output, wsc->workspace)) { continue; } bool found = false; @@ -333,7 +328,7 @@ char *workspace_next_name(const char *output_name) { unsigned int ws_num = 1; do { snprintf(name, sizeof(name), "%u", ws_num++); - } while (workspace_by_number(name)); + } while (workspace_by_number(output, name)); return strdup(name); } @@ -348,7 +343,12 @@ static bool _workspace_by_number(struct sway_workspace *ws, void *data) { return !isdigit(*ws_name); } -struct sway_workspace *workspace_by_number(const char* name) { +struct sway_workspace *workspace_by_number(struct sway_output *output, + const char* name) { + if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT) { + return output_find_workspace(output, _workspace_by_number, + (void*)name); + } return root_find_workspace(_workspace_by_number, (void *) name); } @@ -356,7 +356,8 @@ static bool _workspace_by_name(struct sway_workspace *ws, void *data) { return strcasecmp(ws->name, data) == 0; } -struct sway_workspace *workspace_by_name(const char *name) { +struct sway_workspace *workspace_by_name(struct sway_output *output, + const char *name) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *current = seat_get_focused_workspace(seat); @@ -375,9 +376,23 @@ struct sway_workspace *workspace_by_name(const char *name) { if (!seat->prev_workspace_name) { return NULL; } + if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT) { + if (output == NULL) { + return NULL; + } + return output_find_workspace(output, _workspace_by_name, + (void*)seat->prev_workspace_name); + } return root_find_workspace(_workspace_by_name, (void*)seat->prev_workspace_name); } else { + if (config->workspace_namespace == WORKSPACE_NAMESPACE_OUTPUT) { + if (output == NULL) { + return NULL; + } + return output_find_workspace(output, _workspace_by_name, + (void*)name); + } return root_find_workspace(_workspace_by_name, (void*)name); } } @@ -401,7 +416,7 @@ static struct sway_workspace *workspace_output_prev_next_impl( if (!workspace_is_empty(workspace) && create && (index + dir < 0 || index + dir == output->workspaces->length)) { struct sway_output *output = workspace->output; - char *next = workspace_next_name(output->wlr_output->name); + char *next = workspace_next_name(output); workspace_create(output, next); free(next); } @@ -467,10 +482,10 @@ bool workspace_switch(struct sway_workspace *workspace, if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws && active_ws == workspace && seat->prev_workspace_name) { struct sway_workspace *new_ws = - workspace_by_name(seat->prev_workspace_name); + workspace_by_name(NULL, seat->prev_workspace_name); workspace = new_ws ? new_ws : - workspace_create(NULL, seat->prev_workspace_name); + workspace_create(active_ws->output, seat->prev_workspace_name); } sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", @@ -801,4 +816,4 @@ size_t workspace_num_tiling_views(struct sway_workspace *ws) { size_t count = 0; workspace_for_each_container(ws, count_tiling_views, &count); return count; -} +} \ No newline at end of file