move swap_workspace logic to swap command

Co-authored-by: Arne <ge47com@tum.de>
This commit is contained in:
Fabian Specht 2024-02-26 18:50:10 +01:00
parent afc3a54a85
commit 4cad9a1c43
No known key found for this signature in database
GPG key ID: 3E324CC0E6232463
5 changed files with 191 additions and 216 deletions

View file

@ -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 },

View file

@ -1,7 +1,9 @@
#include <strings.h>
#include <ctype.h>
#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 <arg>'";
"Expected 'swap (container with id|con_id|mark <arg>) |\n\t(wokspace with [number] <name>)'";
/*
* 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);
}

View file

@ -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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
/*
* 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 <name|number x>
*/
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);
}

View file

@ -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',

View file

@ -344,18 +344,20 @@ set|plus|minus|toggle <amount>
"Sticks" a floating window to the current output so that it shows up on all
workspaces.
*swap* container with id|con_id|mark <arg>
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 <arg>) |
(workspace with [number] <number>)
*swap_workspace* with [number] <name>
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* <format>
Sets the format of window titles. The following placeholders may be used: