aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands/move.c
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-25 12:09:42 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-25 12:09:42 +1000
commitf4bc25bcc6c822e264938447940b7d75fa84319b (patch)
treeb04befcfad9b7c679b1dec388d168901fc5daf86 /sway/commands/move.c
parentMerge pull request #2499 from RyanDwyer/refactor-destroy-functions (diff)
downloadsway-f4bc25bcc6c822e264938447940b7d75fa84319b.tar.gz
sway-f4bc25bcc6c822e264938447940b7d75fa84319b.tar.zst
sway-f4bc25bcc6c822e264938447940b7d75fa84319b.zip
Relocate container_move, container_move_to and container_get_in_direction
* container_move is only called from the move command * container_move_to was called from both the move command and the sticky command, but the sticky command can easily not call it * container_get_in_direction is only called from the focus command Moving these functions to their respective commands gives better separation of code and removes bloat from layout.c. These functions will need to be refactored to take advantage of type safety, so separating them will make this easier to refactor. The following static functions have also been moved: * is_parellel * invert_movement * move_offs * container_limit * workspace_rejigger * move_out_of_tabs_stacks * get_swayc_in_output_direction They were all used by the move functions, except for the last one which is used by focus. Other changes: * index_child has been renamed to container_sibling_index, moved to container.c and made public * sway_output_from_wlr has been renamed to output_from_wlr_output, moved to output.c and made public * container_handle_fullscreen_reparent has been made public * sway_dir_to_wlr has been made public No changes have been made to any of the moved functions, other than updating calls to functions that have been renamed.
Diffstat (limited to 'sway/commands/move.c')
-rw-r--r--sway/commands/move.c406
1 files changed, 406 insertions, 0 deletions
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 4c0189ec..51e52665 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -52,6 +52,412 @@ static struct sway_container *output_in_direction(const char *direction,
52 return output_by_name(direction); 52 return output_by_name(direction);
53} 53}
54 54
55static void container_move_to(struct sway_container *container,
56 struct sway_container *destination) {
57 if (!sway_assert(container->type == C_CONTAINER ||
58 container->type == C_VIEW, "Expected a container or view")) {
59 return;
60 }
61 if (container == destination
62 || container_has_ancestor(container, destination)) {
63 return;
64 }
65 struct sway_container *old_parent = NULL;
66 struct sway_container *new_parent = NULL;
67 if (container_is_floating(container)) {
68 // Resolve destination into a workspace
69 struct sway_container *new_ws = NULL;
70 if (destination->type == C_OUTPUT) {
71 new_ws = output_get_active_workspace(destination->sway_output);
72 } else if (destination->type == C_WORKSPACE) {
73 new_ws = destination;
74 } else {
75 new_ws = container_parent(destination, C_WORKSPACE);
76 }
77 if (!new_ws) {
78 // This can happen if the user has run "move container to mark foo",
79 // where mark foo is on a hidden scratchpad container.
80 return;
81 }
82 struct sway_container *old_output =
83 container_parent(container, C_OUTPUT);
84 old_parent = container_remove_child(container);
85 workspace_add_floating(new_ws, container);
86 container_handle_fullscreen_reparent(container, old_parent);
87 // If changing output, center it within the workspace
88 if (old_output != new_ws->parent && !container->is_fullscreen) {
89 container_floating_move_to_center(container);
90 }
91 } else {
92 old_parent = container_remove_child(container);
93 container->width = container->height = 0;
94 container->saved_width = container->saved_height = 0;
95
96 if (destination->type == C_VIEW) {
97 new_parent = container_add_sibling(destination, container);
98 } else {
99 new_parent = destination;
100 container_add_child(destination, container);
101 }
102 }
103
104 if (container->type == C_VIEW) {
105 ipc_event_window(container, "move");
106 }
107 container_notify_subtree_changed(old_parent);
108 container_notify_subtree_changed(new_parent);
109
110 // If view was moved to a fullscreen workspace, refocus the fullscreen view
111 struct sway_container *new_workspace = container;
112 if (new_workspace->type != C_WORKSPACE) {
113 new_workspace = container_parent(new_workspace, C_WORKSPACE);
114 }
115 if (new_workspace->sway_workspace->fullscreen) {
116 struct sway_seat *seat;
117 struct sway_container *focus, *focus_ws;
118 wl_list_for_each(seat, &input_manager->seats, link) {
119 focus = seat_get_focus(seat);
120 focus_ws = focus;
121 if (focus_ws->type != C_WORKSPACE) {
122 focus_ws = container_parent(focus_ws, C_WORKSPACE);
123 }
124 if (focus_ws == new_workspace) {
125 struct sway_container *new_focus = seat_get_focus_inactive(seat,
126 new_workspace->sway_workspace->fullscreen);
127 seat_set_focus(seat, new_focus);
128 }
129 }
130 }
131 // Update workspace urgent state
132 struct sway_container *old_workspace = old_parent;
133 if (old_workspace->type != C_WORKSPACE) {
134 old_workspace = container_parent(old_workspace, C_WORKSPACE);
135 }
136 if (new_workspace != old_workspace) {
137 workspace_detect_urgent(new_workspace);
138 if (old_workspace) {
139 workspace_detect_urgent(old_workspace);
140 }
141 }
142}
143
144static bool is_parallel(enum sway_container_layout layout,
145 enum movement_direction dir) {
146 switch (layout) {
147 case L_TABBED:
148 case L_HORIZ:
149 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
150 case L_STACKED:
151 case L_VERT:
152 return dir == MOVE_UP || dir == MOVE_DOWN;
153 default:
154 return false;
155 }
156}
157
158static enum movement_direction invert_movement(enum movement_direction dir) {
159 switch (dir) {
160 case MOVE_LEFT:
161 return MOVE_RIGHT;
162 case MOVE_RIGHT:
163 return MOVE_LEFT;
164 case MOVE_UP:
165 return MOVE_DOWN;
166 case MOVE_DOWN:
167 return MOVE_UP;
168 default:
169 sway_assert(0, "This function expects left|right|up|down");
170 return MOVE_LEFT;
171 }
172}
173
174static int move_offs(enum movement_direction move_dir) {
175 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
176}
177
178/* Gets the index of the most extreme member based on the movement offset */
179static int container_limit(struct sway_container *container,
180 enum movement_direction move_dir) {
181 return move_offs(move_dir) < 0 ? 0 : container->children->length;
182}
183
184/* Takes one child, sets it aside, wraps the rest of the children in a new
185 * container, switches the layout of the workspace, and drops the child back in.
186 * In other words, rejigger it. */
187static void workspace_rejigger(struct sway_container *ws,
188 struct sway_container *child, enum movement_direction move_dir) {
189 struct sway_container *original_parent = child->parent;
190 struct sway_container *new_parent =
191 container_split(ws, ws->layout);
192
193 container_remove_child(child);
194 for (int i = 0; i < ws->children->length; ++i) {
195 struct sway_container *_child = ws->children->items[i];
196 container_move_to(new_parent, _child);
197 }
198
199 int index = move_offs(move_dir);
200 container_insert_child(ws, child, index < 0 ? 0 : 1);
201 ws->layout =
202 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
203
204 container_flatten(ws);
205 container_reap_empty(original_parent);
206 container_create_notify(new_parent);
207}
208
209static void move_out_of_tabs_stacks(struct sway_container *container,
210 struct sway_container *current, enum movement_direction move_dir,
211 int offs) {
212 if (container->parent == current->parent
213 && current->parent->children->length == 1) {
214 wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id);
215 current->parent->layout = move_dir ==
216 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
217 return;
218 }
219
220 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
221 bool is_workspace = current->parent->type == C_WORKSPACE;
222 struct sway_container *new_parent = container_split(current->parent,
223 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT);
224 if (is_workspace) {
225 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1);
226 } else {
227 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
228 container_reap_empty(new_parent->parent);
229 container_flatten(new_parent->parent);
230 }
231 container_create_notify(new_parent);
232 container_notify_subtree_changed(new_parent);
233}
234
235static void container_move(struct sway_container *container,
236 enum movement_direction move_dir, int move_amt) {
237 if (!sway_assert(
238 container->type != C_CONTAINER || container->type != C_VIEW,
239 "Can only move containers and views")) {
240 return;
241 }
242 int offs = move_offs(move_dir);
243
244 struct sway_container *sibling = NULL;
245 struct sway_container *current = container;
246 struct sway_container *parent = current->parent;
247 struct sway_container *top = &root_container;
248
249 // If moving a fullscreen view, only consider outputs
250 if (container->is_fullscreen) {
251 current = container_parent(container, C_OUTPUT);
252 } else if (container_is_fullscreen_or_child(container) ||
253 container_is_floating_or_child(container)) {
254 // If we've fullscreened a split container, only allow the child to move
255 // around within the fullscreen parent.
256 // Same with floating a split container.
257 struct sway_container *ws = container_parent(container, C_WORKSPACE);
258 top = ws->sway_workspace->fullscreen;
259 }
260
261 struct sway_container *new_parent = container_flatten(parent);
262 if (new_parent != parent) {
263 // Special case: we were the last one in this container, so leave
264 return;
265 }
266
267 while (!sibling) {
268 if (current == top) {
269 return;
270 }
271
272 parent = current->parent;
273 wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current,
274 container_type_to_str(current->type), current->name);
275
276 int index = container_sibling_index(current);
277
278 switch (current->type) {
279 case C_OUTPUT: {
280 enum wlr_direction wlr_dir = 0;
281 if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir),
282 "got invalid direction: %d", move_dir)) {
283 return;
284 }
285 double ref_lx = current->x + current->width / 2;
286 double ref_ly = current->y + current->height / 2;
287 struct wlr_output *next = wlr_output_layout_adjacent_output(
288 root_container.sway_root->output_layout, wlr_dir,
289 current->sway_output->wlr_output, ref_lx, ref_ly);
290 if (!next) {
291 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");
292 return;
293 }
294 struct sway_output *next_output = next->data;
295 current = next_output->swayc;
296 wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);
297 // Select workspace and get outta here
298 current = seat_get_focus_inactive(
299 config->handler_context.seat, current);
300 if (current->type != C_WORKSPACE) {
301 current = container_parent(current, C_WORKSPACE);
302 }
303 sibling = current;
304 break;
305 }
306 case C_WORKSPACE:
307 if (!is_parallel(current->layout, move_dir)) {
308 if (current->children->length >= 2) {
309 wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",
310 current->children->length);
311 workspace_rejigger(current, container, move_dir);
312 return;
313 } else {
314 wlr_log(WLR_DEBUG, "Selecting output");
315 current = current->parent;
316 }
317 } else if (current->layout == L_TABBED
318 || current->layout == L_STACKED) {
319 wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");
320 workspace_rejigger(current, container, move_dir);
321 } else {
322 wlr_log(WLR_DEBUG, "Selecting output");
323 current = current->parent;
324 }
325 break;
326 case C_CONTAINER:
327 case C_VIEW:
328 if (is_parallel(parent->layout, move_dir)) {
329 if ((index == parent->children->length - 1 && offs > 0)
330 || (index == 0 && offs < 0)) {
331 if (current->parent == container->parent) {
332 if (!parent->is_fullscreen &&
333 (parent->layout == L_TABBED ||
334 parent->layout == L_STACKED)) {
335 move_out_of_tabs_stacks(container, current,
336 move_dir, offs);
337 return;
338 } else {
339 wlr_log(WLR_DEBUG, "Hit limit, selecting parent");
340 current = current->parent;
341 }
342 } else {
343 wlr_log(WLR_DEBUG, "Hit limit, "
344 "promoting descendant to sibling");
345 // Special case
346 container_insert_child(current->parent, container,
347 index + (offs < 0 ? 0 : 1));
348 container->width = container->height = 0;
349 return;
350 }
351 } else {
352 sibling = parent->children->items[index + offs];
353 wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
354 }
355 } else if (!parent->is_fullscreen && (parent->layout == L_TABBED ||
356 parent->layout == L_STACKED)) {
357 move_out_of_tabs_stacks(container, current, move_dir, offs);
358 return;
359 } else {
360 wlr_log(WLR_DEBUG, "Moving up to find a parallel container");
361 current = current->parent;
362 }
363 break;
364 default:
365 sway_assert(0, "Not expecting to see container of type %s here",
366 container_type_to_str(current->type));
367 return;
368 }
369 }
370
371 // Part two: move stuff around
372 int index = container_sibling_index(container);
373 struct sway_container *old_parent = container->parent;
374
375 while (sibling) {
376 switch (sibling->type) {
377 case C_VIEW:
378 if (sibling->parent == container->parent) {
379 wlr_log(WLR_DEBUG, "Swapping siblings");
380 sibling->parent->children->items[index + offs] = container;
381 sibling->parent->children->items[index] = sibling;
382 } else {
383 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
384 container_insert_child(sibling->parent, container,
385 container_sibling_index(sibling) + (offs > 0 ? 0 : 1));
386 container->width = container->height = 0;
387 }
388 sibling = NULL;
389 break;
390 case C_WORKSPACE: // Note: only in the case of moving between outputs
391 case C_CONTAINER:
392 if (is_parallel(sibling->layout, move_dir)) {
393 int limit = container_limit(sibling, invert_movement(move_dir));
394 wlr_log(WLR_DEBUG, "limit: %d", limit);
395 wlr_log(WLR_DEBUG,
396 "Reparenting container (parallel) to index %d "
397 "(move dir: %d)", limit, move_dir);
398 container_insert_child(sibling, container, limit);
399 container->width = container->height = 0;
400 sibling = NULL;
401 } else {
402 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
403 struct sway_container *focus_inactive = seat_get_focus_inactive(
404 config->handler_context.seat, sibling);
405 if (focus_inactive && focus_inactive != sibling) {
406 while (focus_inactive->parent != sibling) {
407 focus_inactive = focus_inactive->parent;
408 }
409 wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",
410 focus_inactive->id);
411 sibling = focus_inactive;
412 continue;
413 } else if (sibling->children->length) {
414 wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");
415 container_remove_child(container);
416 container_add_sibling(sibling->children->items[0], container);
417 } else {
418 wlr_log(WLR_DEBUG, "No kiddos, adding container alone");
419 container_remove_child(container);
420 container_add_child(sibling, container);
421 }
422 container->width = container->height = 0;
423 sibling = NULL;
424 }
425 break;
426 default:
427 sway_assert(0, "Not expecting to see container of type %s here",
428 container_type_to_str(sibling->type));
429 return;
430 }
431 }
432
433 container_notify_subtree_changed(old_parent);
434 container_notify_subtree_changed(container->parent);
435
436 if (container->type == C_VIEW) {
437 ipc_event_window(container, "move");
438 }
439
440 if (old_parent) {
441 seat_set_focus(config->handler_context.seat, old_parent);
442 seat_set_focus(config->handler_context.seat, container);
443 }
444
445 struct sway_container *last_ws = old_parent;
446 struct sway_container *next_ws = container->parent;
447 if (last_ws && last_ws->type != C_WORKSPACE) {
448 last_ws = container_parent(last_ws, C_WORKSPACE);
449 }
450 if (next_ws && next_ws->type != C_WORKSPACE) {
451 next_ws = container_parent(next_ws, C_WORKSPACE);
452 }
453 if (last_ws && next_ws && last_ws != next_ws) {
454 ipc_event_workspace(last_ws, next_ws, "focus");
455 workspace_detect_urgent(last_ws);
456 workspace_detect_urgent(next_ws);
457 }
458 container_end_mouse_operation(container);
459}
460
55static struct cmd_results *cmd_move_container(struct sway_container *current, 461static struct cmd_results *cmd_move_container(struct sway_container *current,
56 int argc, char **argv) { 462 int argc, char **argv) {
57 struct cmd_results *error = NULL; 463 struct cmd_results *error = NULL;