summaryrefslogtreecommitdiffstats
path: root/sway/tree
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/arrange.c25
-rw-r--r--sway/tree/container.c184
-rw-r--r--sway/tree/layout.c2
-rw-r--r--sway/tree/output.c2
-rw-r--r--sway/tree/view.c76
-rw-r--r--sway/tree/workspace.c3
6 files changed, 160 insertions, 132 deletions
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index ac99c5df..cb3f8ba2 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -144,6 +144,19 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {
144 } 144 }
145} 145}
146 146
147/**
148 * If a container has been deleted from the pending tree state, we must add it
149 * to the transaction so it can be freed afterwards. To do this, we iterate the
150 * server's destroying_containers list and add all of them. We may add more than
151 * what we need to, but this is easy and has no negative consequences.
152 */
153static void add_deleted_containers(struct sway_transaction *transaction) {
154 for (int i = 0; i < server.destroying_containers->length; ++i) {
155 struct sway_container *child = server.destroying_containers->items[i];
156 transaction_add_container(transaction, child);
157 }
158}
159
147static void arrange_children_of(struct sway_container *parent, 160static void arrange_children_of(struct sway_container *parent,
148 struct sway_transaction *transaction); 161 struct sway_transaction *transaction);
149 162
@@ -158,6 +171,7 @@ static void arrange_floating(struct sway_container *floating,
158 } 171 }
159 transaction_add_container(transaction, floater); 172 transaction_add_container(transaction, floater);
160 } 173 }
174 transaction_add_container(transaction, floating);
161} 175}
162 176
163static void arrange_children_of(struct sway_container *parent, 177static void arrange_children_of(struct sway_container *parent,
@@ -290,7 +304,16 @@ void arrange_windows(struct sway_container *container,
290 case C_TYPES: 304 case C_TYPES:
291 break; 305 break;
292 } 306 }
293 transaction_add_damage(transaction, container_get_box(container)); 307 // Add damage for whatever container arrange_windows() was called with,
308 // unless it was called with the special floating container, in which case
309 // we'll damage the entire output.
310 if (container->type == C_CONTAINER && container->layout == L_FLOATING) {
311 struct sway_container *output = container_parent(container, C_OUTPUT);
312 transaction_add_damage(transaction, container_get_box(output));
313 } else {
314 transaction_add_damage(transaction, container_get_box(container));
315 }
316 add_deleted_containers(transaction);
294} 317}
295 318
296void arrange_and_commit(struct sway_container *container) { 319void arrange_and_commit(struct sway_container *container) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index b071f394..484d26a5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -16,7 +16,6 @@
16#include "sway/ipc-server.h" 16#include "sway/ipc-server.h"
17#include "sway/output.h" 17#include "sway/output.h"
18#include "sway/server.h" 18#include "sway/server.h"
19#include "sway/tree/arrange.h"
20#include "sway/tree/layout.h" 19#include "sway/tree/layout.h"
21#include "sway/tree/view.h" 20#include "sway/tree/view.h"
22#include "sway/tree/workspace.h" 21#include "sway/tree/workspace.h"
@@ -113,10 +112,11 @@ struct sway_container *container_create(enum sway_container_type type) {
113 c->layout = L_NONE; 112 c->layout = L_NONE;
114 c->type = type; 113 c->type = type;
115 c->alpha = 1.0f; 114 c->alpha = 1.0f;
115 c->instructions = create_list();
116 116
117 if (type != C_VIEW) { 117 if (type != C_VIEW) {
118 c->children = create_list(); 118 c->children = create_list();
119 //c->pending.children = create_list(); 119 c->current.children = create_list();
120 } 120 }
121 121
122 wl_signal_init(&c->events.destroy); 122 wl_signal_init(&c->events.destroy);
@@ -133,43 +133,68 @@ struct sway_container *container_create(enum sway_container_type type) {
133 return c; 133 return c;
134} 134}
135 135
136static void _container_destroy(struct sway_container *cont) { 136static void container_workspace_free(struct sway_workspace *ws) {
137 if (cont == NULL) { 137 list_foreach(ws->output_priority, free);
138 return; 138 list_free(ws->output_priority);
139 } 139 ws->floating->destroying = true;
140 140 container_free(ws->floating);
141 wl_signal_emit(&cont->events.destroy, cont); 141 free(ws);
142}
142 143
143 struct sway_container *parent = cont->parent; 144void container_free(struct sway_container *cont) {
144 if (cont->children != NULL && cont->children->length) { 145 if (!sway_assert(cont->destroying,
145 // remove children until there are no more, container_destroy calls 146 "Tried to free container which wasn't marked as destroying")) {
146 // container_remove_child, which removes child from this container 147 return;
147 while (cont->children != NULL && cont->children->length > 0) {
148 struct sway_container *child = cont->children->items[0];
149 ipc_event_window(child, "close");
150 container_remove_child(child);
151 _container_destroy(child);
152 }
153 }
154 if (cont->marks) {
155 list_foreach(cont->marks, free);
156 list_free(cont->marks);
157 }
158 if (parent) {
159 parent = container_remove_child(cont);
160 } 148 }
161 if (cont->name) { 149 if (!sway_assert(cont->instructions->length == 0,
162 free(cont->name); 150 "Tried to free container with pending instructions")) {
151 return;
163 } 152 }
164 153 free(cont->name);
165 wlr_texture_destroy(cont->title_focused); 154 wlr_texture_destroy(cont->title_focused);
166 wlr_texture_destroy(cont->title_focused_inactive); 155 wlr_texture_destroy(cont->title_focused_inactive);
167 wlr_texture_destroy(cont->title_unfocused); 156 wlr_texture_destroy(cont->title_unfocused);
168 wlr_texture_destroy(cont->title_urgent); 157 wlr_texture_destroy(cont->title_urgent);
169 158
159 for (int i = 0; i < server.destroying_containers->length; ++i) {
160 if (server.destroying_containers->items[i] == cont) {
161 list_del(server.destroying_containers, i);
162 break;
163 }
164 }
165
166 list_free(cont->instructions);
170 list_free(cont->children); 167 list_free(cont->children);
171 //list_free(cont->pending.children); 168 list_free(cont->current.children);
172 cont->children = NULL; 169
170 switch (cont->type) {
171 case C_ROOT:
172 break;
173 case C_OUTPUT:
174 cont->sway_output->swayc = NULL;
175 break;
176 case C_WORKSPACE:
177 container_workspace_free(cont->sway_workspace);
178 break;
179 case C_CONTAINER:
180 break;
181 case C_VIEW:
182 {
183 struct sway_view *view = cont->sway_view;
184 view->swayc = NULL;
185 free(view->title_format);
186 view->title_format = NULL;
187
188 if (view->destroying) {
189 view_free(view);
190 }
191 }
192 break;
193 case C_TYPES:
194 sway_assert(false, "Didn't expect to see C_TYPES here");
195 break;
196 }
197
173 free(cont); 198 free(cont);
174} 199}
175 200
@@ -186,7 +211,6 @@ static struct sway_container *container_workspace_destroy(
186 } 211 }
187 212
188 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); 213 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
189 ipc_event_window(workspace, "close");
190 214
191 struct sway_container *parent = workspace->parent; 215 struct sway_container *parent = workspace->parent;
192 if (!workspace_is_empty(workspace) && output) { 216 if (!workspace_is_empty(workspace) && output) {
@@ -209,25 +233,6 @@ static struct sway_container *container_workspace_destroy(
209 container_move_to(floating->children->items[i], 233 container_move_to(floating->children->items[i],
210 new_workspace->sway_workspace->floating); 234 new_workspace->sway_workspace->floating);
211 } 235 }
212 arrange_and_commit(new_workspace);
213 }
214
215 struct sway_workspace *sway_workspace = workspace->sway_workspace;
216
217 // This emits the destroy event and also destroys the swayc.
218 _container_destroy(workspace);
219
220 // Clean up the floating container
221 sway_workspace->floating->parent = NULL;
222 _container_destroy(sway_workspace->floating);
223
224 list_foreach(sway_workspace->output_priority, free);
225 list_free(sway_workspace->output_priority);
226
227 free(sway_workspace);
228
229 if (output) {
230 output_damage_whole(output->sway_output);
231 } 236 }
232 237
233 return parent; 238 return parent;
@@ -266,14 +271,13 @@ static struct sway_container *container_output_destroy(
266 container_add_child(new_output, workspace); 271 container_add_child(new_output, workspace);
267 ipc_event_workspace(workspace, NULL, "move"); 272 ipc_event_workspace(workspace, NULL, "move");
268 } else { 273 } else {
269 container_workspace_destroy(workspace); 274 container_destroy(workspace);
270 } 275 }
271 276
272 container_sort_workspaces(new_output); 277 container_sort_workspaces(new_output);
273 } 278 }
274 } 279 }
275 } 280 }
276 arrange_and_commit(&root_container);
277 281
278 wl_list_remove(&output->sway_output->mode.link); 282 wl_list_remove(&output->sway_output->mode.link);
279 wl_list_remove(&output->sway_output->transform.link); 283 wl_list_remove(&output->sway_output->transform.link);
@@ -285,12 +289,8 @@ static struct sway_container *container_output_destroy(
285 output->sway_output->swayc = NULL; 289 output->sway_output->swayc = NULL;
286 290
287 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 291 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
288 _container_destroy(output);
289 return &root_container;
290}
291 292
292static void container_root_finish(struct sway_container *con) { 293 return &root_container;
293 wlr_log(L_ERROR, "TODO: destroy the root container");
294} 294}
295 295
296bool container_reap_empty(struct sway_container *con) { 296bool container_reap_empty(struct sway_container *con) {
@@ -306,13 +306,13 @@ bool container_reap_empty(struct sway_container *con) {
306 case C_WORKSPACE: 306 case C_WORKSPACE:
307 if (!workspace_is_visible(con) && workspace_is_empty(con)) { 307 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
308 wlr_log(L_DEBUG, "Destroying workspace via reaper"); 308 wlr_log(L_DEBUG, "Destroying workspace via reaper");
309 container_workspace_destroy(con); 309 container_destroy(con);
310 return true; 310 return true;
311 } 311 }
312 break; 312 break;
313 case C_CONTAINER: 313 case C_CONTAINER:
314 if (con->children->length == 0) { 314 if (con->children->length == 0) {
315 _container_destroy(con); 315 container_destroy(con);
316 return true; 316 return true;
317 } 317 }
318 case C_VIEW: 318 case C_VIEW:
@@ -349,46 +349,48 @@ struct sway_container *container_flatten(struct sway_container *container) {
349 return container; 349 return container;
350} 350}
351 351
352/**
353 * container_destroy() is the first step in destroying a container. We'll emit
354 * events, detach it from the tree and mark it as destroying. The container will
355 * remain in memory until it's no longer used by a transaction, then it will be
356 * freed via container_free().
357 */
352struct sway_container *container_destroy(struct sway_container *con) { 358struct sway_container *container_destroy(struct sway_container *con) {
353 if (con == NULL) { 359 if (con == NULL) {
354 return NULL; 360 return NULL;
355 } 361 }
362 if (con->destroying) {
363 return NULL;
364 }
356 365
357 struct sway_container *parent = con->parent; 366 // The below functions move their children to somewhere else.
367 if (con->type == C_OUTPUT) {
368 container_output_destroy(con);
369 } else if (con->type == C_WORKSPACE) {
370 // Workspaces will refuse to be destroyed if they're the last workspace
371 // on their output.
372 if (!container_workspace_destroy(con)) {
373 return NULL;
374 }
375 }
358 376
359 switch (con->type) { 377 // At this point the container being destroyed shouldn't have any children
360 case C_ROOT: 378 // unless sway is terminating.
361 container_root_finish(con); 379 if (!server.terminating) {
362 break; 380 if (!sway_assert(!con->children || con->children->length == 0,
363 case C_OUTPUT: 381 "Didn't expect to see children here")) {
364 // dont try to reap the root after this 382 return NULL;
365 container_output_destroy(con); 383 }
366 break;
367 case C_WORKSPACE:
368 // dont try to reap the output after this
369 container_workspace_destroy(con);
370 break;
371 case C_CONTAINER:
372 if (con->children->length) {
373 for (int i = 0; i < con->children->length; ++i) {
374 struct sway_container *child = con->children->items[0];
375 ipc_event_window(child, "close");
376 container_remove_child(child);
377 container_add_child(parent, child);
378 }
379 }
380 ipc_event_window(con, "close");
381 _container_destroy(con);
382 break;
383 case C_VIEW:
384 _container_destroy(con);
385 break;
386 case C_TYPES:
387 wlr_log(L_ERROR, "container_destroy called on an invalid "
388 "container");
389 break;
390 } 384 }
391 385
386 wl_signal_emit(&con->events.destroy, con);
387 ipc_event_window(con, "close");
388
389 struct sway_container *parent = container_remove_child(con);
390
391 con->destroying = true;
392 list_add(server.destroying_containers, con);
393
392 return container_reap_empty_recursive(parent); 394 return container_reap_empty_recursive(parent);
393} 395}
394 396
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 3724361d..14631ad4 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -30,7 +30,9 @@ void layout_init(void) {
30 root_container.type = C_ROOT; 30 root_container.type = C_ROOT;
31 root_container.layout = L_NONE; 31 root_container.layout = L_NONE;
32 root_container.name = strdup("root"); 32 root_container.name = strdup("root");
33 root_container.instructions = create_list();
33 root_container.children = create_list(); 34 root_container.children = create_list();
35 root_container.current.children = create_list();
34 wl_signal_init(&root_container.events.destroy); 36 wl_signal_init(&root_container.events.destroy);
35 37
36 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); 38 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 8af319d5..e2927cdb 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -29,7 +29,6 @@ static void restore_workspaces(struct sway_container *output) {
29 } 29 }
30 30
31 container_sort_workspaces(output); 31 container_sort_workspaces(output);
32 arrange_and_commit(&root_container);
33} 32}
34 33
35struct sway_container *output_create( 34struct sway_container *output_create(
@@ -66,7 +65,6 @@ struct sway_container *output_create(
66 65
67 struct sway_container *output = container_create(C_OUTPUT); 66 struct sway_container *output = container_create(C_OUTPUT);
68 output->sway_output = sway_output; 67 output->sway_output = sway_output;
69 sway_output->swayc = output;
70 output->name = strdup(name); 68 output->name = strdup(name);
71 if (output->name == NULL) { 69 if (output->name == NULL) {
72 container_destroy(output); 70 container_destroy(output);
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 658a94e8..cb36f123 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -25,47 +25,60 @@ void view_init(struct sway_view *view, enum sway_view_type type,
25 view->impl = impl; 25 view->impl = impl;
26 view->executed_criteria = create_list(); 26 view->executed_criteria = create_list();
27 view->marks = create_list(); 27 view->marks = create_list();
28 view->instructions = create_list();
29 wl_signal_init(&view->events.unmap); 28 wl_signal_init(&view->events.unmap);
30} 29}
31 30
32void view_destroy(struct sway_view *view) { 31void view_free(struct sway_view *view) {
33 if (view == NULL) { 32 if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) {
34 return; 33 return;
35 } 34 }
36 35 if (!sway_assert(view->destroying,
37 if (view->surface != NULL) { 36 "Tried to free view which wasn't marked as destroying")) {
38 view_unmap(view); 37 return;
39 } 38 }
40 39 if (!sway_assert(view->swayc == NULL,
41 if (!sway_assert(view->instructions->length == 0, 40 "Tried to free view which still has a swayc "
42 "Tried to destroy view with pending instructions")) { 41 "(might have a pending transaction?)")) {
43 return; 42 return;
44 } 43 }
45
46 list_free(view->executed_criteria); 44 list_free(view->executed_criteria);
47 45
48 for (int i = 0; i < view->marks->length; ++i) { 46 list_foreach(view->marks, free);
49 free(view->marks->items[i]);
50 }
51 list_free(view->marks); 47 list_free(view->marks);
52 48
53 list_free(view->instructions);
54
55 wlr_texture_destroy(view->marks_focused); 49 wlr_texture_destroy(view->marks_focused);
56 wlr_texture_destroy(view->marks_focused_inactive); 50 wlr_texture_destroy(view->marks_focused_inactive);
57 wlr_texture_destroy(view->marks_unfocused); 51 wlr_texture_destroy(view->marks_unfocused);
58 wlr_texture_destroy(view->marks_urgent); 52 wlr_texture_destroy(view->marks_urgent);
59 53
60 container_destroy(view->swayc); 54 if (view->impl->free) {
61 55 view->impl->free(view);
62 if (view->impl->destroy) {
63 view->impl->destroy(view);
64 } else { 56 } else {
65 free(view); 57 free(view);
66 } 58 }
67} 59}
68 60
61/**
62 * The view may or may not be involved in a transaction. For example, a view may
63 * unmap then attempt to destroy itself before we've applied the new layout. If
64 * an unmapping view is still involved in a transaction then it'll still have a
65 * swayc.
66 *
67 * If there's no transaction we can simply free the view. Otherwise the
68 * destroying flag will make the view get freed when the transaction is
69 * finished.
70 */
71void view_destroy(struct sway_view *view) {
72 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
73 return;
74 }
75 view->destroying = true;
76
77 if (!view->swayc) {
78 view_free(view);
79 }
80}
81
69const char *view_get_title(struct sway_view *view) { 82const char *view_get_title(struct sway_view *view) {
70 if (view->impl->get_string_prop) { 83 if (view->impl->get_string_prop) {
71 return view->impl->get_string_prop(view, VIEW_PROP_TITLE); 84 return view->impl->get_string_prop(view, VIEW_PROP_TITLE);
@@ -356,6 +369,9 @@ static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
356 369
357void view_for_each_surface(struct sway_view *view, 370void view_for_each_surface(struct sway_view *view,
358 wlr_surface_iterator_func_t iterator, void *user_data) { 371 wlr_surface_iterator_func_t iterator, void *user_data) {
372 if (!view->surface) {
373 return;
374 }
359 if (view->impl->for_each_surface) { 375 if (view->impl->for_each_surface) {
360 view->impl->for_each_surface(view, iterator, user_data); 376 view->impl->for_each_surface(view, iterator, user_data);
361 } else { 377 } else {
@@ -523,11 +539,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
523 view_handle_container_reparent(&view->container_reparent, NULL); 539 view_handle_container_reparent(&view->container_reparent, NULL);
524} 540}
525 541
526void view_unmap(struct sway_view *view) { 542struct sway_container *view_unmap(struct sway_view *view) {
527 if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
528 return;
529 }
530
531 wl_signal_emit(&view->events.unmap, view); 543 wl_signal_emit(&view->events.unmap, view);
532 544
533 if (view->is_fullscreen) { 545 if (view->is_fullscreen) {
@@ -535,22 +547,10 @@ void view_unmap(struct sway_view *view) {
535 ws->sway_workspace->fullscreen = NULL; 547 ws->sway_workspace->fullscreen = NULL;
536 } 548 }
537 549
538 container_damage_whole(view->swayc);
539
540 wl_list_remove(&view->surface_new_subsurface.link); 550 wl_list_remove(&view->surface_new_subsurface.link);
541 wl_list_remove(&view->container_reparent.link); 551 wl_list_remove(&view->container_reparent.link);
542 552
543 struct sway_container *parent = container_destroy(view->swayc); 553 return container_destroy(view->swayc);
544
545 view->swayc = NULL;
546 view->surface = NULL;
547
548 if (view->title_format) {
549 free(view->title_format);
550 view->title_format = NULL;
551 }
552
553 arrange_and_commit(parent);
554} 554}
555 555
556void view_update_position(struct sway_view *view, double lx, double ly) { 556void view_update_position(struct sway_view *view, double lx, double ly) {
@@ -924,7 +924,7 @@ void view_update_marks_textures(struct sway_view *view) {
924} 924}
925 925
926bool view_is_visible(struct sway_view *view) { 926bool view_is_visible(struct sway_view *view) {
927 if (!view->swayc) { 927 if (!view->swayc || view->swayc->destroying) {
928 return false; 928 return false;
929 } 929 }
930 struct sway_container *workspace = 930 struct sway_container *workspace =
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index ead752ad..5eb4be0f 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -430,6 +430,9 @@ bool workspace_switch(struct sway_container *workspace) {
430} 430}
431 431
432bool workspace_is_visible(struct sway_container *ws) { 432bool workspace_is_visible(struct sway_container *ws) {
433 if (ws->destroying) {
434 return false;
435 }
433 struct sway_container *output = container_parent(ws, C_OUTPUT); 436 struct sway_container *output = container_parent(ws, C_OUTPUT);
434 struct sway_seat *seat = input_manager_current_seat(input_manager); 437 struct sway_seat *seat = input_manager_current_seat(input_manager);
435 struct sway_container *focus = seat_get_focus_inactive(seat, output); 438 struct sway_container *focus = seat_get_focus_inactive(seat, output);