aboutsummaryrefslogtreecommitdiffstats
path: root/sway/container.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/container.c')
-rw-r--r--sway/container.c1016
1 files changed, 0 insertions, 1016 deletions
diff --git a/sway/container.c b/sway/container.c
deleted file mode 100644
index 829fde69..00000000
--- a/sway/container.c
+++ /dev/null
@@ -1,1016 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h>
3#include <stdlib.h>
4#include <stdbool.h>
5#include <strings.h>
6#include <string.h>
7#include "sway/config.h"
8#include "sway/container.h"
9#include "sway/workspace.h"
10#include "sway/focus.h"
11#include "sway/border.h"
12#include "sway/layout.h"
13#include "sway/input_state.h"
14#include "sway/ipc-server.h"
15#include "sway/output.h"
16#include "log.h"
17#include "stringop.h"
18
19#define ASSERT_NONNULL(PTR) \
20 sway_assert (PTR, #PTR "must be non-null")
21
22static swayc_t *new_swayc(enum swayc_types type) {
23 // next id starts at 1 because 0 is assigned to root_container in layout.c
24 static size_t next_id = 1;
25 swayc_t *c = calloc(1, sizeof(swayc_t));
26 if (!c) {
27 return NULL;
28 }
29 c->id = next_id++;
30 c->handle = -1;
31 c->gaps = -1;
32 c->layout = L_NONE;
33 c->workspace_layout = L_NONE;
34 c->type = type;
35 c->nb_master = 1;
36 c->nb_slave_groups = 1;
37 if (type != C_VIEW) {
38 c->children = create_list();
39 }
40 return c;
41}
42
43static void free_swayc(swayc_t *cont) {
44 if (!ASSERT_NONNULL(cont)) {
45 return;
46 }
47 if (cont->children) {
48 // remove children until there are no more, free_swayc calls
49 // remove_child, which removes child from this container
50 while (cont->children->length) {
51 free_swayc(cont->children->items[0]);
52 }
53 list_free(cont->children);
54 }
55 if (cont->unmanaged) {
56 list_free(cont->unmanaged);
57 }
58 if (cont->floating) {
59 while (cont->floating->length) {
60 free_swayc(cont->floating->items[0]);
61 }
62 list_free(cont->floating);
63 }
64 if (cont->marks) {
65 list_foreach(cont->marks, free);
66 list_free(cont->marks);
67 }
68 if (cont->parent) {
69 remove_child(cont);
70 }
71 if (cont->name) {
72 free(cont->name);
73 }
74 if (cont->class) {
75 free(cont->class);
76 }
77 if (cont->instance) {
78 free(cont->instance);
79 }
80 if (cont->app_id) {
81 free(cont->app_id);
82 }
83 if (cont->bg_pid != 0) {
84 terminate_swaybg(cont->bg_pid);
85 }
86 if (cont->border) {
87 if (cont->border->buffer) {
88 free(cont->border->buffer);
89 }
90 free(cont->border);
91 }
92 free(cont);
93}
94
95static void update_root_geometry() {
96 int width = 0;
97 int height = 0;
98 swayc_t *child;
99 int child_width;
100 int child_height;
101
102 for (int i = 0; i < root_container.children->length; ++i) {
103 child = root_container.children->items[i];
104 child_width = child->width + child->x;
105 child_height = child->height + child->y;
106 if (child_width > width) {
107 width = child_width;
108 }
109
110 if (child_height > height) {
111 height = child_height;
112 }
113 }
114
115 root_container.width = width;
116 root_container.height = height;
117}
118
119// New containers
120
121swayc_t *new_output(wlc_handle handle) {
122 struct wlc_size size;
123 output_get_scaled_size(handle, &size);
124 const char *name = wlc_output_get_name(handle);
125 // Find current outputs to see if this already exists
126 {
127 int i, len = root_container.children->length;
128 for (i = 0; i < len; ++i) {
129 swayc_t *op = root_container.children->items[i];
130 const char *op_name = op->name;
131 if (op_name && name && strcmp(op_name, name) == 0) {
132 sway_log(L_DEBUG, "restoring output %" PRIuPTR ":%s", handle, op_name);
133 return op;
134 }
135 }
136 }
137
138 sway_log(L_DEBUG, "New output %" PRIuPTR ":%s", handle, name);
139
140 struct output_config *oc = NULL, *all = NULL;
141 int i;
142 for (i = 0; i < config->output_configs->length; ++i) {
143 struct output_config *cur = config->output_configs->items[i];
144 if (strcasecmp(name, cur->name) == 0) {
145 sway_log(L_DEBUG, "Matched output config for %s", name);
146 oc = cur;
147 }
148 if (strcasecmp("*", cur->name) == 0) {
149 sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
150 all = cur;
151 }
152
153 if (oc && all) {
154 break;
155 }
156 }
157
158 if (!oc) {
159 oc = all;
160 }
161
162 if (oc && !oc->enabled) {
163 return NULL;
164 }
165
166 swayc_t *output = new_swayc(C_OUTPUT);
167 output->handle = handle;
168 output->name = name ? strdup(name) : NULL;
169 output->width = size.w;
170 output->height = size.h;
171 output->unmanaged = create_list();
172 output->bg_pid = 0;
173
174 apply_output_config(oc, output);
175 add_child(&root_container, output);
176 load_swaybars();
177
178 // Create workspace
179 char *ws_name = NULL;
180 swayc_t *ws = NULL;
181
182 if (name) {
183 for (i = 0; i < config->workspace_outputs->length; ++i) {
184 struct workspace_output *wso = config->workspace_outputs->items[i];
185 if (strcasecmp(wso->output, name) == 0) {
186 sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output);
187 // Check if any other workspaces are using this name
188 if ((ws = workspace_by_name(wso->workspace))) {
189 // if yes, move those to this output, because they should be here
190 move_workspace_to(ws, output);
191 } else if (!ws_name) {
192 // set a workspace name in case we need to create a default one
193 ws_name = strdup(wso->workspace);
194 }
195 }
196 }
197 }
198
199 if (output->children->length == 0) {
200 if (!ws_name) {
201 ws_name = workspace_next_name(output->name);
202 }
203 // create and initialize default workspace
204 sway_log(L_DEBUG, "Creating default workspace %s", ws_name);
205 ws = new_workspace(output, ws_name);
206 ws->is_focused = true;
207 } else {
208 sort_workspaces(output);
209 set_focused_container(output->children->items[0]);
210 }
211
212 free(ws_name);
213 update_root_geometry();
214 return output;
215}
216
217swayc_t *new_workspace(swayc_t *output, const char *name) {
218 if (!ASSERT_NONNULL(output)) {
219 return NULL;
220 }
221 sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle);
222 swayc_t *workspace = new_swayc(C_WORKSPACE);
223
224 workspace->prev_layout = L_NONE;
225 workspace->layout = default_layout(output);
226 workspace->workspace_layout = default_layout(output);
227
228 workspace->x = output->x;
229 workspace->y = output->y;
230 workspace->width = output->width;
231 workspace->height = output->height;
232 workspace->name = !name ? NULL : strdup(name);
233 workspace->visible = false;
234 workspace->floating = create_list();
235
236 add_child(output, workspace);
237 sort_workspaces(output);
238
239 return workspace;
240}
241
242swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
243 if (!ASSERT_NONNULL(child)
244 && !sway_assert(!child->is_floating, "cannot create container around floating window")) {
245 return NULL;
246 }
247 swayc_t *cont = new_swayc(C_CONTAINER);
248
249 sway_log(L_DEBUG, "creating container %p around %p", cont, child);
250
251 cont->prev_layout = L_NONE;
252 cont->layout = layout;
253 cont->width = child->width;
254 cont->height = child->height;
255 cont->x = child->x;
256 cont->y = child->y;
257 cont->visible = child->visible;
258 cont->cached_geometry = child->cached_geometry;
259 cont->gaps = child->gaps;
260
261 /* Container inherits all of workspaces children, layout and whatnot */
262 if (child->type == C_WORKSPACE) {
263 swayc_t *workspace = child;
264 // reorder focus
265 cont->focused = workspace->focused;
266 workspace->focused = cont;
267 // set all children focu to container
268 int i;
269 for (i = 0; i < workspace->children->length; ++i) {
270 ((swayc_t *)workspace->children->items[i])->parent = cont;
271 }
272 // Swap children
273 list_t *tmp_list = workspace->children;
274 workspace->children = cont->children;
275 cont->children = tmp_list;
276 // add container to workspace chidren
277 add_child(workspace, cont);
278 // give them proper layouts
279 cont->layout = workspace->workspace_layout;
280 cont->prev_layout = workspace->prev_layout;
281 /* TODO: might break shit in move_container!!! workspace->layout = layout; */
282 set_focused_container_for(workspace, get_focused_view(workspace));
283 } else { // Or is built around container
284 swayc_t *parent = replace_child(child, cont);
285 if (parent) {
286 add_child(cont, child);
287 }
288 }
289 return cont;
290}
291
292swayc_t *new_view(swayc_t *sibling, wlc_handle handle) {
293 if (!ASSERT_NONNULL(sibling)) {
294 return NULL;
295 }
296 const char *title = wlc_view_get_title(handle);
297 swayc_t *view = new_swayc(C_VIEW);
298 sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%s to container %p %d",
299 handle, title, sibling, sibling ? sibling->type : 0);
300 // Setup values
301 view->handle = handle;
302 view->name = title ? strdup(title) : NULL;
303 const char *class = wlc_view_get_class(handle);
304 view->class = class ? strdup(class) : NULL;
305 const char *instance = wlc_view_get_instance(handle);
306 view->instance = instance ? strdup(instance) : NULL;
307 const char *app_id = wlc_view_get_app_id(handle);
308 view->app_id = app_id ? strdup(app_id) : NULL;
309 view->visible = true;
310 view->is_focused = true;
311 view->sticky = false;
312 view->width = 0;
313 view->height = 0;
314 view->desired_width = -1;
315 view->desired_height = -1;
316 // setup border
317 view->border_type = config->border;
318 view->border_thickness = config->border_thickness;
319
320 view->is_floating = false;
321
322 if (sibling->type == C_WORKSPACE) {
323 // Case of focused workspace, just create as child of it
324 add_child(sibling, view);
325 } else {
326 // Regular case, create as sibling of current container
327 add_sibling(sibling, view);
328 }
329 return view;
330}
331
332swayc_t *new_floating_view(wlc_handle handle) {
333 if (swayc_active_workspace() == NULL) {
334 return NULL;
335 }
336 const char *title = wlc_view_get_title(handle);
337 swayc_t *view = new_swayc(C_VIEW);
338 sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%x:%s as a floating view",
339 handle, wlc_view_get_type(handle), title);
340 // Setup values
341 view->handle = handle;
342 view->name = title ? strdup(title) : NULL;
343 const char *class = wlc_view_get_class(handle);
344 view->class = class ? strdup(class) : NULL;
345 const char *instance = wlc_view_get_instance(handle);
346 view->instance = instance ? strdup(instance) : NULL;
347 const char *app_id = wlc_view_get_app_id(handle);
348 view->app_id = app_id ? strdup(app_id) : NULL;
349 view->visible = true;
350 view->sticky = false;
351
352 // Set the geometry of the floating view
353 const struct wlc_geometry *geometry = wlc_view_get_geometry(handle);
354
355 // give it requested geometry, but place in center if possible
356 // in top left otherwise
357 if (geometry->size.w != 0) {
358 view->x = (swayc_active_workspace()->width - geometry->size.w) / 2;
359 } else {
360 view->x = 0;
361 }
362 if (geometry->size.h != 0) {
363 view->y = (swayc_active_workspace()->height - geometry->size.h) / 2;
364 } else {
365 view->y = 0;
366 }
367
368 view->width = geometry->size.w;
369 view->height = geometry->size.h;
370
371 view->desired_width = view->width;
372 view->desired_height = view->height;
373
374 // setup border
375 view->border_type = config->floating_border;
376 view->border_thickness = config->floating_border_thickness;
377
378 view->is_floating = true;
379
380 // Case of focused workspace, just create as child of it
381 list_add(swayc_active_workspace()->floating, view);
382 view->parent = swayc_active_workspace();
383 if (swayc_active_workspace()->focused == NULL) {
384 set_focused_container_for(swayc_active_workspace(), view);
385 }
386 return view;
387}
388
389void floating_view_sane_size(swayc_t *view) {
390 // floating_minimum is used as sane value.
391 // floating_maximum has priority in case of conflict
392 // TODO: implement total_outputs_dimensions()
393 if (config->floating_minimum_height != -1 &&
394 view->desired_height < config->floating_minimum_height) {
395 view->desired_height = config->floating_minimum_height;
396 }
397 if (config->floating_minimum_width != -1 &&
398 view->desired_width < config->floating_minimum_width) {
399 view->desired_width = config->floating_minimum_width;
400 }
401
402 // if 0 do not resize, only enforce max value
403 if (config->floating_maximum_height == 0) {
404 // Missing total_outputs_dimensions() using swayc_active_workspace()
405 config->floating_maximum_height = swayc_active_workspace()->height;
406
407 } else if (config->floating_maximum_height != -1 &&
408 view->desired_height > config->floating_maximum_height) {
409 view->desired_height = config->floating_maximum_height;
410 }
411
412 // if 0 do not resize, only enforce max value
413 if (config->floating_maximum_width == 0) {
414 // Missing total_outputs_dimensions() using swayc_active_workspace()
415 config->floating_maximum_width = swayc_active_workspace()->width;
416
417 } else if (config->floating_maximum_width != -1 &&
418 view->desired_width > config->floating_maximum_width) {
419 view->desired_width = config->floating_maximum_width;
420 }
421
422 sway_log(L_DEBUG, "Sane values for view to %d x %d @ %.f, %.f",
423 view->desired_width, view->desired_height, view->x, view->y);
424
425 return;
426}
427
428
429// Destroy container
430
431swayc_t *destroy_output(swayc_t *output) {
432 if (!ASSERT_NONNULL(output)) {
433 return NULL;
434 }
435 if (output->children->length > 0) {
436 // TODO save workspaces when there are no outputs.
437 // TODO also check if there will ever be no outputs except for exiting
438 // program
439 if (root_container.children->length > 1) {
440 int p = root_container.children->items[0] == output;
441 // Move workspace from this output to another output
442 while (output->children->length) {
443 swayc_t *child = output->children->items[0];
444 remove_child(child);
445 add_child(root_container.children->items[p], child);
446 }
447 sort_workspaces(root_container.children->items[p]);
448 update_visibility(root_container.children->items[p]);
449 arrange_windows(root_container.children->items[p], -1, -1);
450 }
451 }
452 sway_log(L_DEBUG, "OUTPUT: Destroying output '%" PRIuPTR "'", output->handle);
453 free_swayc(output);
454 update_root_geometry();
455 return &root_container;
456}
457
458swayc_t *destroy_workspace(swayc_t *workspace) {
459 if (!ASSERT_NONNULL(workspace)) {
460 return NULL;
461 }
462
463 // Do not destroy this if it's the last workspace on this output
464 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
465 if (output && output->children->length == 1) {
466 return NULL;
467 }
468
469 swayc_t *parent = workspace->parent;
470 // destroy the WS if there are no children
471 if (workspace->children->length == 0 && workspace->floating->length == 0) {
472 sway_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
473 ipc_event_workspace(workspace, NULL, "empty");
474 } else {
475 // Move children to a different workspace on this output
476 swayc_t *new_workspace = NULL;
477 int i;
478 for(i = 0; i < output->children->length; i++) {
479 if(output->children->items[i] != workspace) {
480 break;
481 }
482 }
483 new_workspace = output->children->items[i];
484
485 sway_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
486 workspace->name, new_workspace->name);
487
488 for(i = 0; i < workspace->children->length; i++) {
489 move_container_to(workspace->children->items[i], new_workspace);
490 }
491
492 for(i = 0; i < workspace->floating->length; i++) {
493 move_container_to(workspace->floating->items[i], new_workspace);
494 }
495 }
496
497 free_swayc(workspace);
498 return parent;
499}
500
501swayc_t *destroy_container(swayc_t *container) {
502 if (!ASSERT_NONNULL(container)) {
503 return NULL;
504 }
505 while (container->children->length == 0 && container->type == C_CONTAINER) {
506 sway_log(L_DEBUG, "Container: Destroying container '%p'", container);
507 swayc_t *parent = container->parent;
508 free_swayc(container);
509 container = parent;
510 }
511 return container;
512}
513
514swayc_t *destroy_view(swayc_t *view) {
515 if (!ASSERT_NONNULL(view)) {
516 return NULL;
517 }
518 sway_log(L_DEBUG, "Destroying view '%p'", view);
519 swayc_t *parent = view->parent;
520 free_swayc(view);
521
522 // Destroy empty containers
523 if (parent && parent->type == C_CONTAINER) {
524 return destroy_container(parent);
525 }
526 return parent;
527}
528
529// Container lookup
530
531
532swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) {
533 if (!container->children) {
534 return NULL;
535 }
536 // Special case for checking floating stuff
537 int i;
538 if (container->type == C_WORKSPACE) {
539 for (i = 0; i < container->floating->length; ++i) {
540 swayc_t *child = container->floating->items[i];
541 if (test(child, data)) {
542 return child;
543 }
544 }
545 }
546 for (i = 0; i < container->children->length; ++i) {
547 swayc_t *child = container->children->items[i];
548 if (test(child, data)) {
549 return child;
550 } else {
551 swayc_t *res = swayc_by_test(child, test, data);
552 if (res) {
553 return res;
554 }
555 }
556 }
557 return NULL;
558}
559
560static bool test_name(swayc_t *view, void *data) {
561 if (!view || !view->name) {
562 return false;
563 }
564 return strcmp(view->name, data) == 0;
565}
566
567swayc_t *swayc_by_name(const char *name) {
568 return swayc_by_test(&root_container, test_name, (void *)name);
569}
570
571swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
572 if (!ASSERT_NONNULL(container)) {
573 return NULL;
574 }
575 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
576 return NULL;
577 }
578 do {
579 container = container->parent;
580 } while (container && container->type != type);
581 return container;
582}
583
584swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) {
585 if (!ASSERT_NONNULL(container)) {
586 return NULL;
587 }
588 if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
589 return NULL;
590 }
591 do {
592 container = container->parent;
593 } while (container && container->layout != layout);
594 return container;
595}
596
597swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types type) {
598 if (!ASSERT_NONNULL(container)) {
599 return NULL;
600 }
601 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
602 return NULL;
603 }
604 do {
605 container = container->focused;
606 } while (container && container->type != type);
607 return container;
608}
609
610swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) {
611 if (!ASSERT_NONNULL(container)) {
612 return NULL;
613 }
614 if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
615 return NULL;
616 }
617 do {
618 container = container->focused;
619 } while (container && container->layout != layout);
620 return container;
621}
622
623
624static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) {
625 if (!parent || !parent->children) {
626 return NULL;
627 }
628 int i, len;
629 swayc_t **child;
630 if (parent->type == C_WORKSPACE) {
631 len = parent->floating->length;
632 child = (swayc_t **)parent->floating->items;
633 for (i = 0; i < len; ++i, ++child) {
634 if ((*child)->handle == handle) {
635 return *child;
636 }
637 }
638 }
639
640 len = parent->children->length;
641 child = (swayc_t**)parent->children->items;
642 for (i = 0; i < len; ++i, ++child) {
643 if ((*child)->handle == handle) {
644 return *child;
645 } else {
646 swayc_t *res;
647 if ((res = _swayc_by_handle_helper(handle, *child))) {
648 return res;
649 }
650 }
651 }
652 return NULL;
653}
654
655swayc_t *swayc_by_handle(wlc_handle handle) {
656 return _swayc_by_handle_helper(handle, &root_container);
657}
658
659swayc_t *swayc_active_output(void) {
660 return root_container.focused;
661}
662
663swayc_t *swayc_active_workspace(void) {
664 return root_container.focused ? root_container.focused->focused : NULL;
665}
666
667swayc_t *swayc_active_workspace_for(swayc_t *cont) {
668 if (!cont) {
669 return NULL;
670 }
671 switch (cont->type) {
672 case C_ROOT:
673 cont = cont->focused;
674 /* Fallthrough */
675
676 case C_OUTPUT:
677 cont = cont ? cont->focused : NULL;
678 /* Fallthrough */
679
680 case C_WORKSPACE:
681 return cont;
682
683 default:
684 return swayc_parent_by_type(cont, C_WORKSPACE);
685 }
686}
687
688static bool pointer_test(swayc_t *view, void *_origin) {
689 const struct wlc_point *origin = _origin;
690 // Determine the output that the view is under
691 swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT);
692 if (origin->x >= view->x && origin->y >= view->y
693 && origin->x < view->x + view->width && origin->y < view->y + view->height
694 && view->visible && parent == root_container.focused) {
695 return true;
696 }
697 return false;
698}
699
700swayc_t *container_under_pointer(void) {
701 // root.output->workspace
702 if (!root_container.focused) {
703 return NULL;
704 }
705 swayc_t *lookup = root_container.focused;
706 // Case of empty workspace
707 if (lookup->children && !lookup->unmanaged) {
708 return NULL;
709 }
710 double x, y;
711 wlc_pointer_get_position_v2(&x, &y);
712 struct wlc_point origin = { .x = x, .y = y };
713
714 while (lookup && lookup->type != C_VIEW) {
715 int i;
716 int len;
717 for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) {
718 wlc_handle *handle = lookup->unmanaged->items[_i];
719 const struct wlc_geometry *geo = wlc_view_get_geometry(*handle);
720 if (origin.x >= geo->origin.x && origin.y >= geo->origin.y
721 && origin.x < geo->origin.x + (int)geo->size.w
722 && origin.y < geo->origin.y + (int)geo->size.h) {
723 // Hack: we force focus upon unmanaged views here
724 wlc_view_focus(*handle);
725 return NULL;
726 }
727 }
728 // if tabbed/stacked go directly to focused container, otherwise search
729 // children
730 if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) {
731 lookup = lookup->focused;
732 continue;
733 }
734 // if workspace, search floating
735 if (lookup->type == C_WORKSPACE) {
736 i = len = lookup->floating->length;
737 bool got_floating = false;
738 while (--i > -1) {
739 if (pointer_test(lookup->floating->items[i], &origin)) {
740 lookup = lookup->floating->items[i];
741 got_floating = true;
742 break;
743 }
744 }
745 if (got_floating) {
746 continue;
747 }
748 }
749 // search children
750 len = lookup->children->length;
751 for (i = 0; i < len; ++i) {
752 if (pointer_test(lookup->children->items[i], &origin)) {
753 lookup = lookup->children->items[i];
754 break;
755 }
756 }
757 // when border and titles are done, this could happen
758 if (i == len) {
759 break;
760 }
761 }
762 return lookup;
763}
764
765swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data) {
766 if (container->children == NULL || container->children->length == 0) {
767 return NULL;
768 }
769
770 swayc_t *con;
771 if (container->type == C_WORKSPACE) {
772 for (int i = 0; i < container->floating->length; ++i) {
773 con = container->floating->items[i];
774 if (f(con, data)) {
775 return con;
776 }
777 con = container_find(con, f, data);
778 if (con != NULL) {
779 return con;
780 }
781 }
782 }
783
784 for (int i = 0; i < container->children->length; ++i) {
785 con = container->children->items[i];
786 if (f(con, data)) {
787 return con;
788 }
789
790 con = container_find(con, f, data);
791 if (con != NULL) {
792 return con;
793 }
794 }
795
796 return NULL;
797}
798
799// Container information
800
801bool swayc_is_fullscreen(swayc_t *view) {
802 return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN);
803}
804
805bool swayc_is_active(swayc_t *view) {
806 return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED);
807}
808
809bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) {
810 while (child != &root_container) {
811 child = child->parent;
812 if (child == parent) {
813 return true;
814 }
815 }
816 return false;
817}
818
819bool swayc_is_child_of(swayc_t *child, swayc_t *parent) {
820 return swayc_is_parent_of(parent, child);
821}
822
823bool swayc_is_empty_workspace(swayc_t *container) {
824 return container->type == C_WORKSPACE && container->children->length == 0;
825}
826
827int swayc_gap(swayc_t *container) {
828 if (container->type == C_VIEW || container->type == C_CONTAINER) {
829 return container->gaps >= 0 ? container->gaps : config->gaps_inner;
830 } else if (container->type == C_WORKSPACE) {
831 int base = container->gaps >= 0 ? container->gaps : config->gaps_outer;
832 if (config->edge_gaps && !(config->smart_gaps && container->children->length == 1)) {
833 // the inner gap is created via a margin around each window which
834 // is half the gap size, so the workspace also needs half a gap
835 // size to make the outermost gap the same size (excluding the
836 // actual "outer gap" size which is handled independently)
837 return base + config->gaps_inner / 2;
838 } else if (config->smart_gaps && container->children->length == 1) {
839 return 0;
840 } else {
841 return base;
842 }
843 } else {
844 return 0;
845 }
846}
847
848// Mapping
849
850void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) {
851 if (container) {
852 int i;
853 if (container->children) {
854 for (i = 0; i < container->children->length; ++i) {
855 swayc_t *child = container->children->items[i];
856 container_map(child, f, data);
857 }
858 }
859 if (container->floating) {
860 for (i = 0; i < container->floating->length; ++i) {
861 swayc_t *child = container->floating->items[i];
862 container_map(child, f, data);
863 }
864 }
865 f(container, data);
866 }
867}
868
869void update_visibility_output(swayc_t *container, wlc_handle output) {
870 // Inherit visibility
871 swayc_t *parent = container->parent;
872 container->visible = parent->visible;
873 // special cases where visibility depends on focus
874 if (parent->type == C_OUTPUT || parent->layout == L_TABBED ||
875 parent->layout == L_STACKED) {
876 container->visible = parent->focused == container && parent->visible;
877 }
878 // Set visibility and output for view
879 if (container->type == C_VIEW) {
880 wlc_view_set_output(container->handle, output);
881 wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0);
882 }
883 // Update visibility for children
884 else {
885 if (container->children) {
886 int i, len = container->children->length;
887 for (i = 0; i < len; ++i) {
888 update_visibility_output(container->children->items[i], output);
889 }
890 }
891 if (container->floating) {
892 int i, len = container->floating->length;
893 for (i = 0; i < len; ++i) {
894 update_visibility_output(container->floating->items[i], output);
895 }
896 }
897 }
898}
899
900void update_visibility(swayc_t *container) {
901 if (!container) return;
902 switch (container->type) {
903 case C_ROOT:
904 container->visible = true;
905 if (container->children) {
906 int i, len = container->children->length;
907 for (i = 0; i < len; ++i) {
908 update_visibility(container->children->items[i]);
909 }
910 }
911 return;
912
913 case C_OUTPUT:
914 container->visible = true;
915 if (container->children) {
916 int i, len = container->children->length;
917 for (i = 0; i < len; ++i) {
918 update_visibility_output(container->children->items[i], container->handle);
919 }
920 }
921 return;
922
923 default:
924 {
925 swayc_t *op = swayc_parent_by_type(container, C_OUTPUT);
926 update_visibility_output(container, op->handle);
927 }
928 }
929}
930
931void set_gaps(swayc_t *view, void *_data) {
932 int *data = _data;
933 if (!ASSERT_NONNULL(view)) {
934 return;
935 }
936 if (view->type == C_WORKSPACE || view->type == C_VIEW) {
937 view->gaps = *data;
938 }
939}
940
941void add_gaps(swayc_t *view, void *_data) {
942 int *data = _data;
943 if (!ASSERT_NONNULL(view)) {
944 return;
945 }
946 if (view->type == C_WORKSPACE || view->type == C_VIEW) {
947 if ((view->gaps += *data) < 0) {
948 view->gaps = 0;
949 }
950 }
951}
952
953static void close_view(swayc_t *container, void *data) {
954 if (container->type == C_VIEW) {
955 wlc_view_close(container->handle);
956 }
957}
958
959void close_views(swayc_t *container) {
960 container_map(container, close_view, NULL);
961}
962
963swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view) {
964 swayc_t *parent = NULL;
965 if (!ASSERT_NONNULL(view)) {
966 return NULL;
967 }
968 while (view->type != C_WORKSPACE && view->parent && view->parent->type != C_WORKSPACE) {
969 view = view->parent;
970 if (view->layout == L_TABBED || view->layout == L_STACKED) {
971 parent = view;
972 }
973 }
974
975 return parent;
976}
977
978swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
979 if (!ASSERT_NONNULL(con)) {
980 return NULL;
981 }
982 if (con->parent && (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED)) {
983 return con->parent;
984 }
985 return NULL;
986}
987
988swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
989 // if layout change modifies the auto layout's major axis, swap width and height
990 // to preserve current ratios.
991 if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
992 enum swayc_layouts prev_major =
993 container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT
994 ? L_HORIZ : L_VERT;
995 enum swayc_layouts new_major =
996 layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT
997 ? L_HORIZ : L_VERT;
998 if (new_major != prev_major) {
999 for (int i = 0; i < container->children->length; ++i) {
1000 swayc_t *child = container->children->items[i];
1001 double h = child->height;
1002 child->height = child->width;
1003 child->width = h;
1004 }
1005 }
1006 }
1007 if (container->type == C_WORKSPACE) {
1008 container->workspace_layout = layout;
1009 if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
1010 container->layout = layout;
1011 }
1012 } else {
1013 container->layout = layout;
1014 }
1015 return container;
1016}