aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--include/sway/output.h2
-rw-r--r--include/sway/tree/container.h2
-rw-r--r--include/sway/tree/layout.h15
-rw-r--r--sway/commands/focus.c193
-rw-r--r--sway/commands/move.c406
-rw-r--r--sway/commands/sticky.c5
-rw-r--r--sway/tree/container.c5
-rw-r--r--sway/tree/layout.c627
-rw-r--r--sway/tree/output.c13
9 files changed, 637 insertions, 631 deletions
diff --git a/include/sway/output.h b/include/sway/output.h
index 098540fb..651fdfe7 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -45,6 +45,8 @@ void output_destroy(struct sway_container *output);
45 45
46void output_begin_destroy(struct sway_container *output); 46void output_begin_destroy(struct sway_container *output);
47 47
48struct sway_container *output_from_wlr_output(struct wlr_output *output);
49
48typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, 50typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
49 struct wlr_surface *surface, struct wlr_box *box, float rotation, 51 struct wlr_surface *surface, struct wlr_box *box, float rotation,
50 void *user_data); 52 void *user_data);
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 2cb23d3c..2cedb613 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -335,4 +335,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con);
335 335
336void container_discover_outputs(struct sway_container *con); 336void container_discover_outputs(struct sway_container *con);
337 337
338int container_sibling_index(const struct sway_container *child);
339
338#endif 340#endif
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h
index 519189d9..5c834ad2 100644
--- a/include/sway/tree/layout.h
+++ b/include/sway/tree/layout.h
@@ -19,9 +19,15 @@ enum wlr_edges;
19 19
20struct sway_container; 20struct sway_container;
21 21
22void container_handle_fullscreen_reparent(struct sway_container *con,
23 struct sway_container *old_parent);
24
22void container_add_child(struct sway_container *parent, 25void container_add_child(struct sway_container *parent,
23 struct sway_container *child); 26 struct sway_container *child);
24 27
28void container_insert_child(struct sway_container *parent,
29 struct sway_container *child, int i);
30
25struct sway_container *container_add_sibling(struct sway_container *parent, 31struct sway_container *container_add_sibling(struct sway_container *parent,
26 struct sway_container *child); 32 struct sway_container *child);
27 33
@@ -30,18 +36,11 @@ struct sway_container *container_remove_child(struct sway_container *child);
30struct sway_container *container_replace_child(struct sway_container *child, 36struct sway_container *container_replace_child(struct sway_container *child,
31 struct sway_container *new_child); 37 struct sway_container *new_child);
32 38
33void container_move_to(struct sway_container* container, 39bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out);
34 struct sway_container* destination);
35
36void container_move(struct sway_container *container,
37 enum movement_direction dir, int move_amt);
38 40
39enum sway_container_layout container_get_default_layout( 41enum sway_container_layout container_get_default_layout(
40 struct sway_container *con); 42 struct sway_container *con);
41 43
42struct sway_container *container_get_in_direction(struct sway_container
43 *container, struct sway_seat *seat, enum movement_direction dir);
44
45struct sway_container *container_split(struct sway_container *child, 44struct sway_container *container_split(struct sway_container *child,
46 enum sway_container_layout layout); 45 enum sway_container_layout layout);
47 46
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 6659a683..a9fa9a0f 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -31,6 +31,199 @@ static bool parse_movement_direction(const char *name,
31 return true; 31 return true;
32} 32}
33 33
34/**
35 * Get swayc in the direction of newly entered output.
36 */
37static struct sway_container *get_swayc_in_output_direction(
38 struct sway_container *output, enum movement_direction dir,
39 struct sway_seat *seat) {
40 if (!output) {
41 return NULL;
42 }
43
44 struct sway_container *ws = seat_get_focus_inactive(seat, output);
45 if (ws->type != C_WORKSPACE) {
46 ws = container_parent(ws, C_WORKSPACE);
47 }
48
49 if (ws == NULL) {
50 wlr_log(WLR_ERROR, "got an output without a workspace");
51 return NULL;
52 }
53
54 if (ws->children->length > 0) {
55 switch (dir) {
56 case MOVE_LEFT:
57 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
58 // get most right child of new output
59 return ws->children->items[ws->children->length-1];
60 } else {
61 return seat_get_focus_inactive(seat, ws);
62 }
63 case MOVE_RIGHT:
64 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
65 // get most left child of new output
66 return ws->children->items[0];
67 } else {
68 return seat_get_focus_inactive(seat, ws);
69 }
70 case MOVE_UP:
71 case MOVE_DOWN: {
72 struct sway_container *focused =
73 seat_get_focus_inactive(seat, ws);
74 if (focused && focused->parent) {
75 struct sway_container *parent = focused->parent;
76 if (parent->layout == L_VERT) {
77 if (dir == MOVE_UP) {
78 // get child furthest down on new output
79 int idx = parent->children->length - 1;
80 return parent->children->items[idx];
81 } else if (dir == MOVE_DOWN) {
82 // get child furthest up on new output
83 return parent->children->items[0];
84 }
85 }
86 return focused;
87 }
88 break;
89 }
90 default:
91 break;
92 }
93 }
94
95 return ws;
96}
97
98static struct sway_container *container_get_in_direction(
99 struct sway_container *container, struct sway_seat *seat,
100 enum movement_direction dir) {
101 struct sway_container *parent = container->parent;
102
103 if (dir == MOVE_CHILD) {
104 return seat_get_focus_inactive(seat, container);
105 }
106 if (container->is_fullscreen) {
107 if (dir == MOVE_PARENT) {
108 return NULL;
109 }
110 container = container_parent(container, C_OUTPUT);
111 parent = container->parent;
112 } else {
113 if (dir == MOVE_PARENT) {
114 if (parent->type == C_OUTPUT || container_is_floating(container)) {
115 return NULL;
116 } else {
117 return parent;
118 }
119 }
120 }
121
122 struct sway_container *wrap_candidate = NULL;
123 while (true) {
124 bool can_move = false;
125 int desired;
126 int idx = list_find(container->parent->children, container);
127 if (idx == -1) {
128 return NULL;
129 }
130 if (parent->type == C_ROOT) {
131 enum wlr_direction wlr_dir = 0;
132 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
133 "got invalid direction: %d", dir)) {
134 return NULL;
135 }
136 int lx = container->x + container->width / 2;
137 int ly = container->y + container->height / 2;
138 struct wlr_output_layout *layout =
139 root_container.sway_root->output_layout;
140 struct wlr_output *wlr_adjacent =
141 wlr_output_layout_adjacent_output(layout, wlr_dir,
142 container->sway_output->wlr_output, lx, ly);
143 struct sway_container *adjacent =
144 output_from_wlr_output(wlr_adjacent);
145
146 if (!adjacent || adjacent == container) {
147 if (!wrap_candidate) {
148 return NULL;
149 }
150 return seat_get_focus_inactive_view(seat, wrap_candidate);
151 }
152 struct sway_container *next =
153 get_swayc_in_output_direction(adjacent, dir, seat);
154 if (next == NULL) {
155 return NULL;
156 }
157 struct sway_container *next_workspace = next;
158 if (next_workspace->type != C_WORKSPACE) {
159 next_workspace = container_parent(next_workspace, C_WORKSPACE);
160 }
161 sway_assert(next_workspace, "Next container has no workspace");
162 if (next_workspace->sway_workspace->fullscreen) {
163 return seat_get_focus_inactive(seat,
164 next_workspace->sway_workspace->fullscreen);
165 }
166 if (next->children && next->children->length) {
167 // TODO consider floating children as well
168 return seat_get_focus_inactive_view(seat, next);
169 } else {
170 return next;
171 }
172 } else {
173 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
174 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
175 can_move = true;
176 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
177 }
178 } else {
179 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
180 can_move = true;
181 desired = idx + (dir == MOVE_UP ? -1 : 1);
182 }
183 }
184 }
185
186 if (can_move) {
187 // TODO handle floating
188 if (desired < 0 || desired >= parent->children->length) {
189 can_move = false;
190 int len = parent->children->length;
191 if (config->focus_wrapping != WRAP_NO && !wrap_candidate
192 && len > 1) {
193 if (desired < 0) {
194 wrap_candidate = parent->children->items[len-1];
195 } else {
196 wrap_candidate = parent->children->items[0];
197 }
198 if (config->focus_wrapping == WRAP_FORCE) {
199 return seat_get_focus_inactive_view(seat,
200 wrap_candidate);
201 }
202 }
203 } else {
204 struct sway_container *desired_con =
205 parent->children->items[desired];
206 wlr_log(WLR_DEBUG,
207 "cont %d-%p dir %i sibling %d: %p", idx,
208 container, dir, desired, desired_con);
209 return seat_get_focus_inactive_view(seat, desired_con);
210 }
211 }
212
213 if (!can_move) {
214 container = parent;
215 parent = parent->parent;
216 if (!parent) {
217 // wrapping is the last chance
218 if (!wrap_candidate) {
219 return NULL;
220 }
221 return seat_get_focus_inactive_view(seat, wrap_candidate);
222 }
223 }
224 }
225}
226
34static struct cmd_results *focus_mode(struct sway_container *con, 227static struct cmd_results *focus_mode(struct sway_container *con,
35 struct sway_seat *seat, bool floating) { 228 struct sway_seat *seat, bool floating) {
36 struct sway_container *ws = con->type == C_WORKSPACE ? 229 struct sway_container *ws = con->type == C_WORKSPACE ?
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;
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index a0dd7215..72ef4282 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -8,6 +8,7 @@
8#include "sway/tree/container.h" 8#include "sway/tree/container.h"
9#include "sway/tree/layout.h" 9#include "sway/tree/layout.h"
10#include "sway/tree/view.h" 10#include "sway/tree/view.h"
11#include "sway/tree/workspace.h"
11#include "list.h" 12#include "list.h"
12 13
13struct cmd_results *cmd_sticky(int argc, char **argv) { 14struct cmd_results *cmd_sticky(int argc, char **argv) {
@@ -44,7 +45,9 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
44 struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); 45 struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE);
45 struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); 46 struct sway_container *current_workspace = container_parent(container, C_WORKSPACE);
46 if (current_workspace != focused_workspace) { 47 if (current_workspace != focused_workspace) {
47 container_move_to(container, focused_workspace); 48 container_remove_child(container);
49 workspace_add_floating(focused_workspace, container);
50 container_handle_fullscreen_reparent(container, current_workspace);
48 arrange_windows(focused_workspace); 51 arrange_windows(focused_workspace);
49 if (!container_reap_empty(current_workspace)) { 52 if (!container_reap_empty(current_workspace)) {
50 arrange_windows(current_workspace); 53 arrange_windows(current_workspace);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 6da6212c..5721c35c 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1123,3 +1123,8 @@ void container_discover_outputs(struct sway_container *con) {
1123 } 1123 }
1124 } 1124 }
1125} 1125}
1126
1127int container_sibling_index(const struct sway_container *child) {
1128 return list_find(child->parent->children, child);
1129}
1130
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 12e7342b..601d0e91 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -18,11 +18,7 @@
18#include "list.h" 18#include "list.h"
19#include "log.h" 19#include "log.h"
20 20
21static int index_child(const struct sway_container *child) { 21void container_handle_fullscreen_reparent(struct sway_container *con,
22 return list_find(child->parent->children, child);
23}
24
25static void container_handle_fullscreen_reparent(struct sway_container *con,
26 struct sway_container *old_parent) { 22 struct sway_container *old_parent) {
27 if (!con->is_fullscreen) { 23 if (!con->is_fullscreen) {
28 return; 24 return;
@@ -86,7 +82,7 @@ struct sway_container *container_add_sibling(struct sway_container *fixed,
86 container_remove_child(active); 82 container_remove_child(active);
87 } 83 }
88 struct sway_container *parent = fixed->parent; 84 struct sway_container *parent = fixed->parent;
89 int i = index_child(fixed); 85 int i = container_sibling_index(fixed);
90 list_insert(parent->children, i + 1, active); 86 list_insert(parent->children, i + 1, active);
91 active->parent = parent; 87 active->parent = parent;
92 container_handle_fullscreen_reparent(active, old_parent); 88 container_handle_fullscreen_reparent(active, old_parent);
@@ -130,97 +126,7 @@ struct sway_container *container_remove_child(struct sway_container *child) {
130 return parent; 126 return parent;
131} 127}
132 128
133void container_move_to(struct sway_container *container, 129bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out) {
134 struct sway_container *destination) {
135 if (!sway_assert(container->type == C_CONTAINER ||
136 container->type == C_VIEW, "Expected a container or view")) {
137 return;
138 }
139 if (container == destination
140 || container_has_ancestor(container, destination)) {
141 return;
142 }
143 struct sway_container *old_parent = NULL;
144 struct sway_container *new_parent = NULL;
145 if (container_is_floating(container)) {
146 // Resolve destination into a workspace
147 struct sway_container *new_ws = NULL;
148 if (destination->type == C_OUTPUT) {
149 new_ws = output_get_active_workspace(destination->sway_output);
150 } else if (destination->type == C_WORKSPACE) {
151 new_ws = destination;
152 } else {
153 new_ws = container_parent(destination, C_WORKSPACE);
154 }
155 if (!new_ws) {
156 // This can happen if the user has run "move container to mark foo",
157 // where mark foo is on a hidden scratchpad container.
158 return;
159 }
160 struct sway_container *old_output =
161 container_parent(container, C_OUTPUT);
162 old_parent = container_remove_child(container);
163 workspace_add_floating(new_ws, container);
164 container_handle_fullscreen_reparent(container, old_parent);
165 // If changing output, center it within the workspace
166 if (old_output != new_ws->parent && !container->is_fullscreen) {
167 container_floating_move_to_center(container);
168 }
169 } else {
170 old_parent = container_remove_child(container);
171 container->width = container->height = 0;
172 container->saved_width = container->saved_height = 0;
173
174 if (destination->type == C_VIEW) {
175 new_parent = container_add_sibling(destination, container);
176 } else {
177 new_parent = destination;
178 container_add_child(destination, container);
179 }
180 }
181
182 if (container->type == C_VIEW) {
183 ipc_event_window(container, "move");
184 }
185 container_notify_subtree_changed(old_parent);
186 container_notify_subtree_changed(new_parent);
187
188 // If view was moved to a fullscreen workspace, refocus the fullscreen view
189 struct sway_container *new_workspace = container;
190 if (new_workspace->type != C_WORKSPACE) {
191 new_workspace = container_parent(new_workspace, C_WORKSPACE);
192 }
193 if (new_workspace->sway_workspace->fullscreen) {
194 struct sway_seat *seat;
195 struct sway_container *focus, *focus_ws;
196 wl_list_for_each(seat, &input_manager->seats, link) {
197 focus = seat_get_focus(seat);
198 focus_ws = focus;
199 if (focus_ws->type != C_WORKSPACE) {
200 focus_ws = container_parent(focus_ws, C_WORKSPACE);
201 }
202 if (focus_ws == new_workspace) {
203 struct sway_container *new_focus = seat_get_focus_inactive(seat,
204 new_workspace->sway_workspace->fullscreen);
205 seat_set_focus(seat, new_focus);
206 }
207 }
208 }
209 // Update workspace urgent state
210 struct sway_container *old_workspace = old_parent;
211 if (old_workspace->type != C_WORKSPACE) {
212 old_workspace = container_parent(old_workspace, C_WORKSPACE);
213 }
214 if (new_workspace != old_workspace) {
215 workspace_detect_urgent(new_workspace);
216 if (old_workspace) {
217 workspace_detect_urgent(old_workspace);
218 }
219 }
220}
221
222static bool sway_dir_to_wlr(enum movement_direction dir,
223 enum wlr_direction *out) {
224 switch (dir) { 130 switch (dir) {
225 case MOVE_UP: 131 case MOVE_UP:
226 *out = WLR_DIRECTION_UP; 132 *out = WLR_DIRECTION_UP;
@@ -241,323 +147,6 @@ static bool sway_dir_to_wlr(enum movement_direction dir,
241 return true; 147 return true;
242} 148}
243 149
244static bool is_parallel(enum sway_container_layout layout,
245 enum movement_direction dir) {
246 switch (layout) {
247 case L_TABBED:
248 case L_HORIZ:
249 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
250 case L_STACKED:
251 case L_VERT:
252 return dir == MOVE_UP || dir == MOVE_DOWN;
253 default:
254 return false;
255 }
256}
257
258static enum movement_direction invert_movement(enum movement_direction dir) {
259 switch (dir) {
260 case MOVE_LEFT:
261 return MOVE_RIGHT;
262 case MOVE_RIGHT:
263 return MOVE_LEFT;
264 case MOVE_UP:
265 return MOVE_DOWN;
266 case MOVE_DOWN:
267 return MOVE_UP;
268 default:
269 sway_assert(0, "This function expects left|right|up|down");
270 return MOVE_LEFT;
271 }
272}
273
274static int move_offs(enum movement_direction move_dir) {
275 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
276}
277
278/* Gets the index of the most extreme member based on the movement offset */
279static int container_limit(struct sway_container *container,
280 enum movement_direction move_dir) {
281 return move_offs(move_dir) < 0 ? 0 : container->children->length;
282}
283
284/* Takes one child, sets it aside, wraps the rest of the children in a new
285 * container, switches the layout of the workspace, and drops the child back in.
286 * In other words, rejigger it. */
287static void workspace_rejigger(struct sway_container *ws,
288 struct sway_container *child, enum movement_direction move_dir) {
289 struct sway_container *original_parent = child->parent;
290 struct sway_container *new_parent =
291 container_split(ws, ws->layout);
292
293 container_remove_child(child);
294 for (int i = 0; i < ws->children->length; ++i) {
295 struct sway_container *_child = ws->children->items[i];
296 container_move_to(new_parent, _child);
297 }
298
299 int index = move_offs(move_dir);
300 container_insert_child(ws, child, index < 0 ? 0 : 1);
301 ws->layout =
302 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
303
304 container_flatten(ws);
305 container_reap_empty(original_parent);
306 container_create_notify(new_parent);
307}
308
309static void move_out_of_tabs_stacks(struct sway_container *container,
310 struct sway_container *current, enum movement_direction move_dir,
311 int offs) {
312 if (container->parent == current->parent
313 && current->parent->children->length == 1) {
314 wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id);
315 current->parent->layout = move_dir ==
316 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
317 return;
318 }
319
320 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
321 bool is_workspace = current->parent->type == C_WORKSPACE;
322 struct sway_container *new_parent = container_split(current->parent,
323 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT);
324 if (is_workspace) {
325 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1);
326 } else {
327 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
328 container_reap_empty(new_parent->parent);
329 container_flatten(new_parent->parent);
330 }
331 container_create_notify(new_parent);
332 container_notify_subtree_changed(new_parent);
333}
334
335void container_move(struct sway_container *container,
336 enum movement_direction move_dir, int move_amt) {
337 if (!sway_assert(
338 container->type != C_CONTAINER || container->type != C_VIEW,
339 "Can only move containers and views")) {
340 return;
341 }
342 int offs = move_offs(move_dir);
343
344 struct sway_container *sibling = NULL;
345 struct sway_container *current = container;
346 struct sway_container *parent = current->parent;
347 struct sway_container *top = &root_container;
348
349 // If moving a fullscreen view, only consider outputs
350 if (container->is_fullscreen) {
351 current = container_parent(container, C_OUTPUT);
352 } else if (container_is_fullscreen_or_child(container) ||
353 container_is_floating_or_child(container)) {
354 // If we've fullscreened a split container, only allow the child to move
355 // around within the fullscreen parent.
356 // Same with floating a split container.
357 struct sway_container *ws = container_parent(container, C_WORKSPACE);
358 top = ws->sway_workspace->fullscreen;
359 }
360
361 struct sway_container *new_parent = container_flatten(parent);
362 if (new_parent != parent) {
363 // Special case: we were the last one in this container, so leave
364 return;
365 }
366
367 while (!sibling) {
368 if (current == top) {
369 return;
370 }
371
372 parent = current->parent;
373 wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current,
374 container_type_to_str(current->type), current->name);
375
376 int index = index_child(current);
377
378 switch (current->type) {
379 case C_OUTPUT: {
380 enum wlr_direction wlr_dir = 0;
381 if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir),
382 "got invalid direction: %d", move_dir)) {
383 return;
384 }
385 double ref_lx = current->x + current->width / 2;
386 double ref_ly = current->y + current->height / 2;
387 struct wlr_output *next = wlr_output_layout_adjacent_output(
388 root_container.sway_root->output_layout, wlr_dir,
389 current->sway_output->wlr_output, ref_lx, ref_ly);
390 if (!next) {
391 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");
392 return;
393 }
394 struct sway_output *next_output = next->data;
395 current = next_output->swayc;
396 wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);
397 // Select workspace and get outta here
398 current = seat_get_focus_inactive(
399 config->handler_context.seat, current);
400 if (current->type != C_WORKSPACE) {
401 current = container_parent(current, C_WORKSPACE);
402 }
403 sibling = current;
404 break;
405 }
406 case C_WORKSPACE:
407 if (!is_parallel(current->layout, move_dir)) {
408 if (current->children->length >= 2) {
409 wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",
410 current->children->length);
411 workspace_rejigger(current, container, move_dir);
412 return;
413 } else {
414 wlr_log(WLR_DEBUG, "Selecting output");
415 current = current->parent;
416 }
417 } else if (current->layout == L_TABBED
418 || current->layout == L_STACKED) {
419 wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");
420 workspace_rejigger(current, container, move_dir);
421 } else {
422 wlr_log(WLR_DEBUG, "Selecting output");
423 current = current->parent;
424 }
425 break;
426 case C_CONTAINER:
427 case C_VIEW:
428 if (is_parallel(parent->layout, move_dir)) {
429 if ((index == parent->children->length - 1 && offs > 0)
430 || (index == 0 && offs < 0)) {
431 if (current->parent == container->parent) {
432 if (!parent->is_fullscreen &&
433 (parent->layout == L_TABBED ||
434 parent->layout == L_STACKED)) {
435 move_out_of_tabs_stacks(container, current,
436 move_dir, offs);
437 return;
438 } else {
439 wlr_log(WLR_DEBUG, "Hit limit, selecting parent");
440 current = current->parent;
441 }
442 } else {
443 wlr_log(WLR_DEBUG, "Hit limit, "
444 "promoting descendant to sibling");
445 // Special case
446 container_insert_child(current->parent, container,
447 index + (offs < 0 ? 0 : 1));
448 container->width = container->height = 0;
449 return;
450 }
451 } else {
452 sibling = parent->children->items[index + offs];
453 wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
454 }
455 } else if (!parent->is_fullscreen && (parent->layout == L_TABBED ||
456 parent->layout == L_STACKED)) {
457 move_out_of_tabs_stacks(container, current, move_dir, offs);
458 return;
459 } else {
460 wlr_log(WLR_DEBUG, "Moving up to find a parallel container");
461 current = current->parent;
462 }
463 break;
464 default:
465 sway_assert(0, "Not expecting to see container of type %s here",
466 container_type_to_str(current->type));
467 return;
468 }
469 }
470
471 // Part two: move stuff around
472 int index = index_child(container);
473 struct sway_container *old_parent = container->parent;
474
475 while (sibling) {
476 switch (sibling->type) {
477 case C_VIEW:
478 if (sibling->parent == container->parent) {
479 wlr_log(WLR_DEBUG, "Swapping siblings");
480 sibling->parent->children->items[index + offs] = container;
481 sibling->parent->children->items[index] = sibling;
482 } else {
483 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
484 container_insert_child(sibling->parent, container,
485 index_child(sibling) + (offs > 0 ? 0 : 1));
486 container->width = container->height = 0;
487 }
488 sibling = NULL;
489 break;
490 case C_WORKSPACE: // Note: only in the case of moving between outputs
491 case C_CONTAINER:
492 if (is_parallel(sibling->layout, move_dir)) {
493 int limit = container_limit(sibling, invert_movement(move_dir));
494 wlr_log(WLR_DEBUG, "limit: %d", limit);
495 wlr_log(WLR_DEBUG,
496 "Reparenting container (parallel) to index %d "
497 "(move dir: %d)", limit, move_dir);
498 container_insert_child(sibling, container, limit);
499 container->width = container->height = 0;
500 sibling = NULL;
501 } else {
502 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
503 struct sway_container *focus_inactive = seat_get_focus_inactive(
504 config->handler_context.seat, sibling);
505 if (focus_inactive && focus_inactive != sibling) {
506 while (focus_inactive->parent != sibling) {
507 focus_inactive = focus_inactive->parent;
508 }
509 wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",
510 focus_inactive->id);
511 sibling = focus_inactive;
512 continue;
513 } else if (sibling->children->length) {
514 wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");
515 container_remove_child(container);
516 container_add_sibling(sibling->children->items[0], container);
517 } else {
518 wlr_log(WLR_DEBUG, "No kiddos, adding container alone");
519 container_remove_child(container);
520 container_add_child(sibling, container);
521 }
522 container->width = container->height = 0;
523 sibling = NULL;
524 }
525 break;
526 default:
527 sway_assert(0, "Not expecting to see container of type %s here",
528 container_type_to_str(sibling->type));
529 return;
530 }
531 }
532
533 container_notify_subtree_changed(old_parent);
534 container_notify_subtree_changed(container->parent);
535
536 if (container->type == C_VIEW) {
537 ipc_event_window(container, "move");
538 }
539
540 if (old_parent) {
541 seat_set_focus(config->handler_context.seat, old_parent);
542 seat_set_focus(config->handler_context.seat, container);
543 }
544
545 struct sway_container *last_ws = old_parent;
546 struct sway_container *next_ws = container->parent;
547 if (last_ws && last_ws->type != C_WORKSPACE) {
548 last_ws = container_parent(last_ws, C_WORKSPACE);
549 }
550 if (next_ws && next_ws->type != C_WORKSPACE) {
551 next_ws = container_parent(next_ws, C_WORKSPACE);
552 }
553 if (last_ws && next_ws && last_ws != next_ws) {
554 ipc_event_workspace(last_ws, next_ws, "focus");
555 workspace_detect_urgent(last_ws);
556 workspace_detect_urgent(next_ws);
557 }
558 container_end_mouse_operation(container);
559}
560
561enum sway_container_layout container_get_default_layout( 150enum sway_container_layout container_get_default_layout(
562 struct sway_container *con) { 151 struct sway_container *con) {
563 if (con->type != C_OUTPUT) { 152 if (con->type != C_OUTPUT) {
@@ -581,212 +170,6 @@ enum sway_container_layout container_get_default_layout(
581 } 170 }
582} 171}
583 172
584/**
585 * Get swayc in the direction of newly entered output.
586 */
587static struct sway_container *get_swayc_in_output_direction(
588 struct sway_container *output, enum movement_direction dir,
589 struct sway_seat *seat) {
590 if (!output) {
591 return NULL;
592 }
593
594 struct sway_container *ws = seat_get_focus_inactive(seat, output);
595 if (ws->type != C_WORKSPACE) {
596 ws = container_parent(ws, C_WORKSPACE);
597 }
598
599 if (ws == NULL) {
600 wlr_log(WLR_ERROR, "got an output without a workspace");
601 return NULL;
602 }
603
604 if (ws->children->length > 0) {
605 switch (dir) {
606 case MOVE_LEFT:
607 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
608 // get most right child of new output
609 return ws->children->items[ws->children->length-1];
610 } else {
611 return seat_get_focus_inactive(seat, ws);
612 }
613 case MOVE_RIGHT:
614 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
615 // get most left child of new output
616 return ws->children->items[0];
617 } else {
618 return seat_get_focus_inactive(seat, ws);
619 }
620 case MOVE_UP:
621 case MOVE_DOWN: {
622 struct sway_container *focused =
623 seat_get_focus_inactive(seat, ws);
624 if (focused && focused->parent) {
625 struct sway_container *parent = focused->parent;
626 if (parent->layout == L_VERT) {
627 if (dir == MOVE_UP) {
628 // get child furthest down on new output
629 int idx = parent->children->length - 1;
630 return parent->children->items[idx];
631 } else if (dir == MOVE_DOWN) {
632 // get child furthest up on new output
633 return parent->children->items[0];
634 }
635 }
636 return focused;
637 }
638 break;
639 }
640 default:
641 break;
642 }
643 }
644
645 return ws;
646}
647
648static struct sway_container *sway_output_from_wlr(struct wlr_output *output) {
649 if (output == NULL) {
650 return NULL;
651 }
652 for (int i = 0; i < root_container.children->length; ++i) {
653 struct sway_container *o = root_container.children->items[i];
654 if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
655 return o;
656 }
657 }
658 return NULL;
659}
660
661struct sway_container *container_get_in_direction(
662 struct sway_container *container, struct sway_seat *seat,
663 enum movement_direction dir) {
664 struct sway_container *parent = container->parent;
665
666 if (dir == MOVE_CHILD) {
667 return seat_get_focus_inactive(seat, container);
668 }
669 if (container->is_fullscreen) {
670 if (dir == MOVE_PARENT) {
671 return NULL;
672 }
673 container = container_parent(container, C_OUTPUT);
674 parent = container->parent;
675 } else {
676 if (dir == MOVE_PARENT) {
677 if (parent->type == C_OUTPUT || container_is_floating(container)) {
678 return NULL;
679 } else {
680 return parent;
681 }
682 }
683 }
684
685 struct sway_container *wrap_candidate = NULL;
686 while (true) {
687 bool can_move = false;
688 int desired;
689 int idx = index_child(container);
690 if (idx == -1) {
691 return NULL;
692 }
693 if (parent->type == C_ROOT) {
694 enum wlr_direction wlr_dir = 0;
695 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
696 "got invalid direction: %d", dir)) {
697 return NULL;
698 }
699 int lx = container->x + container->width / 2;
700 int ly = container->y + container->height / 2;
701 struct wlr_output_layout *layout =
702 root_container.sway_root->output_layout;
703 struct wlr_output *wlr_adjacent =
704 wlr_output_layout_adjacent_output(layout, wlr_dir,
705 container->sway_output->wlr_output, lx, ly);
706 struct sway_container *adjacent =
707 sway_output_from_wlr(wlr_adjacent);
708
709 if (!adjacent || adjacent == container) {
710 if (!wrap_candidate) {
711 return NULL;
712 }
713 return seat_get_focus_inactive_view(seat, wrap_candidate);
714 }
715 struct sway_container *next =
716 get_swayc_in_output_direction(adjacent, dir, seat);
717 if (next == NULL) {
718 return NULL;
719 }
720 struct sway_container *next_workspace = next;
721 if (next_workspace->type != C_WORKSPACE) {
722 next_workspace = container_parent(next_workspace, C_WORKSPACE);
723 }
724 sway_assert(next_workspace, "Next container has no workspace");
725 if (next_workspace->sway_workspace->fullscreen) {
726 return seat_get_focus_inactive(seat,
727 next_workspace->sway_workspace->fullscreen);
728 }
729 if (next->children && next->children->length) {
730 // TODO consider floating children as well
731 return seat_get_focus_inactive_view(seat, next);
732 } else {
733 return next;
734 }
735 } else {
736 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
737 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
738 can_move = true;
739 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
740 }
741 } else {
742 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
743 can_move = true;
744 desired = idx + (dir == MOVE_UP ? -1 : 1);
745 }
746 }
747 }
748
749 if (can_move) {
750 // TODO handle floating
751 if (desired < 0 || desired >= parent->children->length) {
752 can_move = false;
753 int len = parent->children->length;
754 if (config->focus_wrapping != WRAP_NO && !wrap_candidate
755 && len > 1) {
756 if (desired < 0) {
757 wrap_candidate = parent->children->items[len-1];
758 } else {
759 wrap_candidate = parent->children->items[0];
760 }
761 if (config->focus_wrapping == WRAP_FORCE) {
762 return seat_get_focus_inactive_view(seat,
763 wrap_candidate);
764 }
765 }
766 } else {
767 struct sway_container *desired_con =
768 parent->children->items[desired];
769 wlr_log(WLR_DEBUG,
770 "cont %d-%p dir %i sibling %d: %p", idx,
771 container, dir, desired, desired_con);
772 return seat_get_focus_inactive_view(seat, desired_con);
773 }
774 }
775
776 if (!can_move) {
777 container = parent;
778 parent = parent->parent;
779 if (!parent) {
780 // wrapping is the last chance
781 if (!wrap_candidate) {
782 return NULL;
783 }
784 return seat_get_focus_inactive_view(seat, wrap_candidate);
785 }
786 }
787 }
788}
789
790struct sway_container *container_replace_child(struct sway_container *child, 173struct sway_container *container_replace_child(struct sway_container *child,
791 struct sway_container *new_child) { 174 struct sway_container *new_child) {
792 struct sway_container *parent = child->parent; 175 struct sway_container *parent = child->parent;
@@ -915,8 +298,8 @@ static void swap_places(struct sway_container *con1,
915 con2->width = temp->width; 298 con2->width = temp->width;
916 con2->height = temp->height; 299 con2->height = temp->height;
917 300
918 int temp_index = index_child(con1); 301 int temp_index = container_sibling_index(con1);
919 container_insert_child(con2->parent, con1, index_child(con2)); 302 container_insert_child(con2->parent, con1, container_sibling_index(con2));
920 container_insert_child(temp->parent, con2, temp_index); 303 container_insert_child(temp->parent, con2, temp_index);
921 304
922 free(temp); 305 free(temp);
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 80636c11..bfc9c723 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -209,6 +209,19 @@ void output_begin_destroy(struct sway_container *output) {
209 } 209 }
210} 210}
211 211
212struct sway_container *output_from_wlr_output(struct wlr_output *output) {
213 if (output == NULL) {
214 return NULL;
215 }
216 for (int i = 0; i < root_container.children->length; ++i) {
217 struct sway_container *o = root_container.children->items[i];
218 if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
219 return o;
220 }
221 }
222 return NULL;
223}
224
212void output_for_each_workspace(struct sway_container *output, 225void output_for_each_workspace(struct sway_container *output,
213 void (*f)(struct sway_container *con, void *data), void *data) { 226 void (*f)(struct sway_container *con, void *data), void *data) {
214 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 227 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {