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