summaryrefslogtreecommitdiffstats
path: root/sway/tree/container.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree/container.c')
-rw-r--r--sway/tree/container.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/sway/tree/container.c b/sway/tree/container.c
new file mode 100644
index 00000000..ea1c93bb
--- /dev/null
+++ b/sway/tree/container.c
@@ -0,0 +1,534 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <stdint.h>
4#include <stdlib.h>
5#include <string.h>
6#include <strings.h>
7#include <wayland-server.h>
8#include <wlr/types/wlr_output_layout.h>
9#include <wlr/types/wlr_wl_shell.h>
10#include "sway/config.h"
11#include "sway/input/input-manager.h"
12#include "sway/input/seat.h"
13#include "sway/ipc-server.h"
14#include "sway/output.h"
15#include "sway/server.h"
16#include "sway/tree/layout.h"
17#include "sway/tree/view.h"
18#include "sway/tree/workspace.h"
19#include "log.h"
20
21static list_t *bfs_queue;
22
23static list_t *get_bfs_queue() {
24 if (!bfs_queue) {
25 bfs_queue = create_list();
26 if (!bfs_queue) {
27 wlr_log(L_ERROR, "could not allocate list for bfs queue");
28 return NULL;
29 }
30 }
31 bfs_queue->length = 0;
32
33 return bfs_queue;
34}
35
36const char *container_type_to_str(enum sway_container_type type) {
37 switch (type) {
38 case C_ROOT:
39 return "C_ROOT";
40 case C_OUTPUT:
41 return "C_OUTPUT";
42 case C_WORKSPACE:
43 return "C_WORKSPACE";
44 case C_CONTAINER:
45 return "C_CONTAINER";
46 case C_VIEW:
47 return "C_VIEW";
48 default:
49 return "C_UNKNOWN";
50 }
51}
52
53void container_create_notify(struct sway_container *container) {
54 // TODO send ipc event type based on the container type
55 wl_signal_emit(&root_container.sway_root->events.new_container, container);
56
57 if (container->type == C_VIEW || container->type == C_CONTAINER) {
58 ipc_event_window(container, "new");
59 }
60}
61
62static void container_close_notify(struct sway_container *container) {
63 if (container == NULL) {
64 return;
65 }
66 // TODO send ipc event type based on the container type
67 if (container->type == C_VIEW || container->type == C_WORKSPACE) {
68 ipc_event_window(container, "close");
69 }
70}
71
72struct sway_container *container_create(enum sway_container_type type) {
73 // next id starts at 1 because 0 is assigned to root_container in layout.c
74 static size_t next_id = 1;
75 struct sway_container *c = calloc(1, sizeof(struct sway_container));
76 if (!c) {
77 return NULL;
78 }
79 c->id = next_id++;
80 c->layout = L_NONE;
81 c->workspace_layout = L_NONE;
82 c->type = type;
83 c->alpha = 1.0f;
84
85 if (type != C_VIEW) {
86 c->children = create_list();
87 }
88
89 wl_signal_init(&c->events.destroy);
90 wl_signal_init(&c->events.reparent);
91
92 return c;
93}
94
95static void _container_destroy(struct sway_container *cont) {
96 if (cont == NULL) {
97 return;
98 }
99
100 wl_signal_emit(&cont->events.destroy, cont);
101 container_close_notify(cont);
102
103 struct sway_container *parent = cont->parent;
104 if (cont->children != NULL && cont->children->length) {
105 // remove children until there are no more, container_destroy calls
106 // container_remove_child, which removes child from this container
107 while (cont->children != NULL) {
108 struct sway_container *child = cont->children->items[0];
109 container_remove_child(child);
110 _container_destroy(child);
111 }
112 }
113 if (cont->marks) {
114 list_foreach(cont->marks, free);
115 list_free(cont->marks);
116 }
117 if (parent) {
118 parent = container_remove_child(cont);
119 }
120 if (cont->name) {
121 free(cont->name);
122 }
123 list_free(cont->children);
124 cont->children = NULL;
125 free(cont);
126}
127
128static struct sway_container *container_output_destroy(
129 struct sway_container *output) {
130 if (!sway_assert(output, "cannot destroy null output")) {
131 return NULL;
132 }
133
134 if (output->children->length > 0) {
135 // TODO save workspaces when there are no outputs.
136 // TODO also check if there will ever be no outputs except for exiting
137 // program
138 if (root_container.children->length > 1) {
139 int p = root_container.children->items[0] == output;
140 // Move workspace from this output to another output
141 while (output->children->length) {
142 struct sway_container *child = output->children->items[0];
143 container_remove_child(child);
144 container_add_child(root_container.children->items[p], child);
145 }
146 container_sort_workspaces(root_container.children->items[p]);
147 arrange_windows(root_container.children->items[p],
148 -1, -1);
149 }
150 }
151
152 wl_list_remove(&output->sway_output->destroy.link);
153 wl_list_remove(&output->sway_output->mode.link);
154 wl_list_remove(&output->sway_output->transform.link);
155 wl_list_remove(&output->sway_output->scale.link);
156
157 wl_list_remove(&output->sway_output->damage_destroy.link);
158 wl_list_remove(&output->sway_output->damage_frame.link);
159
160 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
161 _container_destroy(output);
162 return &root_container;
163}
164
165static struct sway_container *container_workspace_destroy(
166 struct sway_container *workspace) {
167 if (!sway_assert(workspace, "cannot destroy null workspace")) {
168 return NULL;
169 }
170
171 // Do not destroy this if it's the last workspace on this output
172 struct sway_container *output = container_parent(workspace, C_OUTPUT);
173 if (output && output->children->length == 1) {
174 return NULL;
175 }
176
177 struct sway_container *parent = workspace->parent;
178 if (workspace->children->length == 0) {
179 // destroy the WS if there are no children (TODO check for floating)
180 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
181 ipc_event_workspace(workspace, NULL, "empty");
182 } else {
183 // Move children to a different workspace on this output
184 struct sway_container *new_workspace = NULL;
185 // TODO move floating
186 for (int i = 0; i < output->children->length; i++) {
187 if (output->children->items[i] != workspace) {
188 new_workspace = output->children->items[i];
189 break;
190 }
191 }
192
193 wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
194 workspace->name, new_workspace->name);
195 for (int i = 0; i < workspace->children->length; i++) {
196 container_move_to(workspace->children->items[i], new_workspace);
197 }
198 }
199
200 _container_destroy(workspace);
201
202 output_damage_whole(output->sway_output);
203
204 return parent;
205}
206
207static void container_root_finish(struct sway_container *con) {
208 wlr_log(L_ERROR, "TODO: destroy the root container");
209}
210
211bool container_reap_empty(struct sway_container *con) {
212 switch (con->type) {
213 case C_ROOT:
214 case C_OUTPUT:
215 // dont reap these
216 break;
217 case C_WORKSPACE:
218 if (!workspace_is_visible(con) && con->children->length == 0) {
219 wlr_log(L_DEBUG, "Destroying workspace via reaper");
220 container_workspace_destroy(con);
221 return true;
222 }
223 break;
224 case C_CONTAINER:
225 if (con->children->length == 0) {
226 _container_destroy(con);
227 return true;
228 }
229 case C_VIEW:
230 break;
231 case C_TYPES:
232 sway_assert(false, "container_reap_empty called on an invalid "
233 "container");
234 break;
235 }
236
237 return false;
238}
239
240struct sway_container *container_reap_empty_recursive(
241 struct sway_container *con) {
242 while (con) {
243 struct sway_container *next = con->parent;
244 if (!container_reap_empty(con)) {
245 break;
246 }
247 con = next;
248 }
249 return con;
250}
251
252struct sway_container *container_flatten(struct sway_container *container) {
253 while (container->type == C_CONTAINER && container->children->length == 1) {
254 struct sway_container *child = container->children->items[0];
255 struct sway_container *parent = container->parent;
256 container_replace_child(container, child);
257 container_destroy(container);
258 container = parent;
259 }
260 return container;
261}
262
263struct sway_container *container_destroy(struct sway_container *con) {
264 if (con == NULL) {
265 return NULL;
266 }
267
268 struct sway_container *parent = con->parent;
269
270 switch (con->type) {
271 case C_ROOT:
272 container_root_finish(con);
273 break;
274 case C_OUTPUT:
275 // dont try to reap the root after this
276 container_output_destroy(con);
277 break;
278 case C_WORKSPACE:
279 // dont try to reap the output after this
280 container_workspace_destroy(con);
281 break;
282 case C_CONTAINER:
283 if (con->children->length) {
284 for (int i = 0; i < con->children->length; ++i) {
285 struct sway_container *child = con->children->items[0];
286 container_remove_child(child);
287 container_add_child(parent, child);
288 }
289 }
290 _container_destroy(con);
291 break;
292 case C_VIEW:
293 _container_destroy(con);
294 break;
295 case C_TYPES:
296 wlr_log(L_ERROR, "container_destroy called on an invalid "
297 "container");
298 break;
299 }
300
301 return container_reap_empty_recursive(parent);
302}
303
304static void container_close_func(struct sway_container *container, void *data) {
305 if (container->type == C_VIEW) {
306 view_close(container->sway_view);
307 }
308}
309
310struct sway_container *container_close(struct sway_container *con) {
311 if (!sway_assert(con != NULL,
312 "container_close called with a NULL container")) {
313 return NULL;
314 }
315
316 struct sway_container *parent = con->parent;
317
318 if (con->type == C_VIEW) {
319 view_close(con->sway_view);
320 } else {
321 container_for_each_descendant_dfs(con, container_close_func, NULL);
322 }
323
324 return parent;
325}
326
327struct sway_container *container_view_create(struct sway_container *sibling,
328 struct sway_view *sway_view) {
329 if (!sway_assert(sibling,
330 "container_view_create called with NULL sibling/parent")) {
331 return NULL;
332 }
333 const char *title = view_get_title(sway_view);
334 struct sway_container *swayc = container_create(C_VIEW);
335 wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s",
336 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
337 // Setup values
338 swayc->sway_view = sway_view;
339 swayc->name = title ? strdup(title) : NULL;
340 swayc->width = 0;
341 swayc->height = 0;
342
343 if (sibling->type == C_WORKSPACE) {
344 // Case of focused workspace, just create as child of it
345 container_add_child(sibling, swayc);
346 } else {
347 // Regular case, create as sibling of current container
348 container_add_sibling(sibling, swayc);
349 }
350 container_create_notify(swayc);
351 return swayc;
352}
353
354void container_descendants(struct sway_container *root,
355 enum sway_container_type type,
356 void (*func)(struct sway_container *item, void *data), void *data) {
357 for (int i = 0; i < root->children->length; ++i) {
358 struct sway_container *item = root->children->items[i];
359 if (item->type == type) {
360 func(item, data);
361 }
362 if (item->children && item->children->length) {
363 container_descendants(item, type, func, data);
364 }
365 }
366}
367
368struct sway_container *container_find(struct sway_container *container,
369 bool (*test)(struct sway_container *view, void *data), void *data) {
370 if (!container->children) {
371 return NULL;
372 }
373 // TODO: floating windows
374 for (int i = 0; i < container->children->length; ++i) {
375 struct sway_container *child = container->children->items[i];
376 if (test(child, data)) {
377 return child;
378 } else {
379 struct sway_container *res = container_find(child, test, data);
380 if (res) {
381 return res;
382 }
383 }
384 }
385 return NULL;
386}
387
388struct sway_container *container_parent(struct sway_container *container,
389 enum sway_container_type type) {
390 if (!sway_assert(container, "container is NULL")) {
391 return NULL;
392 }
393 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
394 return NULL;
395 }
396 do {
397 container = container->parent;
398 } while (container && container->type != type);
399 return container;
400}
401
402struct sway_container *container_at(struct sway_container *parent,
403 double lx, double ly,
404 struct wlr_surface **surface, double *sx, double *sy) {
405 list_t *queue = get_bfs_queue();
406 if (!queue) {
407 return NULL;
408 }
409
410 list_add(queue, parent);
411
412 struct sway_container *swayc = NULL;
413 while (queue->length) {
414 swayc = queue->items[0];
415 list_del(queue, 0);
416 if (swayc->type == C_VIEW) {
417 struct sway_view *sview = swayc->sway_view;
418 struct sway_container *soutput = container_parent(swayc, C_OUTPUT);
419 struct wlr_box *output_box =
420 wlr_output_layout_get_box(
421 root_container.sway_root->output_layout,
422 soutput->sway_output->wlr_output);
423 double ox = lx - output_box->x;
424 double oy = ly - output_box->y;
425 double view_sx = ox - swayc->x;
426 double view_sy = oy - swayc->y;
427
428 double _sx, _sy;
429 struct wlr_surface *_surface;
430 switch (sview->type) {
431 case SWAY_VIEW_XWAYLAND:
432 _surface = wlr_surface_surface_at(sview->surface,
433 view_sx, view_sy, &_sx, &_sy);
434 break;
435 case SWAY_VIEW_WL_SHELL:
436 _surface = wlr_wl_shell_surface_surface_at(
437 sview->wlr_wl_shell_surface,
438 view_sx, view_sy, &_sx, &_sy);
439 break;
440 case SWAY_VIEW_XDG_SHELL_V6:
441 // the top left corner of the sway container is the
442 // coordinate of the top left corner of the window geometry
443 view_sx += sview->wlr_xdg_surface_v6->geometry.x;
444 view_sy += sview->wlr_xdg_surface_v6->geometry.y;
445
446 _surface = wlr_xdg_surface_v6_surface_at(
447 sview->wlr_xdg_surface_v6,
448 view_sx, view_sy, &_sx, &_sy);
449 break;
450 }
451 if (_surface) {
452 *sx = _sx;
453 *sy = _sy;
454 *surface = _surface;
455 return swayc;
456 }
457 } else {
458 list_cat(queue, swayc->children);
459 }
460 }
461
462 return NULL;
463}
464
465void container_for_each_descendant_dfs(struct sway_container *container,
466 void (*f)(struct sway_container *container, void *data),
467 void *data) {
468 if (container) {
469 if (container->children) {
470 for (int i = 0; i < container->children->length; ++i) {
471 struct sway_container *child =
472 container->children->items[i];
473 container_for_each_descendant_dfs(child, f, data);
474 }
475 }
476 f(container, data);
477 }
478}
479
480void container_for_each_descendant_bfs(struct sway_container *con,
481 void (*f)(struct sway_container *con, void *data), void *data) {
482 list_t *queue = get_bfs_queue();
483 if (!queue) {
484 return;
485 }
486
487 if (queue == NULL) {
488 wlr_log(L_ERROR, "could not allocate list");
489 return;
490 }
491
492 list_add(queue, con);
493
494 struct sway_container *current = NULL;
495 while (queue->length) {
496 current = queue->items[0];
497 list_del(queue, 0);
498 f(current, data);
499 // TODO floating containers
500 list_cat(queue, current->children);
501 }
502}
503
504bool container_has_anscestor(struct sway_container *descendant,
505 struct sway_container *anscestor) {
506 while (descendant->type != C_ROOT) {
507 descendant = descendant->parent;
508 if (descendant == anscestor) {
509 return true;
510 }
511 }
512 return false;
513}
514
515static bool find_child_func(struct sway_container *con, void *data) {
516 struct sway_container *child = data;
517 return con == child;
518}
519
520bool container_has_child(struct sway_container *con,
521 struct sway_container *child) {
522 if (con == NULL || con->type == C_VIEW || con->children->length == 0) {
523 return false;
524 }
525 return container_find(con, find_child_func, child);
526}
527
528void container_damage_whole(struct sway_container *con) {
529 struct sway_container *output = con;
530 if (output->type != C_OUTPUT) {
531 output = container_parent(output, C_OUTPUT);
532 }
533 output_damage_whole_container(output->sway_output, con);
534}