summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-05-26 11:02:21 -0400
committerLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-05-26 11:05:02 -0400
commit569f4e0e4c75562c38848ea0bbaeb3b2f230b1a9 (patch)
tree02716bf706478cc5ca73d892dfc34c8a327897b3
parentReplace oft-failing abort with if statement (diff)
downloadsway-569f4e0e4c75562c38848ea0bbaeb3b2f230b1a9.tar.gz
sway-569f4e0e4c75562c38848ea0bbaeb3b2f230b1a9.tar.zst
sway-569f4e0e4c75562c38848ea0bbaeb3b2f230b1a9.zip
Implement swap command
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/tree/layout.h2
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/swap.c80
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway.5.scd9
-rw-r--r--sway/tree/layout.c124
7 files changed, 218 insertions, 0 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h
index d39ac56c..365068ae 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -144,6 +144,7 @@ sway_cmd cmd_splitt;
144sway_cmd cmd_splitv; 144sway_cmd cmd_splitv;
145sway_cmd cmd_sticky; 145sway_cmd cmd_sticky;
146sway_cmd cmd_swaybg_command; 146sway_cmd cmd_swaybg_command;
147sway_cmd cmd_swap;
147sway_cmd cmd_title_format; 148sway_cmd cmd_title_format;
148sway_cmd cmd_unmark; 149sway_cmd cmd_unmark;
149sway_cmd cmd_workspace; 150sway_cmd cmd_workspace;
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h
index cc999871..2e0f2abf 100644
--- a/include/sway/tree/layout.h
+++ b/include/sway/tree/layout.h
@@ -69,4 +69,6 @@ struct sway_container *container_split(struct sway_container *child,
69void container_recursive_resize(struct sway_container *container, 69void container_recursive_resize(struct sway_container *container,
70 double amount, enum resize_edge edge); 70 double amount, enum resize_edge edge);
71 71
72void container_swap(struct sway_container *con1, struct sway_container *con2);
73
72#endif 74#endif
diff --git a/sway/commands.c b/sway/commands.c
index 6cba0a1c..c3728afd 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -186,6 +186,7 @@ static struct cmd_handler command_handlers[] = {
186 { "splith", cmd_splith }, 186 { "splith", cmd_splith },
187 { "splitt", cmd_splitt }, 187 { "splitt", cmd_splitt },
188 { "splitv", cmd_splitv }, 188 { "splitv", cmd_splitv },
189 { "swap", cmd_swap },
189 { "title_format", cmd_title_format }, 190 { "title_format", cmd_title_format },
190 { "unmark", cmd_unmark }, 191 { "unmark", cmd_unmark },
191}; 192};
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
new file mode 100644
index 00000000..e925ad33
--- /dev/null
+++ b/sway/commands/swap.c
@@ -0,0 +1,80 @@
1#include <strings.h>
2#include <wlr/util/log.h>
3#include "sway/commands.h"
4#include "sway/tree/layout.h"
5#include "sway/tree/view.h"
6#include "stringop.h"
7
8static const char* EXPECTED_SYNTAX =
9 "Expected 'swap container with id|con_id|mark <arg>'";
10
11static bool test_con_id(struct sway_container *container, void *con_id) {
12 return container->id == (size_t)con_id;
13}
14
15static bool test_id(struct sway_container *container, void *id) {
16 xcb_window_t *wid = id;
17 return (container->type == C_VIEW
18 && container->sway_view->type == SWAY_VIEW_XWAYLAND
19 && container->sway_view->wlr_xwayland_surface->window_id == *wid);
20}
21
22static bool test_mark(struct sway_container *container, void *mark) {
23 if (container->type == C_VIEW && container->sway_view->marks->length) {
24 return !list_seq_find(container->sway_view->marks,
25 (int (*)(const void *, const void *))strcmp, mark);
26 }
27 return false;
28}
29
30struct cmd_results *cmd_swap(int argc, char **argv) {
31 struct cmd_results *error = NULL;
32 if ((error = checkarg(argc, "swap", EXPECTED_AT_LEAST, 4))) {
33 return error;
34 }
35
36 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) {
37 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
38 }
39
40 struct sway_container *current = config->handler_context.current_container;
41 struct sway_container *other;
42
43 char *value = join_args(argv + 3, argc - 3);
44 if (strcasecmp(argv[2], "id") == 0) {
45 xcb_window_t id = strtol(value, NULL, 0);
46 other = container_find(&root_container, test_id, (void *)&id);
47 } else if (strcasecmp(argv[2], "con_id") == 0) {
48 size_t con_id = atoi(value);
49 other = container_find(&root_container, test_con_id, (void *)con_id);
50 } else if (strcasecmp(argv[2], "mark") == 0) {
51 other = container_find(&root_container, test_mark, (void *)value);
52 } else {
53 free(value);
54 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
55 }
56
57 if (!other) {
58 error = cmd_results_new(CMD_FAILURE, "swap",
59 "Failed to find %s '%s'", argv[2], value);
60 } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) {
61 error = cmd_results_new(CMD_FAILURE, "swap",
62 "Can only swap with containers and views");
63 } else if (container_has_anscestor(current, other)
64 || container_has_anscestor(other, current)) {
65 error = cmd_results_new(CMD_FAILURE, "swap",
66 "Cannot swap ancestor and descendant");
67 } else if (current->layout == L_FLOATING || other->layout == L_FLOATING) {
68 error = cmd_results_new(CMD_FAILURE, "swap",
69 "Swapping with floating containers is not supported");
70 }
71
72 free(value);
73
74 if (error) {
75 return error;
76 }
77
78 container_swap(current, other);
79 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
80}
diff --git a/sway/meson.build b/sway/meson.build
index 72347d51..9c942e8e 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -63,6 +63,7 @@ sway_sources = files(
63 'commands/show_marks.c', 63 'commands/show_marks.c',
64 'commands/split.c', 64 'commands/split.c',
65 'commands/swaybg_command.c', 65 'commands/swaybg_command.c',
66 'commands/swap.c',
66 'commands/title_format.c', 67 'commands/title_format.c',
67 'commands/unmark.c', 68 'commands/unmark.c',
68 'commands/workspace.c', 69 'commands/workspace.c',
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index ff138562..25a5eef5 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -167,6 +167,15 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
167 "Sticks" a floating window to the current output so that it shows up on all 167 "Sticks" a floating window to the current output so that it shows up on all
168 workspaces. 168 workspaces.
169 169
170*swap* container with id|con\_id|mark <arg>
171 Swaps the position, geometry, and fullscreen status of two containers. The
172 first container can be selected either by criteria or focus. The second
173 container can be selected by _id_, _con\_id_, or _mark_. _id_ can only be
174 used with xwayland views. If the first container has focus, it will retain
175 focus unless it is moved to a different workspace or the second container
176 becomes fullscreen on the same workspace as the first container. In either
177 of those cases, the second container will gain focus.
178
170The following commands may be used either in the configuration file or at 179The following commands may be used either in the configuration file or at
171runtime. 180runtime.
172 181
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 21cec529..624d5516 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -882,3 +882,127 @@ void container_recursive_resize(struct sway_container *container,
882 } 882 }
883 } 883 }
884} 884}
885
886static void swap_places(struct sway_container *con1,
887 struct sway_container *con2) {
888 struct sway_container *temp = malloc(sizeof(struct sway_container));
889 temp->x = con1->x;
890 temp->y = con1->y;
891 temp->width = con1->width;
892 temp->height = con1->height;
893 temp->parent = con1->parent;
894
895 con1->x = con2->x;
896 con1->y = con2->y;
897 con1->width = con2->width;
898 con1->height = con2->height;
899
900 con2->x = temp->x;
901 con2->y = temp->y;
902 con2->width = temp->width;
903 con2->height = temp->height;
904
905 int temp_index = index_child(con1);
906 container_insert_child(con2->parent, con1, index_child(con2));
907 container_insert_child(temp->parent, con2, temp_index);
908
909 free(temp);
910}
911
912static void swap_focus(struct sway_container *con1,
913 struct sway_container *con2, struct sway_seat *seat,
914 struct sway_container *focus) {
915 if (focus == con1 || focus == con2) {
916 struct sway_container *ws1 = container_parent(con1, C_WORKSPACE);
917 struct sway_container *ws2 = container_parent(con2, C_WORKSPACE);
918 if (focus == con1 && (con2->parent->layout == L_TABBED
919 || con2->parent->layout == L_STACKED)) {
920 if (workspace_is_visible(ws2)) {
921 seat_set_focus_warp(seat, con2, false);
922 }
923 seat_set_focus(seat, ws1 != ws2 ? con2 : con1);
924 } else if (focus == con2 && (con1->parent->layout == L_TABBED
925 || con1->parent->layout == L_STACKED)) {
926 if (workspace_is_visible(ws1)) {
927 seat_set_focus_warp(seat, con1, false);
928 }
929 seat_set_focus(seat, ws1 != ws2 ? con1 : con2);
930 } else if (ws1 != ws2) {
931 seat_set_focus(seat, focus == con1 ? con2 : con1);
932 } else {
933 seat_set_focus(seat, focus);
934 }
935 } else {
936 seat_set_focus(seat, focus);
937 }
938}
939
940void container_swap(struct sway_container *con1, struct sway_container *con2) {
941 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
942 return;
943 }
944 if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER,
945 "Can only swap containers and views")) {
946 return;
947 }
948 if (!sway_assert(!container_has_anscestor(con1, con2)
949 && !container_has_anscestor(con2, con1),
950 "Cannot swap anscestor and descendant")) {
951 return;
952 }
953 if (!sway_assert(con1->layout != L_FLOATING && con2->layout != L_FLOATING,
954 "Swapping with floating containers is not supported")) {
955 return;
956 }
957
958 wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id);
959
960 int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen;
961 int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen;
962 if (fs1) {
963 view_set_fullscreen(con1->sway_view, false);
964 }
965 if (fs2) {
966 view_set_fullscreen(con2->sway_view, false);
967 }
968
969 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
970 struct sway_container *focus = seat_get_focus(seat);
971 struct sway_container *vis1 = container_parent(
972 seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)),
973 C_WORKSPACE);
974 struct sway_container *vis2 = container_parent(
975 seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)),
976 C_WORKSPACE);
977
978 char *stored_prev_name = NULL;
979 if (prev_workspace_name) {
980 stored_prev_name = strdup(prev_workspace_name);
981 }
982
983 swap_places(con1, con2);
984
985 if (!workspace_is_visible(vis1)) {
986 seat_set_focus(seat, seat_get_focus_inactive(seat, vis1));
987 }
988 if (!workspace_is_visible(vis2)) {
989 seat_set_focus(seat, seat_get_focus_inactive(seat, vis2));
990 }
991
992 swap_focus(con1, con2, seat, focus);
993
994 if (stored_prev_name) {
995 free(prev_workspace_name);
996 prev_workspace_name = stored_prev_name;
997 }
998
999 arrange_children_of(con1->parent);
1000 arrange_children_of(con2->parent);
1001
1002 if (fs1 && con2->type == C_VIEW) {
1003 view_set_fullscreen(con2->sway_view, true);
1004 }
1005 if (fs2 && con1->type == C_VIEW) {
1006 view_set_fullscreen(con1->sway_view, true);
1007 }
1008}