diff options
Diffstat (limited to 'sway/commands/move.c')
-rw-r--r-- | sway/commands/move.c | 262 |
1 files changed, 165 insertions, 97 deletions
diff --git a/sway/commands/move.c b/sway/commands/move.c index 841da4c4..af3dc538 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <stdbool.h> | ||
2 | #include <string.h> | 3 | #include <string.h> |
3 | #include <strings.h> | 4 | #include <strings.h> |
4 | #include <wlr/types/wlr_cursor.h> | 5 | #include <wlr/types/wlr_cursor.h> |
@@ -16,11 +17,11 @@ | |||
16 | #include "stringop.h" | 17 | #include "stringop.h" |
17 | #include "list.h" | 18 | #include "list.h" |
18 | 19 | ||
19 | static const char* expected_syntax = | 20 | static const char *expected_syntax = |
20 | "Expected 'move <left|right|up|down> <[px] px>' or " | 21 | "Expected 'move <left|right|up|down> <[px] px>' or " |
21 | "'move <container|window> to workspace <name>' or " | 22 | "'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or " |
22 | "'move <container|window|workspace> to output <name|direction>' or " | 23 | "'move [--no-auto-back-and-forth] <container|window|workspace> [to] output <name|direction>' or " |
23 | "'move position mouse'"; | 24 | "'move <container|window> [to] mark <mark>'"; |
24 | 25 | ||
25 | static struct sway_container *output_in_direction(const char *direction, | 26 | static struct sway_container *output_in_direction(const char *direction, |
26 | struct wlr_output *reference, int ref_lx, int ref_ly) { | 27 | struct wlr_output *reference, int ref_lx, int ref_ly) { |
@@ -52,123 +53,168 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, | |||
52 | int argc, char **argv) { | 53 | int argc, char **argv) { |
53 | struct cmd_results *error = NULL; | 54 | struct cmd_results *error = NULL; |
54 | if ((error = checkarg(argc, "move container/window", | 55 | if ((error = checkarg(argc, "move container/window", |
55 | EXPECTED_AT_LEAST, 4))) { | 56 | EXPECTED_AT_LEAST, 3))) { |
56 | return error; | 57 | return error; |
57 | } else if (strcasecmp(argv[1], "to") == 0 | 58 | } |
58 | && strcasecmp(argv[2], "workspace") == 0) { | 59 | |
59 | // move container to workspace x | 60 | if (current->type == C_WORKSPACE) { |
60 | if (current->type == C_WORKSPACE) { | 61 | if (current->children->length == 0) { |
61 | if (current->children->length == 0) { | ||
62 | return cmd_results_new(CMD_FAILURE, "move", | ||
63 | "Can't move an empty workspace"); | ||
64 | } | ||
65 | current = container_wrap_children(current); | ||
66 | } else if (current->type != C_CONTAINER && current->type != C_VIEW) { | ||
67 | return cmd_results_new(CMD_FAILURE, "move", | 62 | return cmd_results_new(CMD_FAILURE, "move", |
68 | "Can only move containers and views."); | 63 | "Can't move an empty workspace"); |
69 | } | 64 | } |
70 | struct sway_container *ws; | 65 | current = container_wrap_children(current); |
71 | char *ws_name = NULL; | 66 | } else if (current->type != C_CONTAINER && current->type != C_VIEW) { |
72 | if (argc == 5 && strcasecmp(argv[3], "number") == 0) { | 67 | return cmd_results_new(CMD_FAILURE, "move", |
73 | // move "container to workspace number x" | 68 | "Can only move containers and views."); |
74 | ws_name = strdup(argv[4]); | 69 | } |
75 | ws = workspace_by_number(ws_name); | 70 | |
76 | } else { | 71 | bool no_auto_back_and_forth = false; |
77 | ws_name = join_args(argv + 3, argc - 3); | 72 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { |
78 | ws = workspace_by_name(ws_name); | 73 | no_auto_back_and_forth = true; |
74 | if (--argc < 3) { | ||
75 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
76 | } | ||
77 | ++argv; | ||
78 | } | ||
79 | while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) { | ||
80 | no_auto_back_and_forth = true; | ||
81 | if (--argc < 3) { | ||
82 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
79 | } | 83 | } |
84 | argv++; | ||
85 | } | ||
86 | |||
87 | while (strcasecmp(argv[1], "to") == 0) { | ||
88 | if (--argc < 3) { | ||
89 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
90 | } | ||
91 | argv++; | ||
92 | } | ||
80 | 93 | ||
81 | if (config->auto_back_and_forth && prev_workspace_name) { | 94 | struct sway_container *old_parent = current->parent; |
82 | // auto back and forth move | 95 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); |
83 | struct sway_container *curr_ws = container_parent(current, C_WORKSPACE); | 96 | struct sway_container *destination = NULL; |
84 | if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) { | 97 | |
85 | // if target workspace is the current one | 98 | // determine destination |
86 | free(ws_name); | 99 | if (strcasecmp(argv[1], "workspace") == 0) { |
87 | ws_name = strdup(prev_workspace_name); | 100 | // move container to workspace x |
101 | struct sway_container *ws; | ||
102 | if (strcasecmp(argv[2], "next") == 0 || | ||
103 | strcasecmp(argv[2], "prev") == 0 || | ||
104 | strcasecmp(argv[2], "next_on_output") == 0 || | ||
105 | strcasecmp(argv[2], "prev_on_output") == 0 || | ||
106 | strcasecmp(argv[2], "back_and_forth") == 0 || | ||
107 | strcasecmp(argv[2], "current") == 0) { | ||
108 | ws = workspace_by_name(argv[2]); | ||
109 | } else if (strcasecmp(argv[2], "back_and_forth") == 0) { | ||
110 | if (!(ws = workspace_by_name(argv[2]))) { | ||
111 | if (prev_workspace_name) { | ||
112 | ws = workspace_create(NULL, prev_workspace_name); | ||
113 | } else { | ||
114 | return cmd_results_new(CMD_FAILURE, "move", | ||
115 | "No workspace was previously active."); | ||
116 | } | ||
117 | } | ||
118 | } else { | ||
119 | char *ws_name = NULL; | ||
120 | if (strcasecmp(argv[2], "number") == 0) { | ||
121 | // move "container to workspace number x" | ||
122 | if (argc < 4) { | ||
123 | return cmd_results_new(CMD_INVALID, "move", | ||
124 | expected_syntax); | ||
125 | } | ||
126 | ws_name = strdup(argv[3]); | ||
127 | ws = workspace_by_number(ws_name); | ||
128 | } else { | ||
129 | ws_name = join_args(argv + 2, argc - 2); | ||
88 | ws = workspace_by_name(ws_name); | 130 | ws = workspace_by_name(ws_name); |
89 | } | 131 | } |
90 | } | ||
91 | 132 | ||
92 | if (!ws) { | 133 | if (!no_auto_back_and_forth && config->auto_back_and_forth && |
93 | ws = workspace_create(NULL, ws_name); | 134 | prev_workspace_name) { |
94 | } | 135 | // auto back and forth move |
95 | free(ws_name); | 136 | if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) { |
96 | struct sway_container *old_parent = current->parent; | 137 | // if target workspace is the current one |
97 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); | 138 | free(ws_name); |
98 | struct sway_container *destination = seat_get_focus_inactive( | 139 | ws_name = strdup(prev_workspace_name); |
99 | config->handler_context.seat, ws); | 140 | ws = workspace_by_name(ws_name); |
100 | container_move_to(current, destination); | 141 | } |
101 | struct sway_container *focus = seat_get_focus_inactive( | 142 | } |
102 | config->handler_context.seat, old_parent); | ||
103 | seat_set_focus_warp(config->handler_context.seat, focus, true, false); | ||
104 | container_reap_empty(old_parent); | ||
105 | container_reap_empty(destination->parent); | ||
106 | |||
107 | // TODO: Ideally we would arrange the surviving parent after reaping, | ||
108 | // but container_reap_empty does not return it, so we arrange the | ||
109 | // workspace instead. | ||
110 | arrange_windows(old_ws); | ||
111 | arrange_windows(destination->parent); | ||
112 | 143 | ||
113 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 144 | if (!ws) { |
114 | } else if (strcasecmp(argv[1], "to") == 0 | 145 | ws = workspace_create(NULL, ws_name); |
115 | && strcasecmp(argv[2], "output") == 0) { | 146 | } |
116 | if (current->type == C_WORKSPACE) { | 147 | free(ws_name); |
117 | // TODO: Wrap children in a container and move that | ||
118 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | ||
119 | } else if (current->type != C_CONTAINER | ||
120 | && current->type != C_VIEW) { | ||
121 | return cmd_results_new(CMD_FAILURE, "move", | ||
122 | "Can only move containers and views."); | ||
123 | } | 148 | } |
149 | destination = seat_get_focus_inactive(config->handler_context.seat, ws); | ||
150 | } else if (strcasecmp(argv[1], "output") == 0) { | ||
124 | struct sway_container *source = container_parent(current, C_OUTPUT); | 151 | struct sway_container *source = container_parent(current, C_OUTPUT); |
125 | struct sway_container *destination = output_in_direction(argv[3], | 152 | struct sway_container *dest_output = output_in_direction(argv[2], |
126 | source->sway_output->wlr_output, current->x, current->y); | 153 | source->sway_output->wlr_output, current->x, current->y); |
127 | if (!destination) { | 154 | if (!dest_output) { |
128 | return cmd_results_new(CMD_FAILURE, "move workspace", | 155 | return cmd_results_new(CMD_FAILURE, "move workspace", |
129 | "Can't find output with name/direction '%s'", argv[3]); | 156 | "Can't find output with name/direction '%s'", argv[2]); |
130 | } | 157 | } |
131 | struct sway_container *focus = seat_get_focus_inactive( | 158 | destination = seat_get_focus_inactive( |
132 | config->handler_context.seat, destination); | 159 | config->handler_context.seat, dest_output); |
133 | if (!focus) { | 160 | if (!destination) { |
134 | // We've never been to this output before | 161 | // We've never been to this output before |
135 | focus = destination->children->items[0]; | 162 | destination = dest_output->children->items[0]; |
136 | } | 163 | } |
137 | struct sway_container *old_parent = current->parent; | 164 | } else if (strcasecmp(argv[1], "mark") == 0) { |
138 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); | 165 | struct sway_view *dest_view = view_find_mark(argv[2]); |
139 | container_move_to(current, focus); | 166 | if (dest_view == NULL) { |
140 | seat_set_focus_warp(config->handler_context.seat, old_parent, true, false); | 167 | return cmd_results_new(CMD_FAILURE, "move", |
141 | container_reap_empty(old_parent); | 168 | "Mark '%s' not found", argv[2]); |
142 | container_reap_empty(focus->parent); | 169 | } |
143 | 170 | destination = dest_view->swayc; | |
144 | // TODO: Ideally we would arrange the surviving parent after reaping, | 171 | } else { |
145 | // but container_reap_empty does not return it, so we arrange the | 172 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); |
146 | // workspace instead. | ||
147 | arrange_windows(old_ws); | ||
148 | arrange_windows(focus->parent); | ||
149 | |||
150 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
151 | } | 173 | } |
152 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | 174 | |
175 | // move container, arrange windows and return focus | ||
176 | container_move_to(current, destination); | ||
177 | struct sway_container *focus = | ||
178 | seat_get_focus_inactive(config->handler_context.seat, old_parent); | ||
179 | seat_set_focus_warp(config->handler_context.seat, focus, true, false); | ||
180 | container_reap_empty(old_parent); | ||
181 | container_reap_empty(destination->parent); | ||
182 | |||
183 | // TODO: Ideally we would arrange the surviving parent after reaping, | ||
184 | // but container_reap_empty does not return it, so we arrange the | ||
185 | // workspace instead. | ||
186 | arrange_windows(old_ws); | ||
187 | arrange_windows(destination->parent); | ||
188 | |||
189 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
153 | } | 190 | } |
154 | 191 | ||
155 | static struct cmd_results *cmd_move_workspace(struct sway_container *current, | 192 | static struct cmd_results *cmd_move_workspace(struct sway_container *current, |
156 | int argc, char **argv) { | 193 | int argc, char **argv) { |
157 | struct cmd_results *error = NULL; | 194 | struct cmd_results *error = NULL; |
158 | if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { | 195 | if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { |
159 | return error; | 196 | return error; |
160 | } else if (strcasecmp(argv[1], "to") != 0 | 197 | } |
161 | || strcasecmp(argv[2], "output") != 0) { | 198 | |
199 | while (strcasecmp(argv[1], "to") == 0) { | ||
200 | if (--argc < 3) { | ||
201 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | ||
202 | } | ||
203 | ++argv; | ||
204 | } | ||
205 | |||
206 | if (strcasecmp(argv[1], "output") != 0) { | ||
162 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | 207 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); |
163 | } | 208 | } |
209 | |||
164 | struct sway_container *source = container_parent(current, C_OUTPUT); | 210 | struct sway_container *source = container_parent(current, C_OUTPUT); |
165 | int center_x = current->width / 2 + current->x, | 211 | int center_x = current->width / 2 + current->x, |
166 | center_y = current->height / 2 + current->y; | 212 | center_y = current->height / 2 + current->y; |
167 | struct sway_container *destination = output_in_direction(argv[3], | 213 | struct sway_container *destination = output_in_direction(argv[2], |
168 | source->sway_output->wlr_output, center_x, center_y); | 214 | source->sway_output->wlr_output, center_x, center_y); |
169 | if (!destination) { | 215 | if (!destination) { |
170 | return cmd_results_new(CMD_FAILURE, "move workspace", | 216 | return cmd_results_new(CMD_FAILURE, "move workspace", |
171 | "Can't find output with name/direction '%s'", argv[3]); | 217 | "Can't find output with name/direction '%s'", argv[2]); |
172 | } | 218 | } |
173 | if (current->type != C_WORKSPACE) { | 219 | if (current->type != C_WORKSPACE) { |
174 | current = container_parent(current, C_WORKSPACE); | 220 | current = container_parent(current, C_WORKSPACE); |
@@ -242,9 +288,9 @@ static struct cmd_results *move_in_direction(struct sway_container *container, | |||
242 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 288 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
243 | } | 289 | } |
244 | 290 | ||
245 | static const char* expected_position_syntax = | 291 | static const char *expected_position_syntax = |
246 | "Expected 'move [absolute] position <x> <y>' or " | 292 | "Expected 'move [absolute] position <x> [px] <y> [px]' or " |
247 | "'move [absolute] position mouse'"; | 293 | "'move [absolute] position center|mouse'"; |
248 | 294 | ||
249 | static struct cmd_results *move_to_position(struct sway_container *container, | 295 | static struct cmd_results *move_to_position(struct sway_container *container, |
250 | int argc, char **argv) { | 296 | int argc, char **argv) { |
@@ -279,10 +325,18 @@ static struct cmd_results *move_to_position(struct sway_container *container, | |||
279 | double ly = seat->cursor->cursor->y - container->height / 2; | 325 | double ly = seat->cursor->cursor->y - container->height / 2; |
280 | container_floating_move_to(container, lx, ly); | 326 | container_floating_move_to(container, lx, ly); |
281 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 327 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
328 | } else if (strcmp(argv[0], "center") == 0) { | ||
329 | struct sway_container *ws = container_parent(container, C_WORKSPACE); | ||
330 | double lx = ws->x + (ws->width - container->width) / 2; | ||
331 | double ly = ws->y + (ws->height - container->height) / 2; | ||
332 | container_floating_move_to(container, lx, ly); | ||
333 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
282 | } | 334 | } |
283 | if (argc != 2) { | 335 | |
336 | if (argc < 2) { | ||
284 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | 337 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); |
285 | } | 338 | } |
339 | |||
286 | double lx, ly; | 340 | double lx, ly; |
287 | char *inv; | 341 | char *inv; |
288 | lx = (double)strtol(argv[0], &inv, 10); | 342 | lx = (double)strtol(argv[0], &inv, 10); |
@@ -290,11 +344,22 @@ static struct cmd_results *move_to_position(struct sway_container *container, | |||
290 | return cmd_results_new(CMD_FAILURE, "move", | 344 | return cmd_results_new(CMD_FAILURE, "move", |
291 | "Invalid position specified"); | 345 | "Invalid position specified"); |
292 | } | 346 | } |
347 | if (strcmp(argv[1], "px") == 0) { | ||
348 | --argc; | ||
349 | ++argv; | ||
350 | } | ||
351 | |||
352 | if (argc > 3) { | ||
353 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | ||
354 | } | ||
355 | |||
293 | ly = (double)strtol(argv[1], &inv, 10); | 356 | ly = (double)strtol(argv[1], &inv, 10); |
294 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { | 357 | if ((*inv != '\0' && strcasecmp(inv, "px") != 0) || |
358 | (argc == 3 && strcmp(argv[2], "px") != 0)) { | ||
295 | return cmd_results_new(CMD_FAILURE, "move", | 359 | return cmd_results_new(CMD_FAILURE, "move", |
296 | "Invalid position specified"); | 360 | "Invalid position specified"); |
297 | } | 361 | } |
362 | |||
298 | container_floating_move_to(container, lx, ly); | 363 | container_floating_move_to(container, lx, ly); |
299 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 364 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
300 | } | 365 | } |
@@ -342,8 +407,11 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
342 | return move_in_direction(current, MOVE_UP, argc, argv); | 407 | return move_in_direction(current, MOVE_UP, argc, argv); |
343 | } else if (strcasecmp(argv[0], "down") == 0) { | 408 | } else if (strcasecmp(argv[0], "down") == 0) { |
344 | return move_in_direction(current, MOVE_DOWN, argc, argv); | 409 | return move_in_direction(current, MOVE_DOWN, argc, argv); |
345 | } else if (strcasecmp(argv[0], "container") == 0 | 410 | } else if ((strcasecmp(argv[0], "container") == 0 |
346 | || strcasecmp(argv[0], "window") == 0) { | 411 | || strcasecmp(argv[0], "window") == 0) || |
412 | (strcasecmp(argv[0], "--no-auto-back-and-forth") && | ||
413 | (strcasecmp(argv[0], "container") == 0 | ||
414 | || strcasecmp(argv[0], "window") == 0))) { | ||
347 | return cmd_move_container(current, argc, argv); | 415 | return cmd_move_container(current, argc, argv); |
348 | } else if (strcasecmp(argv[0], "workspace") == 0) { | 416 | } else if (strcasecmp(argv[0], "workspace") == 0) { |
349 | return cmd_move_workspace(current, argc, argv); | 417 | return cmd_move_workspace(current, argc, argv); |