Implement swap command

master
Brian Ashworth 7 years ago
parent fea654a6ce
commit 569f4e0e4c

@ -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;

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

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

@ -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);
}

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

@ -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.

@ -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);
}
}

Loading…
Cancel
Save