From 4cad9a1c43aaf72f7fc7f817f25a0b3cd895a811 Mon Sep 17 00:00:00 2001 From: Fabian Specht Date: Mon, 26 Feb 2024 18:50:10 +0100 Subject: [PATCH] move swap_workspace logic to swap command Co-authored-by: Arne --- sway/commands.c | 1 - sway/commands/swap.c | 191 ++++++++++++++++++++++++++++++--- sway/commands/swap_workspace.c | 190 -------------------------------- sway/meson.build | 1 - sway/sway.5.scd | 24 +++-- 5 files changed, 191 insertions(+), 216 deletions(-) delete mode 100644 sway/commands/swap_workspace.c diff --git a/sway/commands.c b/sway/commands.c index 85e7e4fc..8d003dfa 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -136,7 +136,6 @@ static const struct cmd_handler command_handlers[] = { { "splitv", cmd_splitv }, { "sticky", cmd_sticky }, { "swap", cmd_swap }, - { "swap_workspace", cmd_swap_workspace_content }, { "title_format", cmd_title_format }, { "unmark", cmd_unmark }, { "urgent", cmd_urgent }, diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e142eede..dac39a69 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,7 +1,9 @@ #include +#include #include "config.h" #include "log.h" #include "sway/commands.h" +#include "sway/ipc-server.h" #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" @@ -11,7 +13,162 @@ #include "stringop.h" static const char expected_syntax[] = - "Expected 'swap container with id|con_id|mark '"; + "Expected 'swap (container with id|con_id|mark ) |\n\t(wokspace with [number] )'"; + +/* + * prepare the containers inside a workspace to be moved to another + * workspace. This includes setting a few properties of the containers + * as well as ipc events + */ + +static void handle_container_after_move(struct sway_container *container, + void *data) { + node_set_dirty(&container->node); + container_update_representation(container); + + struct sway_workspace *old_workspace = container->pending.workspace; + struct sway_output *old_output = container->pending.workspace->output; + + struct sway_workspace *destination = data; + container->pending.workspace = destination; + + // handle floating containers here by updating their position + if (container_is_floating(container)) { + if (old_output == destination->output || + container->pending.fullscreen_mode) return; + + struct wlr_box workspace_box, old_workspace_box; + + workspace_get_box(destination, &workspace_box); + workspace_get_box(old_workspace, &old_workspace_box); + + floating_fix_coordinates(container, + &old_workspace_box, &workspace_box); + + if (!container->scratchpad || !destination->output) return; + + struct wlr_box output_box; + output_get_box(destination->output, &output_box); + container->transform = workspace_box; + } + + if (container->view) { + ipc_event_window(container, "move"); + } +} + + +/* + * swap the properties necessary to preserve the layout + * as well as their respective contents + */ + +static void swap_workspace_properties(struct sway_workspace *cur_ws, + struct sway_workspace *oth_ws) { + struct sway_workspace cur_ws_cpy = *cur_ws; + + cur_ws->tiling = oth_ws->tiling; + oth_ws->tiling = cur_ws_cpy.tiling; + cur_ws->floating = oth_ws->floating; + oth_ws->floating = cur_ws_cpy.floating; + cur_ws->layout = oth_ws->layout; + oth_ws->layout = cur_ws_cpy.layout; + cur_ws->prev_split_layout = oth_ws->prev_split_layout; + oth_ws->prev_split_layout = cur_ws_cpy.prev_split_layout; + cur_ws->current_gaps = oth_ws->current_gaps; + oth_ws->current_gaps = cur_ws_cpy.current_gaps; + cur_ws->gaps_outer = oth_ws->gaps_outer; + oth_ws->gaps_outer = cur_ws_cpy.gaps_outer; + cur_ws->gaps_inner = oth_ws->gaps_inner; + oth_ws->gaps_inner = cur_ws_cpy.gaps_inner; +} + +static void set_new_focus(struct sway_workspace *ws, struct sway_seat *seat) { + if (ws->tiling->length) { + // this needs to be more specific (focus not just every container, + // but single windows + struct sway_container *container = ws->tiling->items[0]; + struct sway_container *first_view = container_get_first_view(container); + + if (!first_view) { + seat_set_focus(seat, &ws->node); + } + + seat_set_focus(seat, &first_view->node); + } else if (ws->floating->length) { + seat_set_focus(seat, ws->floating->items[0]); + } else { + seat_set_focus(seat, &ws->node); + } +} + +static struct cmd_results *swap_workspaces(int argc, char **argv) { + char *ws_name = NULL; + struct sway_workspace *oth_ws; + + if (strcasecmp(argv[2], "number") == 0) { + if (!isdigit(argv[3][0])) { + return cmd_results_new(CMD_INVALID, + "Invalid workspace number '%s'", argv[3]); + } + + ws_name = join_args(argv + 3, argc - 3); + oth_ws = workspace_by_number(ws_name); + } else { + ws_name = join_args(argv + 2, argc - 2); + oth_ws = workspace_by_name(ws_name); + } + + if (!oth_ws) { + oth_ws = workspace_create(NULL, ws_name); + + if (!oth_ws) { + return cmd_results_new(CMD_FAILURE, + "Unable to create new workspace"); + } + } + + free(ws_name); + + // second workspace is the one currently focused + struct sway_workspace *cur_ws = config->handler_context.workspace; + if (!cur_ws) { + return cmd_results_new(CMD_FAILURE, NULL); + } + + // exit early if there is nothing to swap + if (cur_ws == oth_ws) return cmd_results_new(CMD_SUCCESS, NULL); + + // save seat to set the focus later + struct sway_seat *seat = config->handler_context.seat; + swap_workspace_properties(cur_ws, oth_ws); + + node_set_dirty(&cur_ws->node); + node_set_dirty(&oth_ws->node); + workspace_update_representation(cur_ws); + workspace_update_representation(oth_ws); + + // before rearranging the workspaces we have to set a few properties + // such as dirty + workspace_for_each_container(cur_ws, handle_container_after_move, cur_ws); + workspace_for_each_container(oth_ws, handle_container_after_move, oth_ws); + + workspace_detect_urgent(cur_ws); + workspace_detect_urgent(oth_ws); + + // after swapping we set the focus on the first container in the current + // workspace or the workspace itself if there is no container + set_new_focus(cur_ws, seat); + + // destroy other workspace in case it is empty + workspace_consider_destroy(oth_ws); + + // update both affected workspaces + arrange_workspace(cur_ws); + arrange_workspace(oth_ws); + + return cmd_results_new(CMD_SUCCESS, NULL); +} static bool test_con_id(struct sway_container *container, void *data) { size_t *con_id = data; @@ -34,19 +191,8 @@ static bool test_mark(struct sway_container *container, void *mark) { return false; } -struct cmd_results *cmd_swap(int argc, char **argv) { +static struct cmd_results *swap_containers(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "swap", EXPECTED_AT_LEAST, 4))) { - return error; - } - if (!root->outputs->length) { - return cmd_results_new(CMD_INVALID, - "Can't run this command while there's no outputs connected."); - } - - if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { - return cmd_results_new(CMD_INVALID, "%s", expected_syntax); - } struct sway_container *current = config->handler_context.container; struct sway_container *other = NULL; @@ -105,3 +251,22 @@ struct cmd_results *cmd_swap(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL); } + +struct cmd_results *cmd_swap(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "swap", EXPECTED_AT_LEAST, 4))) { + return error; + } + if (!root->outputs->length) { + return cmd_results_new(CMD_INVALID, + "Can't run this command while there's no outputs connected."); + } + + if (strcasecmp(argv[0], "container") == 0 && strcasecmp(argv[1], "with") == 0) { + return swap_containers(argc, argv); + } else if (strcasecmp(argv[0], "workspace") == 0 && strcasecmp(argv[1], "with") == 0) { + return swap_workspaces(argc, argv); + } + + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); +} diff --git a/sway/commands/swap_workspace.c b/sway/commands/swap_workspace.c deleted file mode 100644 index 1138ce57..00000000 --- a/sway/commands/swap_workspace.c +++ /dev/null @@ -1,190 +0,0 @@ -#include "log.h" -#include "stringop.h" -#include "sway/output.h" -#include "sway/input/input-manager.h" -#include "sway/input/seat.h" -#include "sway/tree/arrange.h" -#include "sway/tree/container.h" -#include "sway/tree/view.h" -#include "sway/tree/workspace.h" -#include "sway/commands.h" -#include "sway/ipc-server.h" -#include "stringop.h" -#include -#include -#include -#include -#include -#include - - -/* - * prepare the containers inside a workspace to be moved to another - * workspace. This includes setting a few properties of the containers - * as well as ipc events - */ - -static void handle_container_after_move(struct sway_container *container, - void *data) { - node_set_dirty(&container->node); - container_update_representation(container); - - struct sway_workspace *old_workspace = container->pending.workspace; - struct sway_output *old_output = container->pending.workspace->output; - - struct sway_workspace *destination = data; - container->pending.workspace = destination; - - // handle floating containers here by updating their position - if (container_is_floating(container)) { - if (old_output == destination->output || - container->pending.fullscreen_mode) return; - - struct wlr_box workspace_box, old_workspace_box; - - workspace_get_box(destination, &workspace_box); - workspace_get_box(old_workspace, &old_workspace_box); - - floating_fix_coordinates(container, - &old_workspace_box, &workspace_box); - - if (!container->scratchpad || !destination->output) return; - - struct wlr_box output_box; - output_get_box(destination->output, &output_box); - container->transform = workspace_box; - } - - if (container->view) { - ipc_event_window(container, "move"); - } -} - - -/* - * swap the properties necessary to preserve the layout - * as well as their respective contents - */ - -static void swap_workspace_properties(struct sway_workspace *cur_ws, - struct sway_workspace *oth_ws) { - struct sway_workspace cur_ws_cpy = *cur_ws; - - cur_ws->tiling = oth_ws->tiling; - oth_ws->tiling = cur_ws_cpy.tiling; - cur_ws->floating = oth_ws->floating; - oth_ws->floating = cur_ws_cpy.floating; - cur_ws->layout = oth_ws->layout; - oth_ws->layout = cur_ws_cpy.layout; - cur_ws->prev_split_layout = oth_ws->prev_split_layout; - oth_ws->prev_split_layout = cur_ws_cpy.prev_split_layout; - cur_ws->current_gaps = oth_ws->current_gaps; - oth_ws->current_gaps = cur_ws_cpy.current_gaps; - cur_ws->gaps_outer = oth_ws->gaps_outer; - oth_ws->gaps_outer = cur_ws_cpy.gaps_outer; - cur_ws->gaps_inner = oth_ws->gaps_inner; - oth_ws->gaps_inner = cur_ws_cpy.gaps_inner; -} - -static void set_new_focus(struct sway_workspace *ws, struct sway_seat *seat) { - if (ws->tiling->length) { - // this needs to be more specific (focus not just every container, - // but single windows - struct sway_container *container = ws->tiling->items[0]; - struct sway_container *first_view = container_get_first_view(container); - - if (!first_view) { - seat_set_focus(seat, &ws->node); - } - - seat_set_focus(seat, &first_view->node); - } else if (ws->floating->length) { - seat_set_focus(seat, ws->floating->items[0]); - } else { - seat_set_focus(seat, &ws->node); - } -} - -/* - * swap the contents of the currently focused workspace with the content - * of the workspace specified in the args - * - * syntax: swap_workspace with - */ - -struct cmd_results *cmd_swap_workspace_content(int argc, char **argv) { - // parse arguments - if (argc < 2) { - return cmd_results_new(CMD_INVALID, "syntax not supported"); - } - - if (strcasecmp(argv[0], "with") != 0) { - return cmd_results_new(CMD_FAILURE, "Invalid syntax"); - } - - char *ws_name = NULL; - struct sway_workspace *oth_ws; - - if (strcasecmp(argv[1], "number") == 0) { - if (!isdigit(argv[2][0])) { - return cmd_results_new(CMD_INVALID, - "Invalid workspace number '%s'", argv[2]); - } - - ws_name = join_args(argv + 2, argc - 2); - oth_ws = workspace_by_number(ws_name); - } else { - ws_name = join_args(argv + 1, argc - 1); - oth_ws = workspace_by_name(ws_name); - } - - if (!oth_ws) { - oth_ws = workspace_create(NULL, ws_name); - - if (!oth_ws) { - return cmd_results_new(CMD_FAILURE, - "Unable to create new workspace"); - } - } - - free(ws_name); - - // second workspace is the one currently focused - struct sway_workspace *cur_ws = config->handler_context.workspace; - if (!cur_ws) { - return cmd_results_new(CMD_FAILURE, NULL); - } - - // exit early if there is nothing to swap - if (cur_ws == oth_ws) return cmd_results_new(CMD_SUCCESS, NULL); - - // save seat to set the focus later - struct sway_seat *seat = config->handler_context.seat; - swap_workspace_properties(cur_ws, oth_ws); - - node_set_dirty(&cur_ws->node); - node_set_dirty(&oth_ws->node); - workspace_update_representation(cur_ws); - workspace_update_representation(oth_ws); - - // before rearranging the workspaces we have to set a few properties - // such as dirty - workspace_for_each_container(cur_ws, handle_container_after_move, cur_ws); - workspace_for_each_container(oth_ws, handle_container_after_move, oth_ws); - - workspace_detect_urgent(cur_ws); - workspace_detect_urgent(oth_ws); - - // after swapping we set the focus on the first container in the current - // workspace or the workspace itself if there is no container - set_new_focus(cur_ws, seat); - - // destroy other workspace in case it is empty - workspace_consider_destroy(oth_ws); - - // update both affected workspaces - arrange_workspace(cur_ws); - arrange_workspace(oth_ws); - - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/meson.build b/sway/meson.build index 7bb3a5c9..d937e425 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -110,7 +110,6 @@ sway_sources = files( 'commands/swaybg_command.c', 'commands/swaynag_command.c', 'commands/swap.c', - 'commands/swap_workspace.c', 'commands/tiling_drag.c', 'commands/tiling_drag_threshold.c', 'commands/title_align.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 8c43e13f..ab7d7bfe 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -344,18 +344,20 @@ set|plus|minus|toggle "Sticks" a floating window to the current output so that it shows up on all workspaces. -*swap* container with id|con_id|mark - Swaps the position, geometry, and fullscreen status of two containers. The - first container can be selected either by criteria or focus. The second - container can be selected by _id_, _con_id_, or _mark_. _id_ can only be - used with xwayland views. If the first container has focus, it will retain - focus unless it is moved to a different workspace or the second container - becomes fullscreen on the same workspace as the first container. In either - of those cases, the second container will gain focus. +*swap* (container with id|con_id|mark ) | + (workspace with [number] ) -*swap_workspace* with [number] - Swaps the content of the currently focused workspace with the content - of the workspace specified as an argument. + If using the container keyword, swaps the position, geometry, and + fullscreen status of two containers. The first container can be selected + either by criteria or focus. The second container can be selected by + _id_, _con_id_, or _mark_. _id_ can only be used with xwayland views. + If the first container has focus, it will retain focus unless it is moved + to a different workspace or the second container becomes fullscreen on the + same workspace as the first container. In either of those cases, the + second container will gain focus. + + If using the workspace keyword, swaps the content of the currently focused + workspace with the content of the workspace specified as an argument. *title_format* Sets the format of window titles. The following placeholders may be used: