summaryrefslogtreecommitdiffstats
path: root/sway/layout.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/layout.c')
-rw-r--r--sway/layout.c1770
1 files changed, 0 insertions, 1770 deletions
diff --git a/sway/layout.c b/sway/layout.c
deleted file mode 100644
index 69291daf..00000000
--- a/sway/layout.c
+++ /dev/null
@@ -1,1770 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdbool.h>
4#include <math.h>
5#include <wlc/wlc.h>
6#include "sway/extensions.h"
7#include "sway/config.h"
8#include "sway/container.h"
9#include "sway/workspace.h"
10#include "sway/focus.h"
11#include "sway/output.h"
12#include "sway/ipc-server.h"
13#include "sway/border.h"
14#include "sway/layout.h"
15#include "list.h"
16#include "log.h"
17
18swayc_t root_container;
19swayc_t *current_focus;
20list_t *scratchpad;
21
22int min_sane_h = 60;
23int min_sane_w = 100;
24
25void init_layout(void) {
26 root_container.id = 0; // normally assigned in new_swayc()
27 root_container.type = C_ROOT;
28 root_container.layout = L_NONE;
29 root_container.name = strdup("root");
30 root_container.children = create_list();
31 root_container.handle = -1;
32 root_container.visible = true;
33 current_focus = &root_container;
34 scratchpad = create_list();
35}
36
37int index_child(const swayc_t *child) {
38 swayc_t *parent = child->parent;
39 int i, len;
40 if (!child->is_floating) {
41 len = parent->children->length;
42 for (i = 0; i < len; ++i) {
43 if (parent->children->items[i] == child) {
44 break;
45 }
46 }
47 } else {
48 len = parent->floating->length;
49 for (i = 0; i < len; ++i) {
50 if (parent->floating->items[i] == child) {
51 break;
52 }
53 }
54 }
55 if (!sway_assert(i < len, "Stray container")) {
56 return -1;
57 }
58 return i;
59}
60
61void add_child(swayc_t *parent, swayc_t *child) {
62 sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
63 child->width, child->height, parent, parent->type, parent->width, parent->height);
64 list_add(parent->children, child);
65 child->parent = parent;
66 // set focus for this container
67 if (!parent->focused) {
68 parent->focused = child;
69 }
70 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
71 child = new_container(child, parent->workspace_layout);
72 }
73}
74
75static double *get_height(swayc_t *cont) {
76 return &cont->height;
77}
78
79static double *get_width(swayc_t *cont) {
80 return &cont->width;
81}
82
83void insert_child(swayc_t *parent, swayc_t *child, int index) {
84 if (index > parent->children->length) {
85 index = parent->children->length;
86 }
87 if (index < 0) {
88 index = 0;
89 }
90 list_insert(parent->children, index, child);
91 child->parent = parent;
92 if (!parent->focused) {
93 parent->focused = child;
94 }
95 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
96 child = new_container(child, parent->workspace_layout);
97 }
98 if (is_auto_layout(parent->layout)) {
99 /* go through each group, adjust the size of the first child of each group */
100 double *(*get_maj_dim)(swayc_t *cont);
101 double *(*get_min_dim)(swayc_t *cont);
102 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
103 get_maj_dim = get_width;
104 get_min_dim = get_height;
105 } else {
106 get_maj_dim = get_height;
107 get_min_dim = get_width;
108 }
109 for (int i = index; i < parent->children->length;) {
110 int start = auto_group_start_index(parent, i);
111 int end = auto_group_end_index(parent, i);
112 swayc_t *first = parent->children->items[start];
113 if (start + 1 < parent->children->length) {
114 /* preserve the group's dimension along major axis */
115 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
116 } else {
117 /* new group, let the apply_layout handle it */
118 first->height = first->width = 0;
119 break;
120 }
121 double remaining = *get_min_dim(parent);
122 for (int j = end - 1; j > start; --j) {
123 swayc_t *sibling = parent->children->items[j];
124 if (sibling == child) {
125 /* the inserted child won't yet have its minor
126 dimension set */
127 remaining -= *get_min_dim(parent) / (end - start);
128 } else {
129 remaining -= *get_min_dim(sibling);
130 }
131 }
132 *get_min_dim(first) = remaining;
133 i = end;
134 }
135 }
136}
137
138void add_floating(swayc_t *ws, swayc_t *child) {
139 sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
140 child->width, child->height, ws, ws->type, ws->width, ws->height);
141 if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) {
142 return;
143 }
144 list_add(ws->floating, child);
145 child->parent = ws;
146 child->is_floating = true;
147 if (!ws->focused) {
148 ws->focused = child;
149 }
150 ipc_event_window(child, "floating");
151}
152
153swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
154 swayc_t *parent = fixed->parent;
155 if (fixed->is_floating) {
156 if (active->is_floating) {
157 int i = index_child(fixed);
158 list_insert(parent->floating, i + 1, active);
159 } else {
160 list_add(parent->children, active);
161 }
162 } else {
163 if (active->is_floating) {
164 list_add(parent->floating, active);
165 } else {
166 int i = index_child(fixed);
167 if (is_auto_layout(parent->layout)) {
168 list_add(parent->children, active);
169 } else {
170 list_insert(parent->children, i + 1, active);
171 }
172 }
173 }
174 active->parent = parent;
175 // focus new child
176 parent->focused = active;
177 return active->parent;
178}
179
180swayc_t *replace_child(swayc_t *child, swayc_t *new_child) {
181 swayc_t *parent = child->parent;
182 if (parent == NULL) {
183 return NULL;
184 }
185 int i = index_child(child);
186 if (child->is_floating) {
187 parent->floating->items[i] = new_child;
188 } else {
189 parent->children->items[i] = new_child;
190 }
191 // Set parent and focus for new_child
192 new_child->parent = child->parent;
193 if (child->parent->focused == child) {
194 child->parent->focused = new_child;
195 }
196 child->parent = NULL;
197
198 // Set geometry for new child
199 new_child->x = child->x;
200 new_child->y = child->y;
201 new_child->width = child->width;
202 new_child->height = child->height;
203
204 // reset geometry for child
205 child->width = 0;
206 child->height = 0;
207
208 // deactivate child
209 if (child->type == C_VIEW) {
210 wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
211 }
212 return parent;
213}
214
215swayc_t *remove_child(swayc_t *child) {
216 int i;
217 swayc_t *parent = child->parent;
218 if (child->is_floating) {
219 // Special case for floating views
220 for (i = 0; i < parent->floating->length; ++i) {
221 if (parent->floating->items[i] == child) {
222 list_del(parent->floating, i);
223 break;
224 }
225 }
226 i = 0;
227 } else {
228 for (i = 0; i < parent->children->length; ++i) {
229 if (parent->children->items[i] == child) {
230 list_del(parent->children, i);
231 break;
232 }
233 }
234 if (is_auto_layout(parent->layout) && parent->children->length) {
235 /* go through each group, adjust the size of the last child of each group */
236 double *(*get_maj_dim)(swayc_t *cont);
237 double *(*get_min_dim)(swayc_t *cont);
238 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
239 get_maj_dim = get_width;
240 get_min_dim = get_height;
241 } else {
242 get_maj_dim = get_height;
243 get_min_dim = get_width;
244 }
245 for (int j = parent->children->length - 1; j >= i;) {
246 int start = auto_group_start_index(parent, j);
247 int end = auto_group_end_index(parent, j);
248 swayc_t *first = parent->children->items[start];
249 if (i == start) {
250 /* removed element was first child in the current group,
251 use its size along the major axis */
252 *get_maj_dim(first) = *get_maj_dim(child);
253 } else if (start > i) {
254 /* preserve the group's dimension along major axis */
255 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
256 }
257 if (end != parent->children->length) {
258 double remaining = *get_min_dim(parent);
259 for (int k = start; k < end - 1; ++k) {
260 swayc_t *sibling = parent->children->items[k];
261 remaining -= *get_min_dim(sibling);
262 }
263 /* last element of the group gets remaining size, elements
264 that don't change groups keep their ratio */
265 *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
266 } /* else last group, let apply_layout handle it */
267 j = start - 1;
268 }
269 }
270 }
271 // Set focused to new container
272 if (parent->focused == child) {
273 if (parent->children->length > 0) {
274 parent->focused = parent->children->items[i ? i-1:0];
275 } else if (parent->floating && parent->floating->length) {
276 parent->focused = parent->floating->items[parent->floating->length - 1];
277 } else {
278 parent->focused = NULL;
279 }
280 }
281 child->parent = NULL;
282 // deactivate view
283 if (child->type == C_VIEW) {
284 wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
285 }
286 return parent;
287}
288
289void swap_container(swayc_t *a, swayc_t *b) {
290 if (!sway_assert(a&&b, "parameters must be non null") ||
291 !sway_assert(a->parent && b->parent, "containers must have parents")) {
292 return;
293 }
294 size_t a_index = index_child(a);
295 size_t b_index = index_child(b);
296 swayc_t *a_parent = a->parent;
297 swayc_t *b_parent = b->parent;
298 // Swap the pointers
299 if (a->is_floating) {
300 a_parent->floating->items[a_index] = b;
301 } else {
302 a_parent->children->items[a_index] = b;
303 }
304 if (b->is_floating) {
305 b_parent->floating->items[b_index] = a;
306 } else {
307 b_parent->children->items[b_index] = a;
308 }
309 a->parent = b_parent;
310 b->parent = a_parent;
311 if (a_parent->focused == a) {
312 a_parent->focused = b;
313 }
314 // don't want to double switch
315 if (b_parent->focused == b && a_parent != b_parent) {
316 b_parent->focused = a;
317 }
318}
319
320void swap_geometry(swayc_t *a, swayc_t *b) {
321 double x = a->x;
322 double y = a->y;
323 double w = a->width;
324 double h = a->height;
325 a->x = b->x;
326 a->y = b->y;
327 a->width = b->width;
328 a->height = b->height;
329 b->x = x;
330 b->y = y;
331 b->width = w;
332 b->height = h;
333}
334
335static void swap_children(swayc_t *container, int a, int b) {
336 if (a >= 0 && b >= 0 && a < container->children->length
337 && b < container->children->length
338 && a != b) {
339 swayc_t *pa = (swayc_t *)container->children->items[a];
340 swayc_t *pb = (swayc_t *)container->children->items[b];
341 container->children->items[a] = container->children->items[b];
342 container->children->items[b] = pa;
343 if (is_auto_layout(container->layout)) {
344 size_t ga = auto_group_index(container, a);
345 size_t gb = auto_group_index(container, b);
346 if (ga != gb) {
347 swap_geometry(pa, pb);
348 }
349 }
350 }
351}
352
353void move_container(swayc_t *container, enum movement_direction dir, int move_amt) {
354 enum swayc_layouts layout = L_NONE;
355 swayc_t *parent = container->parent;
356 if (container->is_floating) {
357 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
358 switch(dir) {
359 case MOVE_LEFT:
360 container->x = MAX(0, container->x - move_amt);
361 break;
362 case MOVE_RIGHT:
363 container->x = MIN(output->width - container->width, container->x + move_amt);
364 break;
365 case MOVE_UP:
366 container->y = MAX(0, container->y - move_amt);
367 break;
368 case MOVE_DOWN:
369 container->y = MIN(output->height - container->height, container->y + move_amt);
370 break;
371 default:
372 break;
373 }
374 update_geometry(container);
375 return;
376 }
377 if (container->type != C_VIEW && container->type != C_CONTAINER) {
378 return;
379 }
380 if (dir == MOVE_UP || dir == MOVE_DOWN) {
381 layout = L_VERT;
382 } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
383 layout = L_HORIZ;
384 } else if (dir == MOVE_FIRST) {
385 // swap first child in auto layout with currently focused child
386 if (is_auto_layout(parent->layout)) {
387 int focused_idx = index_child(container);
388 swayc_t *first = parent->children->items[0];
389 if (focused_idx > 0) {
390 list_swap(parent->children, 0, focused_idx);
391 swap_geometry(first, container);
392 }
393 arrange_windows(parent->parent, -1, -1);
394 ipc_event_window(container, "move");
395 set_focused_container_for(parent->parent, container);
396 }
397 return;
398 } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
399 return;
400 }
401 swayc_t *child = container;
402 bool ascended = false;
403
404 // View is wrapped in intermediate container which is needed for displaying
405 // the titlebar. Moving only the view outside of its parent container would just
406 // wrap it again under worspace. There would effectively be no movement,
407 // just a change of wrapping container.
408 if (child->type == C_VIEW &&
409 parent->type == C_CONTAINER &&
410 parent->children->length == 1 &&
411 parent->parent->type == C_WORKSPACE) {
412 child = parent;
413 parent = parent->parent;
414 }
415
416 while (true) {
417 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
418 container,parent,child);
419 if (parent->layout == layout
420 || (layout == L_NONE && (parent->type == C_CONTAINER || parent->type == C_WORKSPACE)) /* accept any layout for next/prev direction */
421 || (parent->layout == L_TABBED && layout == L_HORIZ)
422 || (parent->layout == L_STACKED && layout == L_VERT)
423 || is_auto_layout(parent->layout)) {
424 int diff;
425 // If it has ascended (parent has moved up), no container is removed
426 // so insert it at index, or index+1.
427 // if it has not, the moved container is removed, so it needs to be
428 // inserted at index-1, or index+1
429 if (ascended) {
430 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
431 } else {
432 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
433 }
434 int idx = index_child(child);
435 int desired = idx + diff;
436 if (dir == MOVE_NEXT || dir == MOVE_PREV) {
437 // Next/Prev always wrap.
438 if (desired < 0) {
439 desired += parent->children->length;
440 } else if (desired >= parent->children->length) {
441 desired = 0;
442 }
443 }
444 // when it has ascended, legal insertion position is 0:len
445 // when it has not, legal insertion position is 0:len-1
446 if (desired >= 0 && desired - ascended < parent->children->length) {
447 if (!ascended) {
448 child = parent->children->items[desired];
449 // Move container into sibling container
450 if (child->type == C_CONTAINER) {
451 parent = child;
452 // Insert it in first/last if matching layout, otherwise
453 // insert it next to focused container
454 if (parent->layout == layout
455 || (parent->layout == L_TABBED && layout == L_HORIZ)
456 || (parent->layout == L_STACKED && layout == L_VERT)
457 || is_auto_layout(parent->layout)) {
458 desired = (diff < 0) * parent->children->length;
459 } else {
460 desired = index_child(child->focused) + 1;
461 }
462 //reset geometry
463 container->width = container->height = 0;
464 }
465 }
466 if (container->parent == parent) {
467 swap_children(parent, idx, desired);
468 } else {
469 swayc_t *old_parent = remove_child(container);
470 insert_child(parent, container, desired);
471 destroy_container(old_parent);
472 sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
473 }
474 break;
475 }
476 }
477 // Change parent layout if we need to
478 if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
479 /* swayc_change_layout(parent, layout); */
480 parent->layout = layout;
481 continue;
482 }
483 if (parent->type == C_WORKSPACE) {
484 // If moving to an adjacent output we need a starting position (since this
485 // output might border to multiple outputs).
486 struct wlc_point abs_pos;
487 get_absolute_center_position(container, &abs_pos);
488
489 swayc_t *output = swayc_adjacent_output(parent->parent, dir, &abs_pos, true);
490
491 if (output) {
492 sway_log(L_DEBUG, "Moving between outputs");
493 swayc_t *old_parent = remove_child(container);
494 destroy_container(old_parent);
495
496 swayc_t *dest = output->focused;
497 switch (dir) {
498 case MOVE_LEFT:
499 case MOVE_UP:
500 // reset container geometry
501 container->width = container->height = 0;
502 add_child(dest, container);
503 break;
504 case MOVE_RIGHT:
505 case MOVE_DOWN:
506 // reset container geometry
507 container->width = container->height = 0;
508 insert_child(dest, container, 0);
509 break;
510 default:
511 break;
512 }
513 // arrange new workspace
514 arrange_windows(dest, -1, -1);
515 set_focused_container(container);
516 break;
517 }
518
519 // We simply cannot move any further.
520 if (parent->layout == layout) {
521 break;
522 }
523 // Create container around workspace to insert child into
524 parent = new_container(parent, layout);
525 // Previous line set the resulting container's layout to
526 // workspace_layout. It should have been just layout.
527 parent->layout = parent->parent->layout;
528 }
529 ascended = true;
530 child = parent;
531 parent = child->parent;
532 }
533 arrange_windows(parent->parent, -1, -1);
534 ipc_event_window(container, "move");
535 set_focused_container_for(parent->parent, container);
536}
537
538void move_container_to(swayc_t* container, swayc_t* destination) {
539 if (container == destination || swayc_is_parent_of(container, destination)) {
540 return;
541 }
542 swayc_t *parent = remove_child(container);
543 // Send to new destination
544 if (container->is_floating) {
545 swayc_t *ws = swayc_active_workspace_for(destination);
546 add_floating(ws, container);
547
548 // If the workspace only has one child after adding one, it
549 // means that the workspace was just initialized.
550 if (ws->children->length + ws->floating->length == 1) {
551 ipc_event_workspace(NULL, ws, "init");
552 }
553 } else if (destination->type == C_WORKSPACE) {
554 // reset container geometry
555 container->width = container->height = 0;
556 add_child(destination, container);
557
558 // If the workspace only has one child after adding one, it
559 // means that the workspace was just initialized.
560 if (destination->children->length + destination->floating->length == 1) {
561 ipc_event_workspace(NULL, destination, "init");
562 }
563 } else {
564 // reset container geometry
565 container->width = container->height = 0;
566 add_sibling(destination, container);
567 }
568 // Destroy old container if we need to
569 parent = destroy_container(parent);
570 // Refocus
571 swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT);
572 swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT);
573 set_focused_container(get_focused_view(op1));
574 arrange_windows(op1, -1, -1);
575 update_visibility(op1);
576 if (op1 != op2) {
577 set_focused_container(get_focused_view(op2));
578 arrange_windows(op2, -1, -1);
579 update_visibility(op2);
580 }
581}
582
583void move_workspace_to(swayc_t* workspace, swayc_t* destination) {
584 if (workspace == destination || swayc_is_parent_of(workspace, destination)) {
585 return;
586 }
587 swayc_t *src_op = remove_child(workspace);
588 // reset container geometry
589 workspace->width = workspace->height = 0;
590 add_child(destination, workspace);
591 sort_workspaces(destination);
592 // Refocus destination (change to new workspace)
593 set_focused_container(get_focused_view(workspace));
594 arrange_windows(destination, -1, -1);
595 update_visibility(destination);
596
597 // make sure source output has a workspace
598 if (src_op->children->length == 0) {
599 char *ws_name = workspace_next_name(src_op->name);
600 swayc_t *ws = new_workspace(src_op, ws_name);
601 ws->is_focused = true;
602 free(ws_name);
603 }
604 set_focused_container(get_focused_view(src_op));
605 update_visibility(src_op);
606}
607
608static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
609 const struct wlc_size *res, int left, int right, int top, int bottom) {
610
611 g->size.w += left + right;
612 if (g->origin.x - left < 0) {
613 g->size.w += g->origin.x - left;
614 } else if (g->origin.x + g->size.w - right > res->w) {
615 g->size.w = res->w - g->origin.x + right;
616 }
617
618 g->size.h += top + bottom;
619 if (g->origin.y - top < 0) {
620 g->size.h += g->origin.y - top;
621 } else if (g->origin.y + g->size.h - top > res->h) {
622 g->size.h = res->h - g->origin.y + top;
623 }
624
625 g->origin.x = MIN((uint32_t)MAX(g->origin.x - left, 0), res->w);
626 g->origin.y = MIN((uint32_t)MAX(g->origin.y - top, 0), res->h);
627
628}
629
630static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) {
631 struct wlc_geometry g = *geometry;
632 c->actual_geometry = g;
633
634 swayc_t *output = swayc_parent_by_type(c, C_OUTPUT);
635 struct wlc_size res;
636 output_get_scaled_size(output->handle, &res);
637
638 switch (c->border_type) {
639 case B_NONE:
640 break;
641 case B_PIXEL:
642 adjust_border_geometry(c, &g, &res, c->border_thickness,
643 c->border_thickness, c->border_thickness, c->border_thickness);
644 break;
645 case B_NORMAL:
646 {
647 int title_bar_height = config->font_height + 4; // borders + padding
648
649 adjust_border_geometry(c, &g, &res, c->border_thickness,
650 c->border_thickness, title_bar_height, c->border_thickness);
651
652 struct wlc_geometry title_bar = {
653 .origin = {
654 .x = c->actual_geometry.origin.x - c->border_thickness,
655 .y = c->actual_geometry.origin.y - title_bar_height
656 },
657 .size = {
658 .w = c->actual_geometry.size.w + (2 * c->border_thickness),
659 .h = title_bar_height
660 }
661 };
662 c->title_bar_geometry = title_bar;
663 break;
664 }
665 }
666
667 c->border_geometry = g;
668 *geometry = c->actual_geometry;
669
670 update_container_border(c);
671}
672
673void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
674 switch (parent->layout) {
675 case L_TABBED:
676 case L_STACKED:
677 if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
678 // cache current geometry for all non-float children
679 int i;
680 for (i = 0; i < parent->children->length; ++i) {
681 swayc_t *child = parent->children->items[i];
682 child->cached_geometry.origin.x = child->x;
683 child->cached_geometry.origin.y = child->y;
684 child->cached_geometry.size.w = child->width;
685 child->cached_geometry.size.h = child->height;
686 }
687 }
688 break;
689 default:
690 if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
691 // recover cached geometry for all non-float children
692 int i;
693 for (i = 0; i < parent->children->length; ++i) {
694 swayc_t *child = parent->children->items[i];
695 // only recoverer cached geometry if non-zero
696 if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
697 child->x = child->cached_geometry.origin.x;
698 child->y = child->cached_geometry.origin.y;
699 child->width = child->cached_geometry.size.w;
700 child->height = child->cached_geometry.size.h;
701 }
702 }
703 }
704 break;
705 }
706}
707
708static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
709 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
710 swayc_t *op = ws->parent;
711 int gap = container->is_floating ? 0 : swayc_gap(container);
712 if (gap % 2 != 0) {
713 // because gaps are implemented as "half sized margins" it's currently
714 // not possible to align views properly with odd sized gaps.
715 gap -= 1;
716 }
717
718 g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
719 g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
720 g->size.w = container->width > gap ? container->width - gap : 1;
721 g->size.h = container->height > gap ? container->height - gap : 1;
722
723 if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
724 // Remove gap against the workspace edges. Because a pixel is not
725 // divisable, depending on gap size and the number of siblings our view
726 // might be at the workspace edge without being exactly so (thus test
727 // with gap, and align correctly).
728 if (container->x - gap <= ws->x) {
729 g->origin.x = ws->x;
730 g->size.w = container->width - gap/2;
731 }
732 if (container->y - gap <= ws->y) {
733 g->origin.y = ws->y;
734 g->size.h = container->height - gap/2;
735 }
736 if (container->x + container->width + gap >= ws->x + ws->width) {
737 g->size.w = ws->x + ws->width - g->origin.x;
738 }
739 if (container->y + container->height + gap >= ws->y + ws->height) {
740 g->size.h = ws->y + ws->height - g->origin.y;
741 }
742 }
743
744 return gap;
745}
746
747void update_geometry(swayc_t *container) {
748 if (container->type != C_VIEW && container->type != C_CONTAINER) {
749 return;
750 }
751
752 swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
753 swayc_t *op = workspace->parent;
754 swayc_t *parent = container->parent;
755
756 struct wlc_geometry geometry = {
757 .origin = {
758 .x = container->x < op->width ? container->x : op->width-1,
759 .y = container->y < op->height ? container->y : op->height-1
760 },
761 .size = {
762 .w = container->width,
763 .h = container->height,
764 }
765 };
766
767 int gap = 0;
768
769 // apply inner gaps to non-tabbed/stacked containers
770 swayc_t *p = swayc_tabbed_stacked_ancestor(container);
771 if (p == NULL) {
772 gap = update_gap_geometry(container, &geometry);
773 }
774
775 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
776 struct wlc_size size;
777 output_get_scaled_size(output->handle, &size);
778
779 if (swayc_is_fullscreen(container)) {
780 geometry.origin.x = 0;
781 geometry.origin.y = 0;
782 geometry.size.w = size.w;
783 geometry.size.h = size.h;
784 if (op->focused == workspace) {
785 wlc_view_bring_to_front(container->handle);
786 }
787
788 container->border_geometry = wlc_geometry_zero;
789 container->title_bar_geometry = wlc_geometry_zero;
790 border_clear(container->border);
791 } else if (container->is_floating) { // allocate border for floating window
792 update_border_geometry_floating(container, &geometry);
793 } else if (!container->is_floating) { // allocate border for titled window
794 container->border_geometry = geometry;
795
796 int border_top = container->border_thickness;
797 int border_bottom = container->border_thickness;
798 int border_left = container->border_thickness;
799 int border_right = container->border_thickness;
800
801 // handle hide_edge_borders
802 if (config->hide_edge_borders != E_NONE && (gap <= 0 || (config->smart_gaps && workspace->children->length == 1))) {
803 if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) {
804 if (geometry.origin.x == workspace->x) {
805 border_left = 0;
806 }
807
808 if (geometry.origin.x + geometry.size.w == workspace->x + workspace->width) {
809 border_right = 0;
810 }
811 }
812
813 if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) {
814 if (geometry.origin.y == workspace->y || should_hide_top_border(container, geometry.origin.y)) {
815 border_top = 0;
816 }
817
818 if (geometry.origin.y + geometry.size.h == workspace->y + workspace->height) {
819 border_bottom = 0;
820 }
821 }
822
823 if (config->hide_edge_borders == E_SMART && workspace->children->length == 1) {
824 border_top = 0;
825 border_bottom = 0;
826 border_left = 0;
827 border_right = 0;
828 }
829 }
830
831 int title_bar_height = config->font_height + 4; //borders + padding
832
833 if (parent->layout == L_TABBED && parent->children->length > 1) {
834 int i, x = 0, w, l, r;
835 l = parent->children->length;
836 w = geometry.size.w / l;
837 r = geometry.size.w % l;
838 for (i = 0; i < parent->children->length; ++i) {
839 swayc_t *view = parent->children->items[i];
840 if (view == container) {
841 x = w * i;
842 if (i == l - 1) {
843 w += r;
844 }
845 break;
846 }
847 }
848
849 struct wlc_geometry title_bar = {
850 .origin = {
851 .x = container->border_geometry.origin.x + x,
852 .y = container->border_geometry.origin.y
853 },
854 .size = {
855 .w = w,
856 .h = title_bar_height
857 }
858 };
859 geometry.origin.x += border_left;
860 geometry.origin.y += title_bar.size.h;
861 geometry.size.w -= (border_left + border_right);
862 geometry.size.h -= (border_bottom + title_bar.size.h);
863 container->title_bar_geometry = title_bar;
864 } else if (parent->layout == L_STACKED && parent->children->length > 1) {
865 int i, y = 0;
866 for (i = 0; i < parent->children->length; ++i) {
867 swayc_t *view = parent->children->items[i];
868 if (view == container) {
869 y = title_bar_height * i;
870 }
871 }
872
873 struct wlc_geometry title_bar = {
874 .origin = {
875 .x = container->border_geometry.origin.x,
876 .y = container->border_geometry.origin.y + y
877 },
878 .size = {
879 .w = container->border_geometry.size.w,
880 .h = title_bar_height
881 }
882 };
883 title_bar_height = title_bar_height * parent->children->length;
884 geometry.origin.x += border_left;
885 geometry.origin.y += title_bar_height;
886 geometry.size.w -= (border_left + border_right);
887 geometry.size.h -= (border_bottom + title_bar_height);
888 container->title_bar_geometry = title_bar;
889 } else {
890 switch (container->border_type) {
891 case B_NONE:
892 break;
893 case B_PIXEL:
894 geometry.origin.x += border_left;
895 geometry.origin.y += border_top;
896 geometry.size.w -= (border_left + border_right);
897 geometry.size.h -= (border_top + border_bottom);
898 break;
899 case B_NORMAL:
900 {
901 struct wlc_geometry title_bar = {
902 .origin = {
903 .x = container->border_geometry.origin.x,
904 .y = container->border_geometry.origin.y
905 },
906 .size = {
907 .w = container->border_geometry.size.w,
908 .h = title_bar_height
909 }
910 };
911 geometry.origin.x += border_left;
912 geometry.origin.y += title_bar.size.h;
913 geometry.size.w -= (border_left + border_right);
914 geometry.size.h -= (border_bottom + title_bar.size.h);
915 container->title_bar_geometry = title_bar;
916 break;
917 }
918 }
919 }
920
921 container->actual_geometry = geometry;
922
923 if (container->type == C_VIEW) {
924 update_container_border(container);
925 }
926 }
927
928 if (container->type == C_VIEW) {
929 wlc_view_set_geometry(container->handle, 0, &geometry);
930 }
931}
932
933/**
934 * Layout application prototypes
935 */
936static void apply_horiz_layout(swayc_t *container, const double x,
937 const double y, const double width,
938 const double height, const int start,
939 const int end);
940static void apply_vert_layout(swayc_t *container, const double x,
941 const double y, const double width,
942 const double height, const int start,
943 const int end);
944static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
945 double y, double width,
946 double height);
947
948static void apply_auto_layout(swayc_t *container, const double x, const double y,
949 const double width, const double height,
950 enum swayc_layouts group_layout,
951 bool master_first);
952
953static void arrange_windows_r(swayc_t *container, double width, double height) {
954 int i;
955 if (width == -1 || height == -1) {
956 swayc_log(L_DEBUG, container, "Arranging layout for %p", container);
957 width = container->width;
958 height = container->height;
959 }
960 // pixels are indivisible. if we don't round the pixels, then the view
961 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
962 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
963 width = floor(width);
964 height = floor(height);
965
966 sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
967 container->name, container->width, container->height, container->x,
968 container->y);
969
970 double x = 0, y = 0;
971 switch (container->type) {
972 case C_ROOT:
973 for (i = 0; i < container->children->length; ++i) {
974 swayc_t *output = container->children->items[i];
975 sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y);
976 arrange_windows_r(output, -1, -1);
977 }
978 return;
979 case C_OUTPUT:
980 {
981 struct wlc_size resolution;
982 output_get_scaled_size(container->handle, &resolution);
983 width = resolution.w; height = resolution.h;
984 // output must have correct size due to e.g. seamless mouse,
985 // but a workspace might be smaller depending on panels.
986 container->width = width;
987 container->height = height;
988 }
989 // arrange all workspaces:
990 for (i = 0; i < container->children->length; ++i) {
991 swayc_t *child = container->children->items[i];
992 arrange_windows_r(child, -1, -1);
993 }
994 // Bring all unmanaged views to the front
995 for (i = 0; i < container->unmanaged->length; ++i) {
996 wlc_handle *handle = container->unmanaged->items[i];
997 wlc_view_bring_to_front(*handle);
998 }
999 return;
1000 case C_WORKSPACE:
1001 {
1002 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
1003 width = output->width, height = output->height;
1004 for (i = 0; i < desktop_shell.panels->length; ++i) {
1005 struct panel_config *config = desktop_shell.panels->items[i];
1006 if (config->output == output->handle) {
1007 struct wlc_size size = *wlc_surface_get_size(config->surface);
1008 sway_log(L_DEBUG, "-> Found panel for this workspace: %ux%u, position: %u", size.w, size.h, config->panel_position);
1009 switch (config->panel_position) {
1010 case DESKTOP_SHELL_PANEL_POSITION_TOP:
1011 y += size.h; height -= size.h;
1012 break;
1013 case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
1014 height -= size.h;
1015 break;
1016 case DESKTOP_SHELL_PANEL_POSITION_LEFT:
1017 x += size.w; width -= size.w;
1018 break;
1019 case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
1020 width -= size.w;
1021 break;
1022 }
1023 }
1024 }
1025 int gap = swayc_gap(container);
1026 x = container->x = x + gap;
1027 y = container->y = y + gap;
1028 width = container->width = width - gap * 2;
1029 height = container->height = height - gap * 2;
1030 sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
1031 }
1032 // children are properly handled below
1033 break;
1034 case C_VIEW:
1035 {
1036 container->width = width;
1037 container->height = height;
1038 update_geometry(container);
1039 sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", container->width,
1040 container->height, container->x, container->y);
1041 }
1042 return;
1043 default:
1044 container->width = width;
1045 container->height = height;
1046 x = container->x;
1047 y = container->y;
1048
1049 // add gaps to top level tapped/stacked container
1050 if (container->parent->type == C_WORKSPACE &&
1051 (container->layout == L_TABBED || container->layout == L_STACKED)) {
1052 update_geometry(container);
1053 width = container->border_geometry.size.w;
1054 height = container->border_geometry.size.h;
1055 x = container->border_geometry.origin.x;
1056 y = container->border_geometry.origin.y;
1057 }
1058
1059 // update container size if it's a direct child in a tabbed/stacked layout
1060 // if parent is a workspace, its actual_geometry won't be initialized
1061 if (swayc_tabbed_stacked_parent(container) != NULL &&
1062 container->parent->type != C_WORKSPACE) {
1063 // Use parent actual_geometry as a base for calculating
1064 // container geometry
1065 container->width = container->parent->actual_geometry.size.w;
1066 container->height = container->parent->actual_geometry.size.h;
1067 container->x = container->parent->actual_geometry.origin.x;
1068 container->y = container->parent->actual_geometry.origin.y;
1069
1070 update_geometry(container);
1071 width = container->width = container->actual_geometry.size.w;
1072 height = container->height = container->actual_geometry.size.h;
1073 x = container->x = container->actual_geometry.origin.x;
1074 y = container->y = container->actual_geometry.origin.y;
1075 }
1076
1077 break;
1078 }
1079
1080 switch (container->layout) {
1081 case L_HORIZ:
1082 default:
1083 apply_horiz_layout(container, x, y, width, height, 0,
1084 container->children->length);
1085 break;
1086 case L_VERT:
1087 apply_vert_layout(container, x, y, width, height, 0,
1088 container->children->length);
1089 break;
1090 case L_TABBED:
1091 case L_STACKED:
1092 apply_tabbed_or_stacked_layout(container, x, y, width, height);
1093 break;
1094 case L_AUTO_LEFT:
1095 apply_auto_layout(container, x, y, width, height, L_VERT, true);
1096 break;
1097 case L_AUTO_RIGHT:
1098 apply_auto_layout(container, x, y, width, height, L_VERT, false);
1099 break;
1100 case L_AUTO_TOP:
1101 apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
1102 break;
1103 case L_AUTO_BOTTOM:
1104 apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
1105 break;
1106 }
1107
1108 // Arrage floating layouts for workspaces last
1109 if (container->type == C_WORKSPACE) {
1110 for (int i = 0; i < container->floating->length; ++i) {
1111 swayc_t *view = container->floating->items[i];
1112 if (view->type == C_VIEW) {
1113 update_geometry(view);
1114 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
1115 view->width, view->height, view->x, view->y);
1116 if (swayc_is_fullscreen(view)) {
1117 wlc_view_bring_to_front(view->handle);
1118 } else if (!container->focused ||
1119 !swayc_is_fullscreen(container->focused)) {
1120 wlc_view_bring_to_front(view->handle);
1121 }
1122 }
1123 }
1124 }
1125}
1126
1127void apply_horiz_layout(swayc_t *container, const double x, const double y,
1128 const double width, const double height,
1129 const int start, const int end) {
1130 double scale = 0;
1131 // Calculate total width
1132 for (int i = start; i < end; ++i) {
1133 double *old_width = &((swayc_t *)container->children->items[i])->width;
1134 if (*old_width <= 0) {
1135 if (end - start > 1) {
1136 *old_width = width / (end - start - 1);
1137 } else {
1138 *old_width = width;
1139 }
1140 }
1141 scale += *old_width;
1142 }
1143 scale = width / scale;
1144
1145 // Resize windows
1146 double child_x = x;
1147 if (scale > 0.1) {
1148 sway_log(L_DEBUG, "Arranging %p horizontally", container);
1149 swayc_t *focused = NULL;
1150 for (int i = start; i < end; ++i) {
1151 swayc_t *child = container->children->items[i];
1152 sway_log(L_DEBUG,
1153 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1154 child->type, width, scale);
1155 child->x = child_x;
1156 child->y = y;
1157
1158 if (child == container->focused) {
1159 focused = child;
1160 }
1161
1162 if (i == end - 1) {
1163 double remaining_width = x + width - child_x;
1164 arrange_windows_r(child, remaining_width, height);
1165 } else {
1166 arrange_windows_r(child, child->width * scale, height);
1167 }
1168 child_x += child->width;
1169 }
1170
1171 // update focused view border last because it may
1172 // depend on the title bar geometry of its siblings.
1173 if (focused && container->children->length > 1) {
1174 update_container_border(focused);
1175 }
1176 }
1177}
1178
1179void apply_vert_layout(swayc_t *container, const double x, const double y,
1180 const double width, const double height, const int start,
1181 const int end) {
1182 int i;
1183 double scale = 0;
1184 // Calculate total height
1185 for (i = start; i < end; ++i) {
1186 double *old_height = &((swayc_t *)container->children->items[i])->height;
1187 if (*old_height <= 0) {
1188 if (end - start > 1) {
1189 *old_height = height / (end - start - 1);
1190 } else {
1191 *old_height = height;
1192 }
1193 }
1194 scale += *old_height;
1195 }
1196 scale = height / scale;
1197
1198 // Resize
1199 double child_y = y;
1200 if (scale > 0.1) {
1201 sway_log(L_DEBUG, "Arranging %p vertically", container);
1202 swayc_t *focused = NULL;
1203 for (i = start; i < end; ++i) {
1204 swayc_t *child = container->children->items[i];
1205 sway_log(L_DEBUG,
1206 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1207 child->type, height, scale);
1208 child->x = x;
1209 child->y = child_y;
1210
1211 if (child == container->focused) {
1212 focused = child;
1213 }
1214
1215 if (i == end - 1) {
1216 double remaining_height = y + height - child_y;
1217 arrange_windows_r(child, width, remaining_height);
1218 } else {
1219 arrange_windows_r(child, width, child->height * scale);
1220 }
1221 child_y += child->height;
1222 }
1223
1224 // update focused view border last because it may
1225 // depend on the title bar geometry of its siblings.
1226 if (focused && container->children->length > 1) {
1227 update_container_border(focused);
1228 }
1229 }
1230}
1231
1232void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
1233 double width, double height) {
1234 int i;
1235 swayc_t *focused = NULL;
1236 for (i = 0; i < container->children->length; ++i) {
1237 swayc_t *child = container->children->items[i];
1238 child->x = x;
1239 child->y = y;
1240 if (child == container->focused) {
1241 focused = child;
1242 } else {
1243 arrange_windows_r(child, width, height);
1244 }
1245 }
1246
1247 if (focused) {
1248 arrange_windows_r(focused, width, height);
1249 }
1250}
1251
1252void apply_auto_layout(swayc_t *container, const double x, const double y,
1253 const double width, const double height,
1254 enum swayc_layouts group_layout,
1255 bool master_first) {
1256 // Auto layout "container" in width x height @ x, y
1257 // using "group_layout" for each of the groups in the container.
1258 // There is one "master" group, plus container->nb_slave_groups.
1259 // Each group is layed out side by side following the "major" axis.
1260 // The direction of the layout used for groups is the "minor" axis.
1261 // Example:
1262 //
1263 // ---- major axis -->
1264 // +---------+-----------+
1265 // | | | |
1266 // | master | slave 1 | |
1267 // | +-----------+ | minor axis (direction of group_layout)
1268 // | | | |
1269 // | | slave 2 | V
1270 // +---------+-----------+
1271 //
1272 // container with three children (one master and two slaves) and
1273 // a single slave group (containing slave 1 and 2). The master
1274 // group and slave group are layed out using L_VERT.
1275
1276 size_t nb_groups = auto_group_count(container);
1277
1278 // the target dimension of the container along the "major" axis, each
1279 // group in the container will be layed out using "group_layout" along
1280 // the "minor" axis.
1281 double dim_maj;
1282 double pos_maj;
1283
1284 // x and y coords for the next group to be laid out.
1285 const double *group_x, *group_y;
1286
1287 // pos of the next group to layout along the major axis
1288 double pos;
1289
1290 // size of the next group along the major axis.
1291 double group_dim;
1292
1293 // height and width of next group to be laid out.
1294 const double *group_h, *group_w;
1295
1296 switch (group_layout) {
1297 default:
1298 sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
1299 group_layout, __func__);
1300 /* fall through */
1301 case L_VERT:
1302 dim_maj = width;
1303 pos_maj = x;
1304
1305 group_x = &pos;
1306 group_y = &y;
1307 group_w = &group_dim;
1308 group_h = &height;
1309 break;
1310 case L_HORIZ:
1311 dim_maj = height;
1312 pos_maj = y;
1313
1314 group_x = &x;
1315 group_y = &pos;
1316 group_w = &width;
1317 group_h = &group_dim;
1318 break;
1319 }
1320
1321 /* Determine the dimension of each of the groups in the layout.
1322 * Dimension will be width for a VERT layout and height for a HORIZ
1323 * layout. */
1324 double old_group_dim[nb_groups];
1325 double old_dim = 0;
1326 for (size_t group = 0; group < nb_groups; ++group) {
1327 int idx;
1328 if (auto_group_bounds(container, group, &idx, NULL)) {
1329 swayc_t *child = container->children->items[idx];
1330 double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
1331 if (*dim <= 0) {
1332 // New child with uninitialized dimension
1333 *dim = dim_maj;
1334 if (nb_groups > 1) {
1335 // child gets a dimension proportional to existing groups,
1336 // it will be later scaled based on to the available size
1337 // in the major axis.
1338 *dim /= (nb_groups - 1);
1339 }
1340 }
1341 old_dim += *dim;
1342 old_group_dim[group] = *dim;
1343 }
1344 }
1345 double scale = dim_maj / old_dim;
1346
1347 /* Apply layout to each group */
1348 pos = pos_maj;
1349
1350 for (size_t group = 0; group < nb_groups; ++group) {
1351 int start, end; // index of first (inclusive) and last (exclusive) child in the group
1352 if (auto_group_bounds(container, group, &start, &end)) {
1353 // adjusted size of the group
1354 group_dim = old_group_dim[group] * scale;
1355 if (group == nb_groups - 1) {
1356 group_dim = pos_maj + dim_maj - pos; // remaining width
1357 }
1358 sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
1359 container, group, start, end, *group_w, *group_h, *group_x, *group_y);
1360 switch (group_layout) {
1361 default:
1362 case L_VERT:
1363 apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1364 break;
1365 case L_HORIZ:
1366 apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1367 break;
1368 }
1369
1370 /* update position for next group */
1371 pos += group_dim;
1372 }
1373 }
1374}
1375
1376void arrange_windows(swayc_t *container, double width, double height) {
1377 update_visibility(container);
1378 arrange_windows_r(container, width, height);
1379 layout_log(&root_container, 0);
1380}
1381
1382void arrange_backgrounds(void) {
1383 struct background_config *bg;
1384 for (int i = 0; i < desktop_shell.backgrounds->length; ++i) {
1385 bg = desktop_shell.backgrounds->items[i];
1386 wlc_view_send_to_back(bg->handle);
1387 }
1388}
1389
1390/**
1391 * Get swayc in the direction of newly entered output.
1392 */
1393static swayc_t *get_swayc_in_output_direction(swayc_t *output, enum movement_direction dir) {
1394 if (!output) {
1395 return NULL;
1396 }
1397
1398 swayc_t *ws = swayc_focus_by_type(output, C_WORKSPACE);
1399 if (ws && ws->children->length > 0) {
1400 switch (dir) {
1401 case MOVE_LEFT:
1402 // get most right child of new output
1403 return ws->children->items[ws->children->length-1];
1404 case MOVE_RIGHT:
1405 // get most left child of new output
1406 return ws->children->items[0];
1407 case MOVE_UP:
1408 case MOVE_DOWN:
1409 {
1410 swayc_t *focused_view = swayc_focus_by_type(ws, C_VIEW);
1411 if (focused_view && focused_view->parent) {
1412 swayc_t *parent = focused_view->parent;
1413 if (parent->layout == L_VERT) {
1414 if (dir == MOVE_UP) {
1415 // get child furthest down on new output
1416 return parent->children->items[parent->children->length-1];
1417 } else if (dir == MOVE_DOWN) {
1418 // get child furthest up on new output
1419 return parent->children->items[0];
1420 }
1421 }
1422 return focused_view;
1423 }
1424 break;
1425 }
1426 default:
1427 break;
1428 }
1429 }
1430
1431 return output;
1432}
1433
1434swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit) {
1435 if (dir == MOVE_CHILD) {
1436 return container->focused;
1437 }
1438
1439 swayc_t *parent = container->parent;
1440 if (dir == MOVE_PARENT) {
1441 if (parent->type == C_OUTPUT) {
1442 return NULL;
1443 } else {
1444 return parent;
1445 }
1446 }
1447
1448 if (dir == MOVE_PREV || dir == MOVE_NEXT) {
1449 int focused_idx = index_child(container);
1450 if (focused_idx == -1) {
1451 return NULL;
1452 } else {
1453 int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
1454 parent->children->length;
1455 if (desired < 0) {
1456 desired += parent->children->length;
1457 }
1458 return parent->children->items[desired];
1459 }
1460 }
1461
1462 // If moving to an adjacent output we need a starting position (since this
1463 // output might border to multiple outputs).
1464 struct wlc_point abs_pos;
1465 get_absolute_center_position(container, &abs_pos);
1466
1467 if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
1468 sway_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
1469 container = swayc_parent_by_type(container, C_OUTPUT);
1470 get_absolute_center_position(container, &abs_pos);
1471 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1472 return get_swayc_in_output_direction(output, dir);
1473 }
1474
1475 if (container->type == C_WORKSPACE && container->fullscreen) {
1476 sway_log(L_DEBUG, "Moving to fullscreen view");
1477 return container->fullscreen;
1478 }
1479
1480 swayc_t *wrap_candidate = NULL;
1481 while (true) {
1482 // Test if we can even make a difference here
1483 bool can_move = false;
1484 int desired;
1485 int idx = index_child(container);
1486 if (parent->type == C_ROOT) {
1487 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1488 if (!output || output == container) {
1489 return wrap_candidate;
1490 }
1491 sway_log(L_DEBUG, "Moving between outputs");
1492 return get_swayc_in_output_direction(output, dir);
1493 } else {
1494 if (is_auto_layout(parent->layout)) {
1495 bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
1496 ? dir == MOVE_LEFT || dir == MOVE_RIGHT
1497 : dir == MOVE_DOWN || dir == MOVE_UP;
1498 size_t gidx = auto_group_index(parent, idx);
1499 if (is_major) {
1500 size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1501 can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
1502 } else {
1503 desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1504 int start, end;
1505 can_move = auto_group_bounds(parent, gidx, &start, &end)
1506 && desired >= start && desired < end;
1507 }
1508 } else {
1509 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
1510 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
1511 can_move = true;
1512 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
1513 }
1514 } else {
1515 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
1516 can_move = true;
1517 desired = idx + (dir == MOVE_UP ? -1 : 1);
1518 }
1519 }
1520 }
1521 }
1522
1523 if (can_move) {
1524 if (container->is_floating) {
1525 if (desired < 0) {
1526 wrap_candidate = parent->floating->items[parent->floating->length-1];
1527 } else if (desired >= parent->floating->length){
1528 wrap_candidate = parent->floating->items[0];
1529 } else {
1530 wrap_candidate = parent->floating->items[desired];
1531 }
1532 if (wrap_candidate) {
1533 wlc_view_bring_to_front(wrap_candidate->handle);
1534 }
1535 return wrap_candidate;
1536 } else if (desired < 0 || desired >= parent->children->length) {
1537 can_move = false;
1538 int len = parent->children->length;
1539 if (!wrap_candidate && len > 1) {
1540 if (desired < 0) {
1541 wrap_candidate = parent->children->items[len-1];
1542 } else {
1543 wrap_candidate = parent->children->items[0];
1544 }
1545 if (config->force_focus_wrapping) {
1546 return wrap_candidate;
1547 }
1548 }
1549 } else {
1550 sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
1551 idx, container, dir, desired, parent->children->items[desired]);
1552 return parent->children->items[desired];
1553 }
1554 }
1555 if (!can_move) {
1556 container = parent;
1557 parent = parent->parent;
1558 if (!parent || container == limit) {
1559 // wrapping is the last chance
1560 return wrap_candidate;
1561 }
1562 }
1563 }
1564}
1565
1566swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) {
1567 return get_swayc_in_direction_under(container, dir, NULL);
1568}
1569
1570void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge) {
1571 int i;
1572 bool layout_match = true;
1573 sway_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
1574 if (edge == WLC_RESIZE_EDGE_LEFT || edge == WLC_RESIZE_EDGE_RIGHT) {
1575 container->width += amount;
1576 layout_match = container->layout == L_HORIZ;
1577 } else if (edge == WLC_RESIZE_EDGE_TOP || edge == WLC_RESIZE_EDGE_BOTTOM) {
1578 container->height += amount;
1579 layout_match = container->layout == L_VERT;
1580 }
1581 if (container->type == C_VIEW) {
1582 update_geometry(container);
1583 return;
1584 }
1585 if (layout_match) {
1586 for (i = 0; i < container->children->length; i++) {
1587 recursive_resize(container->children->items[i], amount/container->children->length, edge);
1588 }
1589 } else {
1590 for (i = 0; i < container->children->length; i++) {
1591 recursive_resize(container->children->items[i], amount, edge);
1592 }
1593 }
1594}
1595
1596enum swayc_layouts default_layout(swayc_t *output) {
1597 if (config->default_layout != L_NONE) {
1598 return config->default_layout;
1599 } else if (config->default_orientation != L_NONE) {
1600 return config->default_orientation;
1601 } else if (output->width >= output->height) {
1602 return L_HORIZ;
1603 } else {
1604 return L_VERT;
1605 }
1606}
1607
1608bool is_auto_layout(enum swayc_layouts layout) {
1609 return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
1610}
1611
1612/**
1613 * Return the number of master elements in a container
1614 */
1615static inline size_t auto_master_count(const swayc_t *container) {
1616 sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
1617 container, container->children->length);
1618 return MIN(container->nb_master, (size_t)container->children->length);
1619}
1620
1621/**
1622 * Return the number of children in the slave groups. This corresponds to the children
1623 * that are not members of the master group.
1624 */
1625static inline size_t auto_slave_count(const swayc_t *container) {
1626 return container->children->length - auto_master_count(container);
1627}
1628
1629/**
1630 * Return the number of slave groups in the container.
1631 */
1632size_t auto_slave_group_count(const swayc_t *container) {
1633 return MIN(container->nb_slave_groups, auto_slave_count(container));
1634}
1635
1636/**
1637 * Return the combined number of master and slave groups in the container.
1638 */
1639size_t auto_group_count(const swayc_t *container) {
1640 return auto_slave_group_count(container)
1641 + (container->children->length && container->nb_master ? 1 : 0);
1642}
1643
1644/**
1645 * given the index of a container's child, return the index of the first child of the group
1646 * which index is a member of.
1647 */
1648int auto_group_start_index(const swayc_t *container, int index) {
1649 if (index < 0 || ! is_auto_layout(container->layout)
1650 || (size_t)index < container->nb_master) {
1651 return 0;
1652 } else {
1653 size_t nb_slaves = auto_slave_count(container);
1654 size_t nb_slave_grp = auto_slave_group_count(container);
1655 size_t grp_sz = nb_slaves / nb_slave_grp;
1656 size_t remainder = nb_slaves % nb_slave_grp;
1657 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1658 int start_idx;
1659 if (index < idx2) {
1660 start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
1661 } else {
1662 start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
1663 }
1664 return MIN(start_idx, container->children->length);
1665 }
1666}
1667
1668/**
1669 * given the index of a container's child, return the index of the first child of the group
1670 * that follows the one which index is a member of.
1671 * This makes the function usable to walk through the groups in a container.
1672 */
1673int auto_group_end_index(const swayc_t *container, int index) {
1674 if (index < 0 || ! is_auto_layout(container->layout)) {
1675 return container->children->length;
1676 } else {
1677 int nxt_idx;
1678 if ((size_t)index < container->nb_master) {
1679 nxt_idx = auto_master_count(container);
1680 } else {
1681 size_t nb_slaves = auto_slave_count(container);
1682 size_t nb_slave_grp = auto_slave_group_count(container);
1683 size_t grp_sz = nb_slaves / nb_slave_grp;
1684 size_t remainder = nb_slaves % nb_slave_grp;
1685 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1686 if (index < idx2) {
1687 nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
1688 } else {
1689 nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
1690 }
1691 }
1692 return MIN(nxt_idx, container->children->length);
1693 }
1694}
1695
1696/**
1697 * return the index of the Group containing <index>th child of <container>.
1698 * The index is the order of the group along the container's major axis (starting at 0).
1699 */
1700size_t auto_group_index(const swayc_t *container, int index) {
1701 if (index < 0) {
1702 return 0;
1703 }
1704 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1705 size_t nb_slaves = auto_slave_count(container);
1706 if ((size_t)index < container->nb_master) {
1707 if (master_first || nb_slaves <= 0) {
1708 return 0;
1709 } else {
1710 return auto_slave_group_count(container);
1711 }
1712 } else {
1713 size_t nb_slave_grp = auto_slave_group_count(container);
1714 size_t grp_sz = nb_slaves / nb_slave_grp;
1715 size_t remainder = nb_slaves % nb_slave_grp;
1716 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1717 size_t grp_idx;
1718 if (index < idx2) {
1719 grp_idx = (index - container->nb_master) / grp_sz;
1720 } else {
1721 grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
1722 }
1723 return grp_idx + (master_first && container-> nb_master ? 1 : 0);
1724 }
1725}
1726
1727/**
1728 * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
1729 * an auto layout.
1730 * If the bounds of the given group can be calculated, they are returned in the start/end
1731 * parameters (int pointers) and the return value will be true.
1732 * The indexes are passed by reference and can be NULL.
1733 */
1734bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
1735 size_t nb_grp = auto_group_count(container);
1736 if (group_index >= nb_grp) {
1737 return false;
1738 }
1739 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1740 size_t nb_master = auto_master_count(container);
1741 size_t nb_slave_grp = auto_slave_group_count(container);
1742 int g_start, g_end;
1743 if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
1744 g_start = 0;
1745 g_end = nb_master;
1746 } else {
1747 size_t nb_slaves = auto_slave_count(container);
1748 size_t grp_sz = nb_slaves / nb_slave_grp;
1749 size_t remainder = nb_slaves % nb_slave_grp;
1750 size_t g0 = master_first && container->nb_master ? 1 : 0;
1751 size_t g1 = g0 + nb_slave_grp - remainder;
1752 if (group_index < g1) {
1753 g_start = container->nb_master + (group_index - g0) * grp_sz;
1754 g_end = g_start + grp_sz;
1755 } else {
1756 size_t g2 = group_index - g1;
1757 g_start = container->nb_master
1758 + (nb_slave_grp - remainder) * grp_sz
1759 + g2 * (grp_sz + 1);
1760 g_end = g_start + grp_sz + 1;
1761 }
1762 }
1763 if (start) {
1764 *start = g_start;
1765 }
1766 if (end) {
1767 *end = g_end;
1768 }
1769 return true;
1770}