Implement swap command

This commit is contained in:
Brian Ashworth 2018-05-26 11:02:21 -04:00
parent fea654a6ce
commit 569f4e0e4c
7 changed files with 218 additions and 0 deletions

View file

@ -144,6 +144,7 @@ sway_cmd cmd_splitt;
sway_cmd cmd_splitv;
sway_cmd cmd_sticky;
sway_cmd cmd_swaybg_command;
sway_cmd cmd_swap;
sway_cmd cmd_title_format;
sway_cmd cmd_unmark;
sway_cmd cmd_workspace;

View file

@ -69,4 +69,6 @@ struct sway_container *container_split(struct sway_container *child,
void container_recursive_resize(struct sway_container *container,
double amount, enum resize_edge edge);
void container_swap(struct sway_container *con1, struct sway_container *con2);
#endif

View file

@ -186,6 +186,7 @@ static struct cmd_handler command_handlers[] = {
{ "splith", cmd_splith },
{ "splitt", cmd_splitt },
{ "splitv", cmd_splitv },
{ "swap", cmd_swap },
{ "title_format", cmd_title_format },
{ "unmark", cmd_unmark },
};

80
sway/commands/swap.c Normal file
View file

@ -0,0 +1,80 @@
#include <strings.h>
#include <wlr/util/log.h>
#include "sway/commands.h"
#include "sway/tree/layout.h"
#include "sway/tree/view.h"
#include "stringop.h"
static const char* EXPECTED_SYNTAX =
"Expected 'swap container with id|con_id|mark <arg>'";
static bool test_con_id(struct sway_container *container, void *con_id) {
return container->id == (size_t)con_id;
}
static bool test_id(struct sway_container *container, void *id) {
xcb_window_t *wid = id;
return (container->type == C_VIEW
&& container->sway_view->type == SWAY_VIEW_XWAYLAND
&& container->sway_view->wlr_xwayland_surface->window_id == *wid);
}
static bool test_mark(struct sway_container *container, void *mark) {
if (container->type == C_VIEW && container->sway_view->marks->length) {
return !list_seq_find(container->sway_view->marks,
(int (*)(const void *, const void *))strcmp, mark);
}
return false;
}
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 (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) {
return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
}
struct sway_container *current = config->handler_context.current_container;
struct sway_container *other;
char *value = join_args(argv + 3, argc - 3);
if (strcasecmp(argv[2], "id") == 0) {
xcb_window_t id = strtol(value, NULL, 0);
other = container_find(&root_container, test_id, (void *)&id);
} else if (strcasecmp(argv[2], "con_id") == 0) {
size_t con_id = atoi(value);
other = container_find(&root_container, test_con_id, (void *)con_id);
} else if (strcasecmp(argv[2], "mark") == 0) {
other = container_find(&root_container, test_mark, (void *)value);
} else {
free(value);
return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
}
if (!other) {
error = cmd_results_new(CMD_FAILURE, "swap",
"Failed to find %s '%s'", argv[2], value);
} else if (current->type < C_CONTAINER || other->type < C_CONTAINER) {
error = cmd_results_new(CMD_FAILURE, "swap",
"Can only swap with containers and views");
} else if (container_has_anscestor(current, other)
|| container_has_anscestor(other, current)) {
error = cmd_results_new(CMD_FAILURE, "swap",
"Cannot swap ancestor and descendant");
} else if (current->layout == L_FLOATING || other->layout == L_FLOATING) {
error = cmd_results_new(CMD_FAILURE, "swap",
"Swapping with floating containers is not supported");
}
free(value);
if (error) {
return error;
}
container_swap(current, other);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View file

@ -63,6 +63,7 @@ sway_sources = files(
'commands/show_marks.c',
'commands/split.c',
'commands/swaybg_command.c',
'commands/swap.c',
'commands/title_format.c',
'commands/unmark.c',
'commands/workspace.c',

View file

@ -167,6 +167,15 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
"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.
The following commands may be used either in the configuration file or at
runtime.

View file

@ -882,3 +882,127 @@ void container_recursive_resize(struct sway_container *container,
}
}
}
static void swap_places(struct sway_container *con1,
struct sway_container *con2) {
struct sway_container *temp = malloc(sizeof(struct sway_container));
temp->x = con1->x;
temp->y = con1->y;
temp->width = con1->width;
temp->height = con1->height;
temp->parent = con1->parent;
con1->x = con2->x;
con1->y = con2->y;
con1->width = con2->width;
con1->height = con2->height;
con2->x = temp->x;
con2->y = temp->y;
con2->width = temp->width;
con2->height = temp->height;
int temp_index = index_child(con1);
container_insert_child(con2->parent, con1, index_child(con2));
container_insert_child(temp->parent, con2, temp_index);
free(temp);
}
static void swap_focus(struct sway_container *con1,
struct sway_container *con2, struct sway_seat *seat,
struct sway_container *focus) {
if (focus == con1 || focus == con2) {
struct sway_container *ws1 = container_parent(con1, C_WORKSPACE);
struct sway_container *ws2 = container_parent(con2, C_WORKSPACE);
if (focus == con1 && (con2->parent->layout == L_TABBED
|| con2->parent->layout == L_STACKED)) {
if (workspace_is_visible(ws2)) {
seat_set_focus_warp(seat, con2, false);
}
seat_set_focus(seat, ws1 != ws2 ? con2 : con1);
} else if (focus == con2 && (con1->parent->layout == L_TABBED
|| con1->parent->layout == L_STACKED)) {
if (workspace_is_visible(ws1)) {
seat_set_focus_warp(seat, con1, false);
}
seat_set_focus(seat, ws1 != ws2 ? con1 : con2);
} else if (ws1 != ws2) {
seat_set_focus(seat, focus == con1 ? con2 : con1);
} else {
seat_set_focus(seat, focus);
}
} else {
seat_set_focus(seat, focus);
}
}
void container_swap(struct sway_container *con1, struct sway_container *con2) {
if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
return;
}
if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER,
"Can only swap containers and views")) {
return;
}
if (!sway_assert(!container_has_anscestor(con1, con2)
&& !container_has_anscestor(con2, con1),
"Cannot swap anscestor and descendant")) {
return;
}
if (!sway_assert(con1->layout != L_FLOATING && con2->layout != L_FLOATING,
"Swapping with floating containers is not supported")) {
return;
}
wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id);
int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen;
int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen;
if (fs1) {
view_set_fullscreen(con1->sway_view, false);
}
if (fs2) {
view_set_fullscreen(con2->sway_view, false);
}
struct sway_seat *seat = input_manager_get_default_seat(input_manager);
struct sway_container *focus = seat_get_focus(seat);
struct sway_container *vis1 = container_parent(
seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)),
C_WORKSPACE);
struct sway_container *vis2 = container_parent(
seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)),
C_WORKSPACE);
char *stored_prev_name = NULL;
if (prev_workspace_name) {
stored_prev_name = strdup(prev_workspace_name);
}
swap_places(con1, con2);
if (!workspace_is_visible(vis1)) {
seat_set_focus(seat, seat_get_focus_inactive(seat, vis1));
}
if (!workspace_is_visible(vis2)) {
seat_set_focus(seat, seat_get_focus_inactive(seat, vis2));
}
swap_focus(con1, con2, seat, focus);
if (stored_prev_name) {
free(prev_workspace_name);
prev_workspace_name = stored_prev_name;
}
arrange_children_of(con1->parent);
arrange_children_of(con2->parent);
if (fs1 && con2->type == C_VIEW) {
view_set_fullscreen(con2->sway_view, true);
}
if (fs2 && con1->type == C_VIEW) {
view_set_fullscreen(con1->sway_view, true);
}
}