summaryrefslogtreecommitdiffstats
path: root/sway/commands/move.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands/move.c')
-rw-r--r--sway/commands/move.c328
1 files changed, 170 insertions, 158 deletions
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 8d89f2ef..a5273ba4 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,186 +1,198 @@
1#define _XOPEN_SOURCE 500
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
3#include <wlc/wlc.h> 4#include <wlr/types/wlr_output.h>
5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/util/log.h>
4#include "sway/commands.h" 7#include "sway/commands.h"
5#include "sway/container.h" 8#include "sway/input/seat.h"
6#include "sway/layout.h"
7#include "sway/output.h" 9#include "sway/output.h"
8#include "sway/workspace.h" 10#include "sway/tree/container.h"
9#include "list.h" 11#include "sway/tree/layout.h"
12#include "sway/tree/workspace.h"
10#include "stringop.h" 13#include "stringop.h"
14#include "list.h"
15
16static const char* expected_syntax =
17 "Expected 'move <left|right|up|down> <[px] px>' or "
18 "'move <container|window> to workspace <name>' or "
19 "'move <container|window|workspace> to output <name|direction>' or "
20 "'move position mouse'";
21
22static struct sway_container *output_in_direction(const char *direction,
23 struct wlr_output *reference, int ref_ox, int ref_oy) {
24 int ref_lx = ref_ox + reference->lx,
25 ref_ly = ref_oy + reference->ly;
26 struct {
27 char *name;
28 enum wlr_direction direction;
29 } names[] = {
30 { "up", WLR_DIRECTION_UP },
31 { "down", WLR_DIRECTION_DOWN },
32 { "left", WLR_DIRECTION_LEFT },
33 { "right", WLR_DIRECTION_RIGHT },
34 };
35 for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
36 if (strcasecmp(names[i].name, direction) == 0) {
37 struct wlr_output *adjacent = wlr_output_layout_adjacent_output(
38 root_container.sway_root->output_layout,
39 names[i].direction, reference, ref_lx, ref_ly);
40 if (adjacent) {
41 struct sway_output *sway_output = adjacent->data;
42 return sway_output->swayc;
43 }
44 break;
45 }
46 }
47 return output_by_name(direction);
48}
49
50static struct cmd_results *cmd_move_container(struct sway_container *current,
51 int argc, char **argv) {
52 struct cmd_results *error = NULL;
53 if ((error = checkarg(argc, "move container/window",
54 EXPECTED_AT_LEAST, 4))) {
55 return error;
56 } else if (strcasecmp(argv[1], "to") == 0
57 && strcasecmp(argv[2], "workspace") == 0) {
58 // move container to workspace x
59 if (current->type == C_WORKSPACE) {
60 // TODO: Wrap children in a container and move that
61 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
62 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
63 return cmd_results_new(CMD_FAILURE, "move",
64 "Can only move containers and views.");
65 }
66 struct sway_container *ws;
67 char *ws_name = NULL;
68 if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
69 // move "container to workspace number x"
70 ws_name = strdup(argv[4]);
71 ws = workspace_by_number(ws_name);
72 } else {
73 ws_name = join_args(argv + 3, argc - 3);
74 ws = workspace_by_name(ws_name);
75 }
76
77 if (config->auto_back_and_forth && prev_workspace_name) {
78 // auto back and forth move
79 struct sway_container *curr_ws = container_parent(current, C_WORKSPACE);
80 if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) {
81 // if target workspace is the current one
82 free(ws_name);
83 ws_name = strdup(prev_workspace_name);
84 ws = workspace_by_name(ws_name);
85 }
86 }
87
88 if (!ws) {
89 ws = workspace_create(NULL, ws_name);
90 }
91 free(ws_name);
92 struct sway_container *old_parent = current->parent;
93 struct sway_container *focus = seat_get_focus_inactive(
94 config->handler_context.seat, ws);
95 container_move_to(current, focus);
96 seat_set_focus(config->handler_context.seat, old_parent);
97 container_reap_empty(old_parent);
98 container_reap_empty(focus->parent);
99 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
100 } else if (strcasecmp(argv[1], "to") == 0
101 && strcasecmp(argv[2], "output") == 0) {
102 if (current->type == C_WORKSPACE) {
103 // TODO: Wrap children in a container and move that
104 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
105 } else if (current->type != C_CONTAINER
106 && current->type != C_VIEW) {
107 return cmd_results_new(CMD_FAILURE, "move",
108 "Can only move containers and views.");
109 }
110 struct sway_container *source = container_parent(current, C_OUTPUT);
111 struct sway_container *destination = output_in_direction(argv[3],
112 source->sway_output->wlr_output, current->x, current->y);
113 if (!destination) {
114 return cmd_results_new(CMD_FAILURE, "move workspace",
115 "Can't find output with name/direction '%s'", argv[3]);
116 }
117 struct sway_container *focus = seat_get_focus_inactive(
118 config->handler_context.seat, destination);
119 if (!focus) {
120 // We've never been to this output before
121 focus = destination->children->items[0];
122 }
123 struct sway_container *old_parent = current->parent;
124 container_move_to(current, focus);
125 seat_set_focus(config->handler_context.seat, old_parent);
126 container_reap_empty(old_parent);
127 container_reap_empty(focus->parent);
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 }
130 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
131}
132
133static struct cmd_results *cmd_move_workspace(struct sway_container *current,
134 int argc, char **argv) {
135 struct cmd_results *error = NULL;
136 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
137 return error;
138 } else if (strcasecmp(argv[1], "to") != 0
139 || strcasecmp(argv[2], "output") != 0) {
140 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
141 }
142 struct sway_container *source = container_parent(current, C_OUTPUT);
143 int center_x = current->width / 2 + current->x,
144 center_y = current->height / 2 + current->y;
145 struct sway_container *destination = output_in_direction(argv[3],
146 source->sway_output->wlr_output, center_x, center_y);
147 if (!destination) {
148 return cmd_results_new(CMD_FAILURE, "move workspace",
149 "Can't find output with name/direction '%s'", argv[3]);
150 }
151 if (current->type != C_WORKSPACE) {
152 current = container_parent(current, C_WORKSPACE);
153 }
154 container_move_to(current, destination);
155 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
156}
11 157
12struct cmd_results *cmd_move(int argc, char **argv) { 158struct cmd_results *cmd_move(int argc, char **argv) {
13 struct cmd_results *error = NULL; 159 struct cmd_results *error = NULL;
14 int move_amt = 10; 160 int move_amt = 10;
15
16 if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
17 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 161 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
18 return error; 162 return error;
19 } 163 }
20 const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first> <[px] px>' or " 164 struct sway_container *current = config->handler_context.current_container;
21 "'move <container|window> to workspace <name>' or "
22 "'move <container|window|workspace> to output <name|direction>' or "
23 "'move position mouse'";
24 swayc_t *view = current_container;
25 165
26 if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) { 166 if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) {
27 char *inv; 167 char *inv;
28 move_amt = (int)strtol(argv[1], &inv, 10); 168 move_amt = (int)strtol(argv[1], &inv, 10);
29 if (*inv != '\0' && strcasecmp(inv, "px") != 0) { 169 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
30 move_amt = 10; 170 return cmd_results_new(CMD_FAILURE, "move",
171 "Invalid distance specified");
31 } 172 }
32 } 173 }
33 174
34 if (strcasecmp(argv[0], "left") == 0) { 175 if (strcasecmp(argv[0], "left") == 0) {
35 move_container(view, MOVE_LEFT, move_amt); 176 container_move(current, MOVE_LEFT, move_amt);
36 } else if (strcasecmp(argv[0], "right") == 0) { 177 } else if (strcasecmp(argv[0], "right") == 0) {
37 move_container(view, MOVE_RIGHT, move_amt); 178 container_move(current, MOVE_RIGHT, move_amt);
38 } else if (strcasecmp(argv[0], "up") == 0) { 179 } else if (strcasecmp(argv[0], "up") == 0) {
39 move_container(view, MOVE_UP, move_amt); 180 container_move(current, MOVE_UP, move_amt);
40 } else if (strcasecmp(argv[0], "down") == 0) { 181 } else if (strcasecmp(argv[0], "down") == 0) {
41 move_container(view, MOVE_DOWN, move_amt); 182 container_move(current, MOVE_DOWN, move_amt);
42 } else if (strcasecmp(argv[0], "next") == 0) { 183 } else if (strcasecmp(argv[0], "container") == 0
43 move_container(view, MOVE_NEXT, move_amt); 184 || strcasecmp(argv[0], "window") == 0) {
44 } else if (strcasecmp(argv[0], "prev") == 0) { 185 return cmd_move_container(current, argc, argv);
45 move_container(view, MOVE_PREV, move_amt);
46 } else if (strcasecmp(argv[0], "first") == 0) {
47 move_container(view, MOVE_FIRST, move_amt);
48 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
49 // "move container ...
50 if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
51 return error;
52 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "workspace") == 0) {
53 // move container to workspace x
54 if (view->type == C_WORKSPACE) {
55 if (!view->children || view->children->length == 0) {
56 return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
57 }
58 view = new_container(view, view->workspace_layout);
59 } if (view->type != C_CONTAINER && view->type != C_VIEW) {
60 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
61 }
62
63 swayc_t *ws;
64 const char *num_name = NULL;
65 char *ws_name = NULL;
66 if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
67 // move "container to workspace number x"
68 num_name = argv[4];
69 ws = workspace_by_number(num_name);
70 } else {
71 ws_name = join_args(argv + 3, argc - 3);
72 ws = workspace_by_name(ws_name);
73 }
74
75 if (ws == NULL) {
76 ws = workspace_create(ws_name ? ws_name : num_name);
77 }
78 if (ws_name) {
79 free(ws_name);
80 }
81 move_container_to(view, get_focused_container(ws));
82 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
83 // move container to output x
84 swayc_t *output = NULL;
85 struct wlc_point abs_pos;
86 get_absolute_center_position(view, &abs_pos);
87 if (view->type == C_WORKSPACE) {
88 if (!view->children || view->children->length == 0) {
89 return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
90 }
91 view = new_container(view, view->workspace_layout);
92 } else if (view->type != C_CONTAINER && view->type != C_VIEW) {
93 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
94 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
95 return cmd_results_new(CMD_FAILURE, "move",
96 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
97 }
98
99 swayc_t *container = get_focused_container(output);
100 if (container->is_floating) {
101 move_container_to(view, container->parent);
102 } else {
103 move_container_to(view, container);
104 }
105 } else {
106 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
107 }
108 } else if (strcasecmp(argv[0], "workspace") == 0) { 186 } else if (strcasecmp(argv[0], "workspace") == 0) {
109 // move workspace (to output x) 187 return cmd_move_workspace(current, argc, argv);
110 swayc_t *output = NULL; 188 } else if (strcasecmp(argv[0], "scratchpad") == 0
111 struct wlc_point abs_pos; 189 || (strcasecmp(argv[0], "to") == 0
112 get_absolute_center_position(view, &abs_pos); 190 && strcasecmp(argv[1], "scratchpad") == 0)) {
113 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { 191 // TODO: scratchpad
114 return error; 192 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
115 } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) {
116 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
117 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
118 return cmd_results_new(CMD_FAILURE, "move workspace",
119 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
120 }
121 if (view->type == C_WORKSPACE) {
122 // This probably means we're moving an empty workspace, but
123 // that's fine.
124 move_workspace_to(view, output);
125 } else {
126 swayc_t *workspace = swayc_parent_by_type(view, C_WORKSPACE);
127 move_workspace_to(workspace, output);
128 }
129 } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && strcasecmp(argv[1], "scratchpad") == 0)) {
130 // move scratchpad ...
131 if (view->type != C_CONTAINER && view->type != C_VIEW) {
132 return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
133 }
134 swayc_t *view = current_container;
135 int i;
136 for (i = 0; i < scratchpad->length; i++) {
137 if (scratchpad->items[i] == view) {
138 hide_view_in_scratchpad(view);
139 sp_view = NULL;
140 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
141 }
142 }
143 list_add(scratchpad, view);
144 if (!view->is_floating) {
145 destroy_container(remove_child(view));
146 } else {
147 remove_child(view);
148 }
149 wlc_view_set_mask(view->handle, 0);
150 arrange_windows(swayc_active_workspace(), -1, -1);
151 swayc_t *focused = container_under_pointer();
152 if (focused == NULL) {
153 focused = swayc_active_workspace();
154 }
155 set_focused_container(focused);
156 } else if (strcasecmp(argv[0], "position") == 0) { 193 } else if (strcasecmp(argv[0], "position") == 0) {
157 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 2))) { 194 // TODO: floating
158 return error; 195 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
159 }
160 if (strcasecmp(argv[1], "mouse")) {
161 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
162 }
163
164 if (view->is_floating) {
165 swayc_t *output = swayc_parent_by_type(view, C_OUTPUT);
166 struct wlc_geometry g;
167 wlc_view_get_visible_geometry(view->handle, &g);
168 const struct wlc_size *size = wlc_output_get_resolution(output->handle);
169
170 double x_pos, y_pos;
171 wlc_pointer_get_position_v2(&x_pos, &y_pos);
172
173 int32_t x = x_pos - g.size.w / 2;
174 int32_t y = y_pos - g.size.h / 2;
175
176 uint32_t w = size->w - g.size.w;
177 uint32_t h = size->h - g.size.h;
178
179 view->x = g.origin.x = MIN((int32_t)w, MAX(x, 0));
180 view->y = g.origin.y = MIN((int32_t)h, MAX(y, 0));
181
182 wlc_view_set_geometry(view->handle, 0, &g);
183 }
184 } else { 196 } else {
185 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 197 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
186 } 198 }