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