aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands/move.c
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-30 21:00:10 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-09-05 18:01:43 +1000
commit7586f150c058997d9dde387ea7c091ffa7a3c3c7 (patch)
tree63d19027974c1db62ce3a74ca1d2314eb6d5049b /sway/commands/move.c
parentMerge pull request #2569 from RyanDwyer/deny-reload-repeat (diff)
downloadsway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.gz
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.zst
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.zip
Implement type safe arguments and demote sway_container
This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
Diffstat (limited to 'sway/commands/move.c')
-rw-r--r--sway/commands/move.c873
1 files changed, 407 insertions, 466 deletions
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 4426f24e..1b2e830c 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -40,7 +40,7 @@ enum wlr_direction opposite_direction(enum wlr_direction d) {
40 } 40 }
41} 41}
42 42
43static struct sway_container *output_in_direction(const char *direction_string, 43static struct sway_output *output_in_direction(const char *direction_string,
44 struct wlr_output *reference, int ref_lx, int ref_ly) { 44 struct wlr_output *reference, int ref_lx, int ref_ly) {
45 struct { 45 struct {
46 char *name; 46 char *name;
@@ -63,447 +63,367 @@ static struct sway_container *output_in_direction(const char *direction_string,
63 63
64 if (direction) { 64 if (direction) {
65 struct wlr_output *target = wlr_output_layout_adjacent_output( 65 struct wlr_output *target = wlr_output_layout_adjacent_output(
66 root_container.sway_root->output_layout, 66 root->output_layout, direction, reference, ref_lx, ref_ly);
67 direction, reference, ref_lx, ref_ly);
68 67
69 if (!target) { 68 if (!target) {
70 target = wlr_output_layout_farthest_output( 69 target = wlr_output_layout_farthest_output(
71 root_container.sway_root->output_layout, 70 root->output_layout, opposite_direction(direction),
72 opposite_direction(direction), reference, ref_lx, ref_ly); 71 reference, ref_lx, ref_ly);
73 } 72 }
74 73
75 if (target) { 74 if (target) {
76 struct sway_output *sway_output = target->data; 75 return target->data;
77 return sway_output->swayc;
78 } 76 }
79 } 77 }
80 78
81 return output_by_name(direction_string); 79 return output_by_name(direction_string);
82} 80}
83 81
84static void container_move_to(struct sway_container *container, 82static bool is_parallel(enum sway_container_layout layout,
85 struct sway_container *destination) { 83 enum movement_direction dir) {
86 if (!sway_assert(container->type == C_CONTAINER || 84 switch (layout) {
87 container->type == C_VIEW, "Expected a container or view")) { 85 case L_TABBED:
88 return; 86 case L_HORIZ:
87 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
88 case L_STACKED:
89 case L_VERT:
90 return dir == MOVE_UP || dir == MOVE_DOWN;
91 default:
92 return false;
89 } 93 }
90 if (container == destination 94}
91 || container_has_ancestor(container, destination)) { 95
96/**
97 * Ensures all seats focus the fullscreen container if needed.
98 */
99static void workspace_focus_fullscreen(struct sway_workspace *workspace) {
100 if (!workspace->fullscreen) {
92 return; 101 return;
93 } 102 }
94 struct sway_container *old_parent = NULL; 103 struct sway_seat *seat;
95 struct sway_container *new_parent = NULL; 104 struct sway_workspace *focus_ws;
96 if (container_is_floating(container)) { 105 wl_list_for_each(seat, &input_manager->seats, link) {
97 // Resolve destination into a workspace 106 focus_ws = seat_get_focused_workspace(seat);
98 struct sway_container *new_ws = NULL; 107 if (focus_ws == workspace) {
99 if (destination->type == C_OUTPUT) { 108 struct sway_node *new_focus =
100 new_ws = output_get_active_workspace(destination->sway_output); 109 seat_get_focus_inactive(seat, &workspace->fullscreen->node);
101 } else if (destination->type == C_WORKSPACE) { 110 seat_set_focus(seat, new_focus);
102 new_ws = destination;
103 } else {
104 new_ws = container_parent(destination, C_WORKSPACE);
105 } 111 }
106 if (!new_ws) { 112 }
107 // This can happen if the user has run "move container to mark foo", 113}
108 // where mark foo is on a hidden scratchpad container. 114
109 return; 115static void container_move_to_container_from_direction(
116 struct sway_container *container, struct sway_container *destination,
117 enum movement_direction move_dir) {
118 if (destination->view) {
119 if (destination->parent == container->parent) {
120 wlr_log(WLR_DEBUG, "Swapping siblings");
121 list_t *siblings = container_get_siblings(container);
122 int container_index = list_find(siblings, container);
123 int destination_index = list_find(siblings, destination);
124 list_swap(siblings, container_index, destination_index);
125 } else {
126 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
127 int offset = move_dir == MOVE_LEFT || move_dir == MOVE_UP;
128 int index = container_sibling_index(destination) + offset;
129 if (destination->parent) {
130 container_insert_child(destination->parent, container, index);
131 } else {
132 workspace_insert_tiling(destination->workspace,
133 container, index);
134 }
135 container->width = container->height = 0;
110 } 136 }
111 struct sway_container *old_output = 137 return;
112 container_parent(container, C_OUTPUT); 138 }
113 old_parent = container_remove_child(container); 139
114 workspace_add_floating(new_ws, container); 140 if (is_parallel(destination->layout, move_dir)) {
115 container_handle_fullscreen_reparent(container, old_parent); 141 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
142 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
143 0 : destination->children->length;
144 container_insert_child(destination, container, index);
145 container->width = container->height = 0;
146 return;
147 }
148
149 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
150 struct sway_node *focus_inactive = seat_get_active_child(
151 config->handler_context.seat, &destination->node);
152 if (!focus_inactive || focus_inactive == &destination->node) {
153 // The container has no children
154 container_add_child(destination, container);
155 return;
156 }
157
158 // Try again but with the child
159 container_move_to_container_from_direction(container,
160 focus_inactive->sway_container, move_dir);
161}
162
163static void container_move_to_workspace_from_direction(
164 struct sway_container *container, struct sway_workspace *workspace,
165 enum movement_direction move_dir) {
166 if (is_parallel(workspace->layout, move_dir)) {
167 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
168 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
169 0 : workspace->tiling->length;
170 workspace_insert_tiling(workspace, container, index);
171 return;
172 }
173
174 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
175 struct sway_container *focus_inactive = seat_get_focus_inactive_tiling(
176 config->handler_context.seat, workspace);
177 if (!focus_inactive) {
178 // The workspace has no tiling children
179 workspace_add_tiling(workspace, container);
180 return;
181 }
182 while (focus_inactive->parent) {
183 focus_inactive = focus_inactive->parent;
184 }
185 container_move_to_container_from_direction(container, focus_inactive,
186 move_dir);
187}
188
189static void container_move_to_workspace(struct sway_container *container,
190 struct sway_workspace *workspace) {
191 if (container->workspace == workspace) {
192 return;
193 }
194 struct sway_workspace *old_workspace = container->workspace;
195 if (container_is_floating(container)) {
196 struct sway_output *old_output = container->workspace->output;
197 container_detach(container);
198 workspace_add_floating(workspace, container);
199 container_handle_fullscreen_reparent(container);
116 // If changing output, center it within the workspace 200 // If changing output, center it within the workspace
117 if (old_output != new_ws->parent && !container->is_fullscreen) { 201 if (old_output != workspace->output && !container->is_fullscreen) {
118 container_floating_move_to_center(container); 202 container_floating_move_to_center(container);
119 } 203 }
120 } else { 204 } else {
121 old_parent = container_remove_child(container); 205 container_detach(container);
122 container->width = container->height = 0; 206 container->width = container->height = 0;
123 container->saved_width = container->saved_height = 0; 207 container->saved_width = container->saved_height = 0;
124 208 workspace_add_tiling(workspace, container);
125 if (destination->type == C_VIEW) { 209 container_update_representation(container);
126 new_parent = container_add_sibling(destination, container);
127 } else {
128 new_parent = destination;
129 container_add_child(destination, container);
130 }
131 } 210 }
132 211 if (container->view) {
133 if (container->type == C_VIEW) {
134 ipc_event_window(container, "move"); 212 ipc_event_window(container, "move");
135 } 213 }
136 container_notify_subtree_changed(old_parent); 214 workspace_detect_urgent(old_workspace);
137 container_notify_subtree_changed(new_parent); 215 workspace_detect_urgent(workspace);
138 216 workspace_focus_fullscreen(workspace);
139 // If view was moved to a fullscreen workspace, refocus the fullscreen view 217}
140 struct sway_container *new_workspace = container; 218
141 if (new_workspace->type != C_WORKSPACE) { 219static void container_move_to_container(struct sway_container *container,
142 new_workspace = container_parent(new_workspace, C_WORKSPACE); 220 struct sway_container *destination) {
143 } 221 if (container == destination
144 if (new_workspace->sway_workspace->fullscreen) { 222 || container_has_ancestor(container, destination)
145 struct sway_seat *seat; 223 || container_has_ancestor(destination, container)) {
146 struct sway_container *focus, *focus_ws; 224 return;
147 wl_list_for_each(seat, &input_manager->seats, link) {
148 focus = seat_get_focus(seat);
149 focus_ws = focus;
150 if (focus_ws->type != C_WORKSPACE) {
151 focus_ws = container_parent(focus_ws, C_WORKSPACE);
152 }
153 if (focus_ws == new_workspace) {
154 struct sway_container *new_focus = seat_get_focus_inactive(seat,
155 new_workspace->sway_workspace->fullscreen);
156 seat_set_focus(seat, new_focus);
157 }
158 }
159 } 225 }
160 // Update workspace urgent state 226 if (container_is_floating(container)) {
161 struct sway_container *old_workspace = old_parent; 227 return;
162 if (old_workspace->type != C_WORKSPACE) {
163 old_workspace = container_parent(old_workspace, C_WORKSPACE);
164 }
165 if (new_workspace != old_workspace) {
166 workspace_detect_urgent(new_workspace);
167 if (old_workspace) {
168 workspace_detect_urgent(old_workspace);
169 }
170 } 228 }
171} 229 struct sway_workspace *old_workspace = container->workspace;
172 230
173static bool is_parallel(enum sway_container_layout layout, 231 container_detach(container);
174 enum movement_direction dir) { 232 container->width = container->height = 0;
175 switch (layout) { 233 container->saved_width = container->saved_height = 0;
176 case L_TABBED: 234
177 case L_HORIZ: 235 if (destination->view) {
178 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 236 container_add_sibling(destination, container, 1);
179 case L_STACKED: 237 } else {
180 case L_VERT: 238 container_add_child(destination, container);
181 return dir == MOVE_UP || dir == MOVE_DOWN;
182 default:
183 return false;
184 } 239 }
185}
186 240
187static enum movement_direction invert_movement(enum movement_direction dir) { 241 if (container->view) {
188 switch (dir) { 242 ipc_event_window(container, "move");
189 case MOVE_LEFT:
190 return MOVE_RIGHT;
191 case MOVE_RIGHT:
192 return MOVE_LEFT;
193 case MOVE_UP:
194 return MOVE_DOWN;
195 case MOVE_DOWN:
196 return MOVE_UP;
197 default:
198 sway_assert(0, "This function expects left|right|up|down");
199 return MOVE_LEFT;
200 } 243 }
201}
202 244
203static int move_offs(enum movement_direction move_dir) { 245 workspace_focus_fullscreen(destination->workspace);
204 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
205}
206 246
207/* Gets the index of the most extreme member based on the movement offset */ 247 // Update workspace urgent state
208static int container_limit(struct sway_container *container, 248 workspace_detect_urgent(destination->workspace);
209 enum movement_direction move_dir) { 249 if (old_workspace != destination->workspace) {
210 return move_offs(move_dir) < 0 ? 0 : container->children->length; 250 workspace_detect_urgent(old_workspace);
251 }
211} 252}
212 253
213/* Takes one child, sets it aside, wraps the rest of the children in a new 254/* Takes one child, sets it aside, wraps the rest of the children in a new
214 * container, switches the layout of the workspace, and drops the child back in. 255 * container, switches the layout of the workspace, and drops the child back in.
215 * In other words, rejigger it. */ 256 * In other words, rejigger it. */
216static void workspace_rejigger(struct sway_container *ws, 257static void workspace_rejigger(struct sway_workspace *ws,
217 struct sway_container *child, enum movement_direction move_dir) { 258 struct sway_container *child, enum movement_direction move_dir) {
218 struct sway_container *original_parent = child->parent; 259 if (!sway_assert(child->parent == NULL, "Expected a root child")) {
219 struct sway_container *new_parent = 260 return;
220 container_split(ws, ws->layout);
221
222 container_remove_child(child);
223 for (int i = 0; i < ws->children->length; ++i) {
224 struct sway_container *_child = ws->children->items[i];
225 container_move_to(new_parent, _child);
226 } 261 }
262 container_detach(child);
263 workspace_wrap_children(ws);
227 264
228 int index = move_offs(move_dir); 265 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1;
229 container_insert_child(ws, child, index < 0 ? 0 : 1); 266 workspace_insert_tiling(ws, child, index);
230 ws->layout = 267 ws->layout =
231 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 268 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
232 269 workspace_update_representation(ws);
233 container_flatten(ws);
234 container_reap_empty(original_parent);
235 container_create_notify(new_parent);
236} 270}
237 271
238static void move_out_of_tabs_stacks(struct sway_container *container, 272static void move_out_of_tabs_stacks(struct sway_container *container,
239 struct sway_container *current, enum movement_direction move_dir, 273 struct sway_container *current, enum movement_direction move_dir,
240 int offs) { 274 int offs) {
241 if (container->parent == current->parent 275 enum sway_container_layout layout = move_dir ==
242 && current->parent->children->length == 1) { 276 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
243 wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); 277 list_t *siblings = container_get_siblings(container);
244 current->parent->layout = move_dir == 278 if (container == current && siblings->length == 1) {
245 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 279 wlr_log(WLR_DEBUG, "Changing layout of parent");
280 if (container->parent) {
281 container->parent->layout = layout;
282 container_update_representation(container);
283 } else {
284 container->workspace->layout = layout;
285 workspace_update_representation(container->workspace);
286 }
246 return; 287 return;
247 } 288 }
248 289
249 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); 290 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
250 bool is_workspace = current->parent->type == C_WORKSPACE; 291 if (container->parent) {
251 struct sway_container *new_parent = container_split(current->parent, 292 struct sway_container *new_parent =
252 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); 293 container_split(current->parent, layout);
253 if (is_workspace) {
254 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1);
255 } else {
256 container_insert_child(new_parent, container, offs < 0 ? 0 : 1); 294 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
257 container_reap_empty(new_parent->parent); 295 container_reap_empty(new_parent);
258 container_flatten(new_parent->parent); 296 container_flatten(new_parent);
297 } else {
298 // Changing a workspace
299 struct sway_workspace *workspace = container->workspace;
300 workspace_split(workspace, layout);
301 workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1);
259 } 302 }
260 container_create_notify(new_parent);
261 container_notify_subtree_changed(new_parent);
262} 303}
263 304
264static void container_move(struct sway_container *container, 305// Returns true if moved
265 enum movement_direction move_dir, int move_amt) { 306static bool container_move_in_direction(struct sway_container *container,
266 if (!sway_assert( 307 enum movement_direction move_dir) {
267 container->type != C_CONTAINER || container->type != C_VIEW,
268 "Can only move containers and views")) {
269 return;
270 }
271 int offs = move_offs(move_dir);
272
273 struct sway_container *sibling = NULL;
274 struct sway_container *current = container;
275 struct sway_container *parent = current->parent;
276 struct sway_container *top = &root_container;
277
278 // If moving a fullscreen view, only consider outputs 308 // If moving a fullscreen view, only consider outputs
279 if (container->is_fullscreen) { 309 if (container->is_fullscreen) {
280 current = container_parent(container, C_OUTPUT); 310 struct sway_output *new_output =
281 } else if (container_is_fullscreen_or_child(container) || 311 output_get_in_direction(container->workspace->output, move_dir);
282 container_is_floating_or_child(container)) { 312 if (!new_output) {
283 // If we've fullscreened a split container, only allow the child to move 313 return false;
284 // around within the fullscreen parent. 314 }
285 // Same with floating a split container. 315 struct sway_workspace *ws = output_get_active_workspace(new_output);
286 struct sway_container *ws = container_parent(container, C_WORKSPACE); 316 container_move_to_workspace(container, ws);
287 top = ws->sway_workspace->fullscreen; 317 return true;
288 }
289
290 struct sway_container *new_parent = container_flatten(parent);
291 if (new_parent != parent) {
292 // Special case: we were the last one in this container, so leave
293 return;
294 } 318 }
295 319
296 while (!sibling) { 320 // If container is in a split container by itself, move out of the split
297 if (current == top) { 321 if (container->parent) {
298 return; 322 struct sway_container *new_parent =
323 container_flatten(container->parent);
324 if (new_parent != container->parent) {
325 return true;
299 } 326 }
327 }
300 328
301 parent = current->parent; 329 // Look for a suitable *container* sibling or parent.
302 wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, 330 // The below loop stops once we hit the workspace because current->parent
303 container_type_to_str(current->type), current->name); 331 // is NULL for the topmost containers in a workspace.
304 332 struct sway_container *current = container;
305 int index = container_sibling_index(current); 333 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
306 334
307 switch (current->type) { 335 while (current) {
308 case C_OUTPUT: { 336 struct sway_container *parent = current->parent;
309 enum wlr_direction wlr_dir = 0; 337 list_t *siblings = container_get_siblings(current);
310 if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), 338 enum sway_container_layout layout = container_parent_layout(current);
311 "got invalid direction: %d", move_dir)) { 339 int index = list_find(siblings, current);
312 return; 340 int desired = index + offs;
313 } 341
314 double ref_lx = current->x + current->width / 2; 342 if (is_parallel(layout, move_dir)) {
315 double ref_ly = current->y + current->height / 2; 343 if (desired == -1 || desired == siblings->length) {
316 struct wlr_output *next = wlr_output_layout_adjacent_output( 344 if (current->parent == container->parent) {
317 root_container.sway_root->output_layout, wlr_dir, 345 if (!(parent && parent->is_fullscreen) &&
318 current->sway_output->wlr_output, ref_lx, ref_ly); 346 (layout == L_TABBED || layout == L_STACKED)) {
319 if (!next) { 347 move_out_of_tabs_stacks(container, current,
320 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); 348 move_dir, offs);
321 return; 349 return true;
322 }
323 struct sway_output *next_output = next->data;
324 current = next_output->swayc;
325 wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);
326 // Select workspace and get outta here
327 current = seat_get_focus_inactive(
328 config->handler_context.seat, current);
329 if (current->type != C_WORKSPACE) {
330 current = container_parent(current, C_WORKSPACE);
331 }
332 sibling = current;
333 break;
334 }
335 case C_WORKSPACE:
336 if (!is_parallel(current->layout, move_dir)) {
337 if (current->children->length >= 2) {
338 wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",
339 current->children->length);
340 workspace_rejigger(current, container, move_dir);
341 return;
342 } else {
343 wlr_log(WLR_DEBUG, "Selecting output");
344 current = current->parent;
345 }
346 } else if (current->layout == L_TABBED
347 || current->layout == L_STACKED) {
348 wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");
349 workspace_rejigger(current, container, move_dir);
350 } else {
351 wlr_log(WLR_DEBUG, "Selecting output");
352 current = current->parent;
353 }
354 break;
355 case C_CONTAINER:
356 case C_VIEW:
357 if (is_parallel(parent->layout, move_dir)) {
358 if ((index == parent->children->length - 1 && offs > 0)
359 || (index == 0 && offs < 0)) {
360 if (current->parent == container->parent) {
361 if (!parent->is_fullscreen &&
362 (parent->layout == L_TABBED ||
363 parent->layout == L_STACKED)) {
364 move_out_of_tabs_stacks(container, current,
365 move_dir, offs);
366 return;
367 } else {
368 wlr_log(WLR_DEBUG, "Hit limit, selecting parent");
369 current = current->parent;
370 }
371 } else { 350 } else {
372 wlr_log(WLR_DEBUG, "Hit limit, " 351 current = current->parent;
373 "promoting descendant to sibling"); 352 continue;
374 // Special case 353 }
354 } else {
355 // Special case
356 if (current->parent) {
375 container_insert_child(current->parent, container, 357 container_insert_child(current->parent, container,
376 index + (offs < 0 ? 0 : 1)); 358 index + (offs < 0 ? 0 : 1));
377 container->width = container->height = 0; 359 } else {
378 return; 360 workspace_insert_tiling(current->workspace, container,
361 index + (offs < 0 ? 0 : 1));
379 } 362 }
380 } else { 363 return true;
381 sibling = parent->children->items[index + offs];
382 wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
383 } 364 }
384 } else if (!parent->is_fullscreen && (parent->layout == L_TABBED ||
385 parent->layout == L_STACKED)) {
386 move_out_of_tabs_stacks(container, current, move_dir, offs);
387 return;
388 } else { 365 } else {
389 wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); 366 // Container can move within its siblings
390 current = current->parent; 367 container_move_to_container_from_direction(container,
368 siblings->items[desired], move_dir);
369 return true;
391 } 370 }
392 break; 371 } else if (!(parent && parent->is_fullscreen) &&
393 default: 372 (layout == L_TABBED || layout == L_STACKED)) {
394 sway_assert(0, "Not expecting to see container of type %s here", 373 move_out_of_tabs_stacks(container, current, move_dir, offs);
395 container_type_to_str(current->type)); 374 return true;
396 return;
397 } 375 }
398 }
399 376
400 // Part two: move stuff around 377 current = current->parent;
401 int index = container_sibling_index(container);
402 struct sway_container *old_parent = container->parent;
403 378
404 while (sibling) { 379 // Don't allow containers to move out of their
405 switch (sibling->type) { 380 // fullscreen or floating parent
406 case C_VIEW: 381 if (current &&
407 if (sibling->parent == container->parent) { 382 (current->is_fullscreen || container_is_floating(current))) {
408 wlr_log(WLR_DEBUG, "Swapping siblings"); 383 return false;
409 sibling->parent->children->items[index + offs] = container;
410 sibling->parent->children->items[index] = sibling;
411 } else {
412 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
413 container_insert_child(sibling->parent, container,
414 container_sibling_index(sibling) + (offs > 0 ? 0 : 1));
415 container->width = container->height = 0;
416 }
417 sibling = NULL;
418 break;
419 case C_WORKSPACE: // Note: only in the case of moving between outputs
420 case C_CONTAINER:
421 if (is_parallel(sibling->layout, move_dir)) {
422 int limit = container_limit(sibling, invert_movement(move_dir));
423 wlr_log(WLR_DEBUG, "limit: %d", limit);
424 wlr_log(WLR_DEBUG,
425 "Reparenting container (parallel) to index %d "
426 "(move dir: %d)", limit, move_dir);
427 container_insert_child(sibling, container, limit);
428 container->width = container->height = 0;
429 sibling = NULL;
430 } else {
431 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
432 struct sway_container *focus_inactive = seat_get_focus_inactive(
433 config->handler_context.seat, sibling);
434 if (focus_inactive && focus_inactive != sibling) {
435 while (focus_inactive->parent != sibling) {
436 focus_inactive = focus_inactive->parent;
437 }
438 wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",
439 focus_inactive->id);
440 sibling = focus_inactive;
441 continue;
442 } else if (sibling->children->length) {
443 wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");
444 container_remove_child(container);
445 container_add_sibling(sibling->children->items[0], container);
446 } else {
447 wlr_log(WLR_DEBUG, "No kiddos, adding container alone");
448 container_remove_child(container);
449 container_add_child(sibling, container);
450 }
451 container->width = container->height = 0;
452 sibling = NULL;
453 }
454 break;
455 default:
456 sway_assert(0, "Not expecting to see container of type %s here",
457 container_type_to_str(sibling->type));
458 return;
459 } 384 }
460 } 385 }
461 386
462 container_notify_subtree_changed(old_parent); 387 // Maybe rejigger the workspace
463 container_notify_subtree_changed(container->parent); 388 struct sway_workspace *ws = container->workspace;
464 389 if (!is_parallel(ws->layout, move_dir)) {
465 if (container->type == C_VIEW) { 390 if (ws->tiling->length >= 2) {
466 ipc_event_window(container, "move"); 391 workspace_rejigger(ws, container, move_dir);
467 } 392 return true;
468 393 }
469 if (old_parent) { 394 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
470 seat_set_focus(config->handler_context.seat, old_parent); 395 workspace_rejigger(ws, container, move_dir);
471 seat_set_focus(config->handler_context.seat, container); 396 return true;
472 } 397 }
473 398
474 struct sway_container *last_ws = old_parent; 399 // Try adjacent output
475 struct sway_container *next_ws = container->parent; 400 struct sway_output *output =
476 if (last_ws && last_ws->type != C_WORKSPACE) { 401 output_get_in_direction(container->workspace->output, move_dir);
477 last_ws = container_parent(last_ws, C_WORKSPACE); 402 if (output) {
478 } 403 struct sway_workspace *ws = output_get_active_workspace(output);
479 if (next_ws && next_ws->type != C_WORKSPACE) { 404 container_move_to_workspace_from_direction(container, ws, move_dir);
480 next_ws = container_parent(next_ws, C_WORKSPACE); 405 return true;
481 } 406 }
482 if (last_ws && next_ws && last_ws != next_ws) { 407 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");
483 ipc_event_workspace(last_ws, next_ws, "focus"); 408 return false;
484 workspace_detect_urgent(last_ws);
485 workspace_detect_urgent(next_ws);
486 }
487 container_end_mouse_operation(container);
488} 409}
489 410
490static struct cmd_results *cmd_move_container(struct sway_container *current, 411static struct cmd_results *cmd_move_container(int argc, char **argv) {
491 int argc, char **argv) {
492 struct cmd_results *error = NULL; 412 struct cmd_results *error = NULL;
493 if ((error = checkarg(argc, "move container/window", 413 if ((error = checkarg(argc, "move container/window",
494 EXPECTED_AT_LEAST, 3))) { 414 EXPECTED_AT_LEAST, 3))) {
495 return error; 415 return error;
496 } 416 }
497 417
498 if (current->type == C_WORKSPACE) { 418 struct sway_node *node = config->handler_context.node;
499 if (current->children->length == 0) { 419 struct sway_workspace *workspace = config->handler_context.workspace;
420 struct sway_container *container = config->handler_context.container;
421 if (node->type == N_WORKSPACE) {
422 if (workspace->tiling->length == 0) {
500 return cmd_results_new(CMD_FAILURE, "move", 423 return cmd_results_new(CMD_FAILURE, "move",
501 "Can't move an empty workspace"); 424 "Can't move an empty workspace");
502 } 425 }
503 current = workspace_wrap_children(current); 426 container = workspace_wrap_children(workspace);
504 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
505 return cmd_results_new(CMD_FAILURE, "move",
506 "Can only move containers and views.");
507 } 427 }
508 428
509 bool no_auto_back_and_forth = false; 429 bool no_auto_back_and_forth = false;
@@ -530,15 +450,15 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
530 } 450 }
531 451
532 struct sway_seat *seat = config->handler_context.seat; 452 struct sway_seat *seat = config->handler_context.seat;
533 struct sway_container *old_parent = current->parent; 453 struct sway_container *old_parent = container->parent;
534 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 454 struct sway_workspace *old_ws = container->workspace;
535 struct sway_container *old_output = container_parent(current, C_OUTPUT); 455 struct sway_output *old_output = old_ws->output;
536 struct sway_container *destination = NULL; 456 struct sway_node *destination = NULL;
537 457
538 // determine destination 458 // determine destination
539 if (strcasecmp(argv[1], "workspace") == 0) { 459 if (strcasecmp(argv[1], "workspace") == 0) {
540 // move container to workspace x 460 // move container to workspace x
541 struct sway_container *ws = NULL; 461 struct sway_workspace *ws = NULL;
542 char *ws_name = NULL; 462 char *ws_name = NULL;
543 if (strcasecmp(argv[2], "next") == 0 || 463 if (strcasecmp(argv[2], "next") == 0 ||
544 strcasecmp(argv[2], "prev") == 0 || 464 strcasecmp(argv[2], "prev") == 0 ||
@@ -588,8 +508,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
588 // We have to create the workspace, but if the container is 508 // We have to create the workspace, but if the container is
589 // sticky and the workspace is going to be created on the same 509 // sticky and the workspace is going to be created on the same
590 // output, we'll bail out first. 510 // output, we'll bail out first.
591 if (current->is_sticky) { 511 if (container->is_sticky) {
592 struct sway_container *new_output = 512 struct sway_output *new_output =
593 workspace_get_initial_output(ws_name); 513 workspace_get_initial_output(ws_name);
594 if (old_output == new_output) { 514 if (old_output == new_output) {
595 free(ws_name); 515 free(ws_name);
@@ -601,105 +521,113 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
601 ws = workspace_create(NULL, ws_name); 521 ws = workspace_create(NULL, ws_name);
602 } 522 }
603 free(ws_name); 523 free(ws_name);
604 destination = seat_get_focus_inactive(seat, ws); 524 destination = seat_get_focus_inactive(seat, &ws->node);
605 } else if (strcasecmp(argv[1], "output") == 0) { 525 } else if (strcasecmp(argv[1], "output") == 0) {
606 struct sway_container *dest_output = output_in_direction(argv[2], 526 struct sway_output *new_output = output_in_direction(argv[2],
607 old_output->sway_output->wlr_output, current->x, current->y); 527 old_output->wlr_output, container->x, container->y);
608 if (!dest_output) { 528 if (!new_output) {
609 return cmd_results_new(CMD_FAILURE, "move workspace", 529 return cmd_results_new(CMD_FAILURE, "move workspace",
610 "Can't find output with name/direction '%s'", argv[2]); 530 "Can't find output with name/direction '%s'", argv[2]);
611 } 531 }
612 destination = seat_get_focus_inactive(seat, dest_output); 532 destination = seat_get_focus_inactive(seat, &new_output->node);
613 if (!destination) {
614 // We've never been to this output before
615 destination = dest_output->children->items[0];
616 }
617 } else if (strcasecmp(argv[1], "mark") == 0) { 533 } else if (strcasecmp(argv[1], "mark") == 0) {
618 struct sway_view *dest_view = view_find_mark(argv[2]); 534 struct sway_view *dest_view = view_find_mark(argv[2]);
619 if (dest_view == NULL) { 535 if (dest_view == NULL) {
620 return cmd_results_new(CMD_FAILURE, "move", 536 return cmd_results_new(CMD_FAILURE, "move",
621 "Mark '%s' not found", argv[2]); 537 "Mark '%s' not found", argv[2]);
622 } 538 }
623 destination = dest_view->swayc; 539 destination = &dest_view->container->node;
624 } else { 540 } else {
625 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 541 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
626 } 542 }
627 543
628 struct sway_container *new_output = destination->type == C_OUTPUT ? 544 if (container->is_sticky &&
629 destination : container_parent(destination, C_OUTPUT); 545 node_has_ancestor(destination, &old_output->node)) {
630 if (current->is_sticky && old_output == new_output) {
631 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " 546 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky "
632 "container to another workspace on the same output"); 547 "container to another workspace on the same output");
633 } 548 }
634 549
635 struct sway_container *new_output_last_ws = old_output == new_output ? 550 struct sway_output *new_output = node_get_output(destination);
636 NULL : seat_get_active_child(seat, new_output); 551 struct sway_workspace *new_output_last_ws = old_output == new_output ?
637 struct sway_container *new_workspace = destination->type == C_WORKSPACE ? 552 NULL : output_get_active_workspace(new_output);
638 destination : container_parent(destination, C_WORKSPACE);
639 553
640 // move container, arrange windows and return focus 554 // move container, arrange windows and return focus
641 container_move_to(current, destination); 555 switch (destination->type) {
556 case N_WORKSPACE:
557 container_move_to_workspace(container, destination->sway_workspace);
558 break;
559 case N_OUTPUT: {
560 struct sway_output *output = destination->sway_output;
561 struct sway_workspace *ws = output_get_active_workspace(output);
562 container_move_to_workspace(container, ws);
563 }
564 break;
565 case N_CONTAINER:
566 container_move_to_container(container, destination->sway_container);
567 break;
568 case N_ROOT:
569 break;
570 }
571 struct sway_workspace *new_workspace =
572 output_get_active_workspace(new_output);
642 if (new_output_last_ws && new_output_last_ws != new_workspace) { 573 if (new_output_last_ws && new_output_last_ws != new_workspace) {
643 // change focus on destination output back to its last active workspace 574 // change focus on destination output back to its last active workspace
644 struct sway_container *new_output_last_focus = 575 struct sway_node *new_output_last_focus =
645 seat_get_focus_inactive(seat, new_output_last_ws); 576 seat_get_focus_inactive(seat, &new_output_last_ws->node);
646 seat_set_focus_warp(seat, new_output_last_focus, false, false); 577 seat_set_focus_warp(seat, new_output_last_focus, false, false);
647 } 578 }
648 struct sway_container *focus = seat_get_focus_inactive(seat, old_parent); 579
580 struct sway_node *focus = NULL;
581 if (old_parent) {
582 focus = seat_get_focus_inactive(seat, &old_parent->node);
583 } else {
584 focus = seat_get_focus_inactive(seat, &old_ws->node);
585 }
649 seat_set_focus_warp(seat, focus, true, false); 586 seat_set_focus_warp(seat, focus, true, false);
650 container_reap_empty(old_parent);
651 container_reap_empty(destination->parent);
652 587
653 // TODO: Ideally we would arrange the surviving parent after reaping, 588 if (old_parent) {
654 // but container_reap_empty does not return it, so we arrange the 589 container_reap_empty(old_parent);
655 // workspace instead. 590 } else {
656 arrange_windows(old_ws); 591 workspace_consider_destroy(old_ws);
657 arrange_windows(destination->parent); 592 }
593
594 arrange_workspace(old_ws);
595 arrange_node(node_get_parent(destination));
658 596
659 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 597 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
660} 598}
661 599
662static void workspace_move_to_output(struct sway_container *workspace, 600static void workspace_move_to_output(struct sway_workspace *workspace,
663 struct sway_container *output) { 601 struct sway_output *output) {
664 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 602 if (workspace->output == output) {
665 return; 603 return;
666 } 604 }
667 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 605 struct sway_output *old_output = workspace->output;
668 return; 606 workspace_detach(workspace);
669 } 607 struct sway_workspace *new_output_old_ws =
670 if (workspace->parent == output) { 608 output_get_active_workspace(output);
671 return;
672 }
673 struct sway_container *old_output = container_remove_child(workspace);
674 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
675 struct sway_container *new_output_focus =
676 seat_get_focus_inactive(seat, output);
677 609
678 container_add_child(output, workspace); 610 output_add_workspace(output, workspace);
679 611
680 // If moving the last workspace from the old output, create a new workspace 612 // If moving the last workspace from the old output, create a new workspace
681 // on the old output 613 // on the old output
682 if (old_output->children->length == 0) { 614 struct sway_seat *seat = config->handler_context.seat;
683 char *ws_name = workspace_next_name(old_output->name); 615 if (old_output->workspaces->length == 0) {
684 struct sway_container *ws = workspace_create(old_output, ws_name); 616 char *ws_name = workspace_next_name(old_output->wlr_output->name);
617 struct sway_workspace *ws = workspace_create(old_output, ws_name);
685 free(ws_name); 618 free(ws_name);
686 seat_set_focus(seat, ws); 619 seat_set_focus(seat, &ws->node);
687 } 620 }
688 621
689 // Try to remove an empty workspace from the destination output. 622 workspace_consider_destroy(new_output_old_ws);
690 container_reap_empty(new_output_focus);
691 623
692 output_sort_workspaces(output); 624 output_sort_workspaces(output);
693 seat_set_focus(seat, output); 625 seat_set_focus(seat, &output->node);
694 workspace_output_raise_priority(workspace, old_output, output); 626 workspace_output_raise_priority(workspace, old_output, output);
695 ipc_event_workspace(NULL, workspace, "move"); 627 ipc_event_workspace(NULL, workspace, "move");
696
697 container_notify_subtree_changed(old_output);
698 container_notify_subtree_changed(output);
699} 628}
700 629
701static struct cmd_results *cmd_move_workspace(struct sway_container *current, 630static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
702 int argc, char **argv) {
703 struct cmd_results *error = NULL; 631 struct cmd_results *error = NULL;
704 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { 632 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
705 return error; 633 return error;
@@ -716,27 +644,25 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
716 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 644 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
717 } 645 }
718 646
719 struct sway_container *source = container_parent(current, C_OUTPUT); 647 struct sway_workspace *workspace = config->handler_context.workspace;
720 int center_x = current->width / 2 + current->x, 648 struct sway_output *old_output = workspace->output;
721 center_y = current->height / 2 + current->y; 649 int center_x = workspace->width / 2 + workspace->x,
722 struct sway_container *destination = output_in_direction(argv[2], 650 center_y = workspace->height / 2 + workspace->y;
723 source->sway_output->wlr_output, center_x, center_y); 651 struct sway_output *new_output = output_in_direction(argv[2],
724 if (!destination) { 652 old_output->wlr_output, center_x, center_y);
653 if (!new_output) {
725 return cmd_results_new(CMD_FAILURE, "move workspace", 654 return cmd_results_new(CMD_FAILURE, "move workspace",
726 "Can't find output with name/direction '%s'", argv[2]); 655 "Can't find output with name/direction '%s'", argv[2]);
727 } 656 }
728 if (current->type != C_WORKSPACE) { 657 workspace_move_to_output(workspace, new_output);
729 current = container_parent(current, C_WORKSPACE);
730 }
731 workspace_move_to_output(current, destination);
732 658
733 arrange_windows(source); 659 arrange_output(old_output);
734 arrange_windows(destination); 660 arrange_output(new_output);
735 661
736 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 662 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
737} 663}
738 664
739static struct cmd_results *move_in_direction(struct sway_container *container, 665static struct cmd_results *cmd_move_in_direction(
740 enum movement_direction direction, int argc, char **argv) { 666 enum movement_direction direction, int argc, char **argv) {
741 int move_amt = 10; 667 int move_amt = 10;
742 if (argc > 1) { 668 if (argc > 1) {
@@ -748,7 +674,8 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
748 } 674 }
749 } 675 }
750 676
751 if (container->type == C_WORKSPACE) { 677 struct sway_container *container = config->handler_context.container;
678 if (!container) {
752 return cmd_results_new(CMD_FAILURE, "move", 679 return cmd_results_new(CMD_FAILURE, "move",
753 "Cannot move workspaces in a direction"); 680 "Cannot move workspaces in a direction");
754 } 681 }
@@ -780,20 +707,34 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
780 container_floating_move_to(container, lx, ly); 707 container_floating_move_to(container, lx, ly);
781 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 708 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
782 } 709 }
783 // For simplicity, we'll arrange the entire workspace. The reason for this 710 struct sway_workspace *old_ws = container->workspace;
784 // is moving the container might reap the old parent, and container_move
785 // does not return a surviving parent.
786 // TODO: Make container_move return the surviving parent so we can arrange
787 // just that.
788 struct sway_container *old_ws = container_parent(container, C_WORKSPACE);
789 container_move(container, direction, move_amt);
790 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
791 711
792 arrange_windows(old_ws); 712 if (!container_move_in_direction(container, direction)) {
713 // Container didn't move
714 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
715 }
716
717 struct sway_workspace *new_ws = container->workspace;
718
719 arrange_workspace(old_ws);
793 if (new_ws != old_ws) { 720 if (new_ws != old_ws) {
794 arrange_windows(new_ws); 721 arrange_workspace(new_ws);
722 }
723
724 if (container->view) {
725 ipc_event_window(container, "move");
795 } 726 }
796 727
728 seat_set_focus(config->handler_context.seat, &new_ws->node);
729 seat_set_focus(config->handler_context.seat, &container->node);
730
731 if (old_ws != new_ws) {
732 ipc_event_workspace(old_ws, new_ws, "focus");
733 workspace_detect_urgent(old_ws);
734 workspace_detect_urgent(new_ws);
735 }
736 container_end_mouse_operation(container);
737
797 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 738 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
798} 739}
799 740
@@ -802,9 +743,9 @@ static const char *expected_position_syntax =
802 "'move [absolute] position center' or " 743 "'move [absolute] position center' or "
803 "'move position cursor|mouse|pointer'"; 744 "'move position cursor|mouse|pointer'";
804 745
805static struct cmd_results *move_to_position(struct sway_container *container, 746static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
806 int argc, char **argv) { 747 struct sway_container *container = config->handler_context.container;
807 if (!container_is_floating(container)) { 748 if (!container || !container_is_floating(container)) {
808 return cmd_results_new(CMD_FAILURE, "move", 749 return cmd_results_new(CMD_FAILURE, "move",
809 "Only floating containers " 750 "Only floating containers "
810 "can be moved to an absolute position"); 751 "can be moved to an absolute position");
@@ -842,10 +783,10 @@ static struct cmd_results *move_to_position(struct sway_container *container,
842 } else if (strcmp(argv[0], "center") == 0) { 783 } else if (strcmp(argv[0], "center") == 0) {
843 double lx, ly; 784 double lx, ly;
844 if (absolute) { 785 if (absolute) {
845 lx = root_container.x + (root_container.width - container->width) / 2; 786 lx = root->x + (root->width - container->width) / 2;
846 ly = root_container.y + (root_container.height - container->height) / 2; 787 ly = root->y + (root->height - container->height) / 2;
847 } else { 788 } else {
848 struct sway_container *ws = container_parent(container, C_WORKSPACE); 789 struct sway_workspace *ws = container->workspace;
849 lx = ws->x + (ws->width - container->width) / 2; 790 lx = ws->x + (ws->width - container->width) / 2;
850 ly = ws->y + (ws->height - container->height) / 2; 791 ly = ws->y + (ws->height - container->height) / 2;
851 } 792 }
@@ -881,30 +822,31 @@ static struct cmd_results *move_to_position(struct sway_container *container,
881 } 822 }
882 823
883 if (!absolute) { 824 if (!absolute) {
884 struct sway_container *ws = container_parent(container, C_WORKSPACE); 825 lx += container->workspace->x;
885 lx += ws->x; 826 ly += container->workspace->y;
886 ly += ws->y;
887 } 827 }
888 container_floating_move_to(container, lx, ly); 828 container_floating_move_to(container, lx, ly);
889 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 829 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
890} 830}
891 831
892static struct cmd_results *move_to_scratchpad(struct sway_container *con) { 832static struct cmd_results *cmd_move_to_scratchpad(void) {
893 if (con->type == C_WORKSPACE && con->children->length == 0) { 833 struct sway_node *node = config->handler_context.node;
834 struct sway_container *con = config->handler_context.container;
835 struct sway_workspace *ws = config->handler_context.workspace;
836 if (node->type == N_WORKSPACE && ws->tiling->length == 0) {
894 return cmd_results_new(CMD_INVALID, "move", 837 return cmd_results_new(CMD_INVALID, "move",
895 "Can't move an empty workspace to the scratchpad"); 838 "Can't move an empty workspace to the scratchpad");
896 } 839 }
897 if (con->type == C_WORKSPACE) { 840 if (node->type == N_WORKSPACE) {
898 // Wrap the workspace's children in a container 841 // Wrap the workspace's children in a container
899 struct sway_container *workspace = con; 842 con = workspace_wrap_children(ws);
900 con = workspace_wrap_children(con); 843 ws->layout = L_HORIZ;
901 workspace->layout = L_HORIZ;
902 } 844 }
903 845
904 // If the container is in a floating split container, 846 // If the container is in a floating split container,
905 // operate on the split container instead of the child. 847 // operate on the split container instead of the child.
906 if (container_is_floating_or_child(con)) { 848 if (container_is_floating_or_child(con)) {
907 while (con->parent->type != C_WORKSPACE) { 849 while (con->parent) {
908 con = con->parent; 850 con = con->parent;
909 } 851 }
910 } 852 }
@@ -922,32 +864,31 @@ struct cmd_results *cmd_move(int argc, char **argv) {
922 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 864 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
923 return error; 865 return error;
924 } 866 }
925 struct sway_container *current = config->handler_context.current_container;
926 867
927 if (strcasecmp(argv[0], "left") == 0) { 868 if (strcasecmp(argv[0], "left") == 0) {
928 return move_in_direction(current, MOVE_LEFT, argc, argv); 869 return cmd_move_in_direction(MOVE_LEFT, argc, argv);
929 } else if (strcasecmp(argv[0], "right") == 0) { 870 } else if (strcasecmp(argv[0], "right") == 0) {
930 return move_in_direction(current, MOVE_RIGHT, argc, argv); 871 return cmd_move_in_direction(MOVE_RIGHT, argc, argv);
931 } else if (strcasecmp(argv[0], "up") == 0) { 872 } else if (strcasecmp(argv[0], "up") == 0) {
932 return move_in_direction(current, MOVE_UP, argc, argv); 873 return cmd_move_in_direction(MOVE_UP, argc, argv);
933 } else if (strcasecmp(argv[0], "down") == 0) { 874 } else if (strcasecmp(argv[0], "down") == 0) {
934 return move_in_direction(current, MOVE_DOWN, argc, argv); 875 return cmd_move_in_direction(MOVE_DOWN, argc, argv);
935 } else if ((strcasecmp(argv[0], "container") == 0 876 } else if ((strcasecmp(argv[0], "container") == 0
936 || strcasecmp(argv[0], "window") == 0) || 877 || strcasecmp(argv[0], "window") == 0) ||
937 (strcasecmp(argv[0], "--no-auto-back-and-forth") && 878 (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2
938 (strcasecmp(argv[0], "container") == 0 879 && (strcasecmp(argv[1], "container") == 0
939 || strcasecmp(argv[0], "window") == 0))) { 880 || strcasecmp(argv[1], "window") == 0))) {
940 return cmd_move_container(current, argc, argv); 881 return cmd_move_container(argc, argv);
941 } else if (strcasecmp(argv[0], "workspace") == 0) { 882 } else if (strcasecmp(argv[0], "workspace") == 0) {
942 return cmd_move_workspace(current, argc, argv); 883 return cmd_move_workspace(argc, argv);
943 } else if (strcasecmp(argv[0], "scratchpad") == 0 884 } else if (strcasecmp(argv[0], "scratchpad") == 0
944 || (strcasecmp(argv[0], "to") == 0 && argc == 2 885 || (strcasecmp(argv[0], "to") == 0 && argc == 2
945 && strcasecmp(argv[1], "scratchpad") == 0)) { 886 && strcasecmp(argv[1], "scratchpad") == 0)) {
946 return move_to_scratchpad(current); 887 return cmd_move_to_scratchpad();
947 } else if (strcasecmp(argv[0], "position") == 0) { 888 } else if (strcasecmp(argv[0], "position") == 0) {
948 return move_to_position(current, argc, argv); 889 return cmd_move_to_position(argc, argv);
949 } else if (strcasecmp(argv[0], "absolute") == 0) { 890 } else if (strcasecmp(argv[0], "absolute") == 0) {
950 return move_to_position(current, argc, argv); 891 return cmd_move_to_position(argc, argv);
951 } else { 892 } else {
952 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 893 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
953 } 894 }