aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-20 15:54:30 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-24 22:17:28 +1000
commitb6058703fa240780d66fac8ef96982c66b2b0263 (patch)
tree5e056a7859751c68c0cfb425fc6f37599c3f7400
parentMerge pull request #2470 from ianyfan/completions (diff)
downloadsway-b6058703fa240780d66fac8ef96982c66b2b0263.tar.gz
sway-b6058703fa240780d66fac8ef96982c66b2b0263.tar.zst
sway-b6058703fa240780d66fac8ef96982c66b2b0263.zip
Refactor destroy functions and save workspaces when there's no outputs
This changes the destroy functions to the following: * output_begin_destroy * output_destroy * workspace_begin_destroy * workspace_destroy * container_begin_destroy * container_destroy * view_begin_destroy * view_destroy The terminology was `destroy` and `free`, and it has been changed to `begin_destroy` and `destroy` respectively. When the last output is disconnected, its workspaces will now be stashed in the root. Upon connection of a new output they will be restored. There is a new function `workspace_consider_destroy` which decides whether the given workspace should be destroyed or not (ie. empty and not visible). Calling container_begin_destroy will no longer automatically reap the parents. In some places we want to reap the parents and in some we don't, so this is left to the caller. container_reap_empty_recursive and container_reap_empty have been combined into one function and it will recurse up the tree.
-rw-r--r--include/sway/output.h6
-rw-r--r--include/sway/tree/container.h30
-rw-r--r--include/sway/tree/root.h1
-rw-r--r--include/sway/tree/view.h4
-rw-r--r--include/sway/tree/workspace.h9
-rw-r--r--sway/commands/move.c2
-rw-r--r--sway/config/output.c4
-rw-r--r--sway/desktop/output.c4
-rw-r--r--sway/desktop/transaction.c17
-rw-r--r--sway/desktop/xdg_shell.c2
-rw-r--r--sway/desktop/xdg_shell_v6.c2
-rw-r--r--sway/desktop/xwayland.c2
-rw-r--r--sway/input/seat.c21
-rw-r--r--sway/tree/container.c284
-rw-r--r--sway/tree/layout.c4
-rw-r--r--sway/tree/output.c108
-rw-r--r--sway/tree/root.c2
-rw-r--r--sway/tree/view.c10
-rw-r--r--sway/tree/workspace.c63
19 files changed, 267 insertions, 308 deletions
diff --git a/include/sway/output.h b/include/sway/output.h
index d0d034b3..098540fb 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -39,6 +39,12 @@ struct sway_output {
39 } events; 39 } events;
40}; 40};
41 41
42struct sway_container *output_create(struct sway_output *sway_output);
43
44void output_destroy(struct sway_container *output);
45
46void output_begin_destroy(struct sway_container *output);
47
42typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, 48typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
43 struct wlr_surface *surface, struct wlr_box *box, float rotation, 49 struct wlr_surface *surface, struct wlr_box *box, float rotation,
44 void *user_data); 50 void *user_data);
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index cd886cd0..2cb23d3c 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -176,27 +176,6 @@ struct sway_container *container_create(enum sway_container_type type);
176 176
177const char *container_type_to_str(enum sway_container_type type); 177const char *container_type_to_str(enum sway_container_type type);
178 178
179struct sway_container *output_create(struct sway_output *sway_output);
180
181/**
182 * Create a new container container. A container container can be a a child of
183 * a workspace container or another container container.
184 */
185struct sway_container *container_container_create();
186
187/**
188 * Create a new output. Outputs are children of the root container and have no
189 * order in the tree structure.
190 */
191struct sway_container *output_create(struct sway_output *sway_output);
192
193/**
194 * Create a new workspace container. Workspaces are children of an output
195 * container and are ordered alphabetically by name.
196 */
197struct sway_container *workspace_create(struct sway_container *output,
198 const char *name);
199
200/* 179/*
201 * Create a new view container. A view can be a child of a workspace container 180 * Create a new view container. A view can be a child of a workspace container
202 * or a container container and are rendered in the order and structure of 181 * or a container container and are rendered in the order and structure of
@@ -205,9 +184,9 @@ struct sway_container *workspace_create(struct sway_container *output,
205struct sway_container *container_view_create( 184struct sway_container *container_view_create(
206 struct sway_container *sibling, struct sway_view *sway_view); 185 struct sway_container *sibling, struct sway_view *sway_view);
207 186
208void container_free(struct sway_container *cont); 187void container_destroy(struct sway_container *con);
209 188
210struct sway_container *container_destroy(struct sway_container *container); 189void container_begin_destroy(struct sway_container *con);
211 190
212struct sway_container *container_close(struct sway_container *container); 191struct sway_container *container_close(struct sway_container *container);
213 192
@@ -255,10 +234,7 @@ void container_update_textures_recursive(struct sway_container *con);
255 234
256void container_damage_whole(struct sway_container *container); 235void container_damage_whole(struct sway_container *container);
257 236
258bool container_reap_empty(struct sway_container *con); 237struct sway_container *container_reap_empty(struct sway_container *con);
259
260struct sway_container *container_reap_empty_recursive(
261 struct sway_container *con);
262 238
263struct sway_container *container_flatten(struct sway_container *container); 239struct sway_container *container_flatten(struct sway_container *container);
264 240
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h
index d1f04a96..ee4bd836 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -24,6 +24,7 @@ struct sway_root {
24 struct wl_list outputs; // sway_output::link 24 struct wl_list outputs; // sway_output::link
25 25
26 list_t *scratchpad; // struct sway_container 26 list_t *scratchpad; // struct sway_container
27 list_t *saved_workspaces; // For when there's no connected outputs
27 28
28 struct { 29 struct {
29 struct wl_signal new_container; 30 struct wl_signal new_container;
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 5fdecc2b..f73ce571 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -284,10 +284,10 @@ void view_for_each_popup(struct sway_view *view,
284void view_init(struct sway_view *view, enum sway_view_type type, 284void view_init(struct sway_view *view, enum sway_view_type type,
285 const struct sway_view_impl *impl); 285 const struct sway_view_impl *impl);
286 286
287void view_free(struct sway_view *view);
288
289void view_destroy(struct sway_view *view); 287void view_destroy(struct sway_view *view);
290 288
289void view_begin_destroy(struct sway_view *view);
290
291void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); 291void view_map(struct sway_view *view, struct wlr_surface *wlr_surface);
292 292
293void view_unmap(struct sway_view *view); 293void view_unmap(struct sway_view *view);
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index 35c91017..efcb7c69 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -18,6 +18,15 @@ extern char *prev_workspace_name;
18 18
19struct sway_container *workspace_get_initial_output(const char *name); 19struct sway_container *workspace_get_initial_output(const char *name);
20 20
21struct sway_container *workspace_create(struct sway_container *output,
22 const char *name);
23
24void workspace_destroy(struct sway_container *workspace);
25
26void workspace_begin_destroy(struct sway_container *workspace);
27
28void workspace_consider_destroy(struct sway_container *ws);
29
21char *workspace_next_name(const char *output_name); 30char *workspace_next_name(const char *output_name);
22 31
23bool workspace_switch(struct sway_container *workspace, 32bool workspace_switch(struct sway_container *workspace,
diff --git a/sway/commands/move.c b/sway/commands/move.c
index c6dc0775..4c0189ec 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -247,7 +247,7 @@ static void workspace_move_to_output(struct sway_container *workspace,
247 } 247 }
248 248
249 // Try to remove an empty workspace from the destination output. 249 // Try to remove an empty workspace from the destination output.
250 container_reap_empty_recursive(new_output_focus); 250 container_reap_empty(new_output_focus);
251 251
252 output_sort_workspaces(output); 252 output_sort_workspaces(output);
253 seat_set_focus(seat, output); 253 seat_set_focus(seat, output);
diff --git a/sway/config/output.c b/sway/config/output.c
index 1d8cb3ef..7f9b1007 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -181,13 +181,11 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
181 struct wlr_output *wlr_output = output->sway_output->wlr_output; 181 struct wlr_output *wlr_output = output->sway_output->wlr_output;
182 182
183 if (oc && oc->enabled == 0) { 183 if (oc && oc->enabled == 0) {
184 struct sway_output *sway_output = output->sway_output;
185 if (output->sway_output->bg_pid != 0) { 184 if (output->sway_output->bg_pid != 0) {
186 terminate_swaybg(output->sway_output->bg_pid); 185 terminate_swaybg(output->sway_output->bg_pid);
187 output->sway_output->bg_pid = 0; 186 output->sway_output->bg_pid = 0;
188 } 187 }
189 container_destroy(output); 188 output_begin_destroy(output);
190 sway_output->swayc = NULL;
191 wlr_output_layout_remove(root_container.sway_root->output_layout, 189 wlr_output_layout_remove(root_container.sway_root->output_layout,
192 wlr_output); 190 wlr_output);
193 return; 191 return;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 3d8bbff5..401d3c44 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -498,7 +498,7 @@ void output_damage_whole_container(struct sway_output *output,
498static void damage_handle_destroy(struct wl_listener *listener, void *data) { 498static void damage_handle_destroy(struct wl_listener *listener, void *data) {
499 struct sway_output *output = 499 struct sway_output *output =
500 wl_container_of(listener, output, damage_destroy); 500 wl_container_of(listener, output, damage_destroy);
501 container_destroy(output->swayc); 501 output_begin_destroy(output->swayc);
502} 502}
503 503
504static void handle_destroy(struct wl_listener *listener, void *data) { 504static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -506,7 +506,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
506 wl_signal_emit(&output->events.destroy, output); 506 wl_signal_emit(&output->events.destroy, output);
507 507
508 if (output->swayc) { 508 if (output->swayc) {
509 container_destroy(output->swayc); 509 output_begin_destroy(output->swayc);
510 } 510 }
511 511
512 wl_list_remove(&output->link); 512 wl_list_remove(&output->link);
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index f82e5ef2..c18529fb 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -54,7 +54,22 @@ static void transaction_destroy(struct sway_transaction *transaction) {
54 con->instruction = NULL; 54 con->instruction = NULL;
55 } 55 }
56 if (con->destroying && con->ntxnrefs == 0) { 56 if (con->destroying && con->ntxnrefs == 0) {
57 container_free(con); 57 switch (con->type) {
58 case C_ROOT:
59 break;
60 case C_OUTPUT:
61 output_destroy(con);
62 break;
63 case C_WORKSPACE:
64 workspace_destroy(con);
65 break;
66 case C_CONTAINER:
67 case C_VIEW:
68 container_destroy(con);
69 break;
70 case C_TYPES:
71 break;
72 }
58 } 73 }
59 free(instruction); 74 free(instruction);
60 } 75 }
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index aae129bd..f5aaa575 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -448,7 +448,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
448 wl_list_remove(&xdg_shell_view->map.link); 448 wl_list_remove(&xdg_shell_view->map.link);
449 wl_list_remove(&xdg_shell_view->unmap.link); 449 wl_list_remove(&xdg_shell_view->unmap.link);
450 view->wlr_xdg_surface = NULL; 450 view->wlr_xdg_surface = NULL;
451 view_destroy(view); 451 view_begin_destroy(view);
452} 452}
453 453
454struct sway_view *view_from_wlr_xdg_surface( 454struct sway_view *view_from_wlr_xdg_surface(
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 277c53a3..f623b77b 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -441,7 +441,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
441 wl_list_remove(&xdg_shell_v6_view->map.link); 441 wl_list_remove(&xdg_shell_v6_view->map.link);
442 wl_list_remove(&xdg_shell_v6_view->unmap.link); 442 wl_list_remove(&xdg_shell_v6_view->unmap.link);
443 view->wlr_xdg_surface_v6 = NULL; 443 view->wlr_xdg_surface_v6 = NULL;
444 view_destroy(view); 444 view_begin_destroy(view);
445} 445}
446 446
447struct sway_view *view_from_wlr_xdg_surface_v6( 447struct sway_view *view_from_wlr_xdg_surface_v6(
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index ce7235e4..6fcc850d 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -341,7 +341,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
341 wl_list_remove(&xwayland_view->set_hints.link); 341 wl_list_remove(&xwayland_view->set_hints.link);
342 wl_list_remove(&xwayland_view->map.link); 342 wl_list_remove(&xwayland_view->map.link);
343 wl_list_remove(&xwayland_view->unmap.link); 343 wl_list_remove(&xwayland_view->unmap.link);
344 view_destroy(&xwayland_view->view); 344 view_begin_destroy(&xwayland_view->view);
345} 345}
346 346
347static void handle_unmap(struct wl_listener *listener, void *data) { 347static void handle_unmap(struct wl_listener *listener, void *data) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 4077a8dd..997d7815 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -694,13 +694,11 @@ void seat_set_focus_warp(struct sway_seat *seat,
694 694
695 // clean up unfocused empty workspace on new output 695 // clean up unfocused empty workspace on new output
696 if (new_output_last_ws) { 696 if (new_output_last_ws) {
697 if (!workspace_is_visible(new_output_last_ws) 697 workspace_consider_destroy(new_output_last_ws);
698 && workspace_is_empty(new_output_last_ws)) { 698 if (new_output_last_ws->destroying &&
699 if (last_workspace == new_output_last_ws) { 699 last_workspace == new_output_last_ws) {
700 last_focus = NULL; 700 last_focus = NULL;
701 last_workspace = NULL; 701 last_workspace = NULL;
702 }
703 container_destroy(new_output_last_ws);
704 } 702 }
705 } 703 }
706 704
@@ -716,12 +714,9 @@ void seat_set_focus_warp(struct sway_seat *seat,
716 if (notify && last_workspace != new_workspace) { 714 if (notify && last_workspace != new_workspace) {
717 ipc_event_workspace(last_workspace, new_workspace, "focus"); 715 ipc_event_workspace(last_workspace, new_workspace, "focus");
718 } 716 }
719 if (!workspace_is_visible(last_workspace) 717 workspace_consider_destroy(last_workspace);
720 && workspace_is_empty(last_workspace)) { 718 if (last_workspace->destroying && last_workspace == last_focus) {
721 if (last_workspace == last_focus) { 719 last_focus = NULL;
722 last_focus = NULL;
723 }
724 container_destroy(last_workspace);
725 } 720 }
726 } 721 }
727 722
diff --git a/sway/tree/container.c b/sway/tree/container.c
index a8c6e667..6da6212c 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -104,209 +104,52 @@ struct sway_container *container_create(enum sway_container_type type) {
104 return c; 104 return c;
105} 105}
106 106
107static void container_workspace_free(struct sway_workspace *ws) { 107void container_destroy(struct sway_container *con) {
108 list_foreach(ws->output_priority, free); 108 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
109 list_free(ws->output_priority); 109 "Expected a container or view")) {
110 list_free(ws->floating); 110 return;
111 free(ws); 111 }
112} 112 if (!sway_assert(con->destroying,
113
114void container_free(struct sway_container *cont) {
115 if (!sway_assert(cont->destroying,
116 "Tried to free container which wasn't marked as destroying")) { 113 "Tried to free container which wasn't marked as destroying")) {
117 return; 114 return;
118 } 115 }
119 if (!sway_assert(cont->ntxnrefs == 0, "Tried to free container " 116 if (!sway_assert(con->ntxnrefs == 0, "Tried to free container "
120 "which is still referenced by transactions")) { 117 "which is still referenced by transactions")) {
121 return; 118 return;
122 } 119 }
123 free(cont->name); 120 free(con->name);
124 free(cont->formatted_title); 121 free(con->formatted_title);
125 wlr_texture_destroy(cont->title_focused); 122 wlr_texture_destroy(con->title_focused);
126 wlr_texture_destroy(cont->title_focused_inactive); 123 wlr_texture_destroy(con->title_focused_inactive);
127 wlr_texture_destroy(cont->title_unfocused); 124 wlr_texture_destroy(con->title_unfocused);
128 wlr_texture_destroy(cont->title_urgent); 125 wlr_texture_destroy(con->title_urgent);
129 list_free(cont->children); 126 list_free(con->children);
130 list_free(cont->current.children); 127 list_free(con->current.children);
131 list_free(cont->outputs); 128 list_free(con->outputs);
132
133 switch (cont->type) {
134 case C_ROOT:
135 break;
136 case C_OUTPUT:
137 break;
138 case C_WORKSPACE:
139 container_workspace_free(cont->sway_workspace);
140 break;
141 case C_CONTAINER:
142 break;
143 case C_VIEW:
144 {
145 struct sway_view *view = cont->sway_view;
146 view->swayc = NULL;
147 free(view->title_format);
148 view->title_format = NULL;
149
150 if (view->destroying) {
151 view_free(view);
152 }
153 }
154 break;
155 case C_TYPES:
156 sway_assert(false, "Didn't expect to see C_TYPES here");
157 break;
158 }
159
160 free(cont);
161}
162
163static struct sway_container *container_destroy_noreaping(
164 struct sway_container *con);
165
166static struct sway_container *container_workspace_destroy(
167 struct sway_container *workspace) {
168 if (!sway_assert(workspace, "cannot destroy null workspace")) {
169 return NULL;
170 }
171
172 struct sway_container *output = container_parent(workspace, C_OUTPUT);
173
174 // If we're destroying the output, it will be NULL here. Return the root so
175 // that it doesn't appear that the workspace has refused to be destoyed,
176 // which would leave it in a broken state with no parent.
177 if (output == NULL) {
178 return &root_container;
179 }
180
181 // Do not destroy this if it's the last workspace on this output
182 if (output->children->length == 1) {
183 return NULL;
184 }
185
186 wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name);
187
188 if (!workspace_is_empty(workspace)) {
189 // Move children to a different workspace on this output
190 struct sway_container *new_workspace = NULL;
191 for (int i = 0; i < output->children->length; i++) {
192 if (output->children->items[i] != workspace) {
193 new_workspace = output->children->items[i];
194 break;
195 }
196 }
197
198 wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'",
199 workspace->name, new_workspace->name);
200 for (int i = 0; i < workspace->children->length; i++) {
201 container_move_to(workspace->children->items[i], new_workspace);
202 }
203 list_t *floating = workspace->sway_workspace->floating;
204 for (int i = 0; i < floating->length; i++) {
205 struct sway_container *floater = floating->items[i];
206 container_remove_child(floater);
207 workspace_add_floating(new_workspace, floater);
208 }
209 }
210
211 return output;
212}
213
214static void untrack_output(struct sway_container *con, void *data) {
215 struct sway_output *output = data;
216 int index = list_find(con->outputs, output);
217 if (index != -1) {
218 list_del(con->outputs, index);
219 }
220}
221
222static struct sway_container *container_output_destroy(
223 struct sway_container *output) {
224 if (!sway_assert(output, "cannot destroy null output")) {
225 return NULL;
226 }
227
228 if (output->children->length > 0) {
229 // TODO save workspaces when there are no outputs.
230 // TODO also check if there will ever be no outputs except for exiting
231 // program
232 if (root_container.children->length > 1) {
233 // Move workspace from this output to another output
234 struct sway_container *fallback_output =
235 root_container.children->items[0];
236 if (fallback_output == output) {
237 fallback_output = root_container.children->items[1];
238 }
239
240 while (output->children->length) {
241 struct sway_container *workspace = output->children->items[0];
242
243 struct sway_container *new_output =
244 workspace_output_get_highest_available(workspace, output);
245 if (!new_output) {
246 new_output = fallback_output;
247 workspace_output_add_priority(workspace, new_output);
248 }
249 129
250 container_remove_child(workspace); 130 if (con->type == C_VIEW) {
251 if (!workspace_is_empty(workspace)) { 131 struct sway_view *view = con->sway_view;
252 container_add_child(new_output, workspace); 132 view->swayc = NULL;
253 ipc_event_workspace(NULL, workspace, "move"); 133 free(view->title_format);
254 } else { 134 view->title_format = NULL;
255 container_destroy(workspace);
256 }
257 135
258 output_sort_workspaces(new_output); 136 if (view->destroying) {
259 } 137 view_destroy(view);
260 } 138 }
261 } 139 }
262 140
263 root_for_each_container(untrack_output, output->sway_output); 141 free(con);
264
265 wl_list_remove(&output->sway_output->mode.link);
266 wl_list_remove(&output->sway_output->transform.link);
267 wl_list_remove(&output->sway_output->scale.link);
268
269 wl_list_remove(&output->sway_output->damage_destroy.link);
270 wl_list_remove(&output->sway_output->damage_frame.link);
271
272 output->sway_output->swayc = NULL;
273 output->sway_output = NULL;
274
275 wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
276
277 return &root_container;
278} 142}
279 143
280/** 144void container_begin_destroy(struct sway_container *con) {
281 * Implement the actual destroy logic, without reaping. 145 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
282 */ 146 "Expected a container or view")) {
283static struct sway_container *container_destroy_noreaping( 147 return;
284 struct sway_container *con) {
285 if (con == NULL) {
286 return NULL;
287 }
288 if (con->destroying) {
289 return NULL;
290 } 148 }
291 149
292 wl_signal_emit(&con->events.destroy, con); 150 wl_signal_emit(&con->events.destroy, con);
293
294 // emit IPC event
295 if (con->type == C_VIEW) { 151 if (con->type == C_VIEW) {
296 ipc_event_window(con, "close"); 152 ipc_event_window(con, "close");
297 } else if (con->type == C_WORKSPACE) {
298 ipc_event_workspace(NULL, con, "empty");
299 }
300
301 // The below functions move their children to somewhere else.
302 if (con->type == C_OUTPUT) {
303 container_output_destroy(con);
304 } else if (con->type == C_WORKSPACE) {
305 // Workspaces will refuse to be destroyed if they're the last workspace
306 // on their output.
307 if (!container_workspace_destroy(con)) {
308 return NULL;
309 }
310 } 153 }
311 154
312 container_end_mouse_operation(con); 155 container_end_mouse_operation(con);
@@ -318,51 +161,22 @@ static struct sway_container *container_destroy_noreaping(
318 root_scratchpad_remove_container(con); 161 root_scratchpad_remove_container(con);
319 } 162 }
320 163
321 if (!con->parent) { 164 if (con->parent) {
322 return NULL; 165 container_remove_child(con);
323 }
324
325 return container_remove_child(con);
326}
327
328bool container_reap_empty(struct sway_container *con) {
329 switch (con->type) {
330 case C_ROOT:
331 case C_OUTPUT:
332 // dont reap these
333 break;
334 case C_WORKSPACE:
335 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
336 wlr_log(WLR_DEBUG, "Destroying workspace via reaper");
337 container_destroy_noreaping(con);
338 return true;
339 }
340 break;
341 case C_CONTAINER:
342 if (con->children->length == 0) {
343 container_destroy_noreaping(con);
344 return true;
345 }
346 case C_VIEW:
347 break;
348 case C_TYPES:
349 sway_assert(false, "container_reap_empty called on an invalid "
350 "container");
351 break;
352 } 166 }
353
354 return false;
355} 167}
356 168
357struct sway_container *container_reap_empty_recursive( 169struct sway_container *container_reap_empty(struct sway_container *con) {
358 struct sway_container *con) { 170 while (con && con->type == C_CONTAINER) {
359 while (con) {
360 struct sway_container *next = con->parent; 171 struct sway_container *next = con->parent;
361 if (!container_reap_empty(con)) { 172 if (con->children->length == 0) {
362 break; 173 container_begin_destroy(con);
363 } 174 }
364 con = next; 175 con = next;
365 } 176 }
177 if (con && con->type == C_WORKSPACE) {
178 workspace_consider_destroy(con);
179 }
366 return con; 180 return con;
367} 181}
368 182
@@ -371,34 +185,12 @@ struct sway_container *container_flatten(struct sway_container *container) {
371 struct sway_container *child = container->children->items[0]; 185 struct sway_container *child = container->children->items[0];
372 struct sway_container *parent = container->parent; 186 struct sway_container *parent = container->parent;
373 container_replace_child(container, child); 187 container_replace_child(container, child);
374 container_destroy_noreaping(container); 188 container_begin_destroy(container);
375 container = parent; 189 container = parent;
376 } 190 }
377 return container; 191 return container;
378} 192}
379 193
380/**
381 * container_destroy() is the first step in destroying a container. We'll emit
382 * events, detach it from the tree and mark it as destroying. The container will
383 * remain in memory until it's no longer used by a transaction, then it will be
384 * freed via container_free().
385 *
386 * This function just wraps container_destroy_noreaping(), then does reaping.
387 */
388struct sway_container *container_destroy(struct sway_container *con) {
389 if (con->is_fullscreen) {
390 struct sway_container *ws = container_parent(con, C_WORKSPACE);
391 ws->sway_workspace->fullscreen = NULL;
392 }
393 struct sway_container *parent = container_destroy_noreaping(con);
394
395 if (!parent) {
396 return NULL;
397 }
398
399 return container_reap_empty_recursive(parent);
400}
401
402static void container_close_func(struct sway_container *container, void *data) { 194static void container_close_func(struct sway_container *container, void *data) {
403 if (container->type == C_VIEW) { 195 if (container->type == C_VIEW) {
404 view_close(container->sway_view); 196 view_close(container->sway_view);
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index a3de44ce..12e7342b 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -302,7 +302,7 @@ static void workspace_rejigger(struct sway_container *ws,
302 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 302 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
303 303
304 container_flatten(ws); 304 container_flatten(ws);
305 container_reap_empty_recursive(original_parent); 305 container_reap_empty(original_parent);
306 container_create_notify(new_parent); 306 container_create_notify(new_parent);
307} 307}
308 308
@@ -325,7 +325,7 @@ static void move_out_of_tabs_stacks(struct sway_container *container,
325 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1); 325 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1);
326 } else { 326 } else {
327 container_insert_child(new_parent, container, offs < 0 ? 0 : 1); 327 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
328 container_reap_empty_recursive(new_parent->parent); 328 container_reap_empty(new_parent->parent);
329 container_flatten(new_parent->parent); 329 container_flatten(new_parent->parent);
330 } 330 }
331 container_create_notify(new_parent); 331 container_create_notify(new_parent);
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 6da63064..80636c11 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -10,6 +10,7 @@
10#include "log.h" 10#include "log.h"
11 11
12static void restore_workspaces(struct sway_container *output) { 12static void restore_workspaces(struct sway_container *output) {
13 // Workspace output priority
13 for (int i = 0; i < root_container.children->length; i++) { 14 for (int i = 0; i < root_container.children->length; i++) {
14 struct sway_container *other = root_container.children->items[i]; 15 struct sway_container *other = root_container.children->items[i];
15 if (other == output) { 16 if (other == output) {
@@ -29,6 +30,15 @@ static void restore_workspaces(struct sway_container *output) {
29 } 30 }
30 } 31 }
31 32
33 // Saved workspaces
34 list_t *saved = root_container.sway_root->saved_workspaces;
35 for (int i = 0; i < saved->length; ++i) {
36 struct sway_container *ws = saved->items[i];
37 container_add_child(output, ws);
38 ipc_event_workspace(NULL, ws, "move");
39 }
40 saved->length = 0;
41
32 output_sort_workspaces(output); 42 output_sort_workspaces(output);
33} 43}
34 44
@@ -68,7 +78,7 @@ struct sway_container *output_create(
68 output->sway_output = sway_output; 78 output->sway_output = sway_output;
69 output->name = strdup(name); 79 output->name = strdup(name);
70 if (output->name == NULL) { 80 if (output->name == NULL) {
71 container_destroy(output); 81 output_begin_destroy(output);
72 return NULL; 82 return NULL;
73 } 83 }
74 84
@@ -103,6 +113,102 @@ struct sway_container *output_create(
103 return output; 113 return output;
104} 114}
105 115
116static void output_evacuate(struct sway_container *output) {
117 if (!output->children->length) {
118 return;
119 }
120 struct sway_container *fallback_output = NULL;
121 if (root_container.children->length > 1) {
122 fallback_output = root_container.children->items[0];
123 if (fallback_output == output) {
124 fallback_output = root_container.children->items[1];
125 }
126 }
127
128 while (output->children->length) {
129 struct sway_container *workspace = output->children->items[0];
130
131 struct sway_container *new_output =
132 workspace_output_get_highest_available(workspace, output);
133 if (!new_output) {
134 new_output = fallback_output;
135 }
136
137 container_remove_child(workspace);
138
139 if (new_output) {
140 workspace_output_add_priority(workspace, new_output);
141 container_add_child(new_output, workspace);
142 output_sort_workspaces(new_output);
143 ipc_event_workspace(NULL, workspace, "move");
144 } else {
145 list_add(root_container.sway_root->saved_workspaces, workspace);
146 }
147 }
148}
149
150void output_destroy(struct sway_container *output) {
151 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
152 return;
153 }
154 if (!sway_assert(output->destroying,
155 "Tried to free output which wasn't marked as destroying")) {
156 return;
157 }
158 if (!sway_assert(output->ntxnrefs == 0, "Tried to free output "
159 "which is still referenced by transactions")) {
160 return;
161 }
162 free(output->name);
163 free(output->formatted_title);
164 wlr_texture_destroy(output->title_focused);
165 wlr_texture_destroy(output->title_focused_inactive);
166 wlr_texture_destroy(output->title_unfocused);
167 wlr_texture_destroy(output->title_urgent);
168 list_free(output->children);
169 list_free(output->current.children);
170 list_free(output->outputs);
171 free(output);
172
173 // NOTE: We don't actually destroy the sway_output here
174}
175
176static void untrack_output(struct sway_container *con, void *data) {
177 struct sway_output *output = data;
178 int index = list_find(con->outputs, output);
179 if (index != -1) {
180 list_del(con->outputs, index);
181 }
182}
183
184void output_begin_destroy(struct sway_container *output) {
185 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
186 return;
187 }
188 wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
189 wl_signal_emit(&output->events.destroy, output);
190
191 output_evacuate(output);
192
193 output->destroying = true;
194 container_set_dirty(output);
195
196 root_for_each_container(untrack_output, output->sway_output);
197
198 wl_list_remove(&output->sway_output->mode.link);
199 wl_list_remove(&output->sway_output->transform.link);
200 wl_list_remove(&output->sway_output->scale.link);
201 wl_list_remove(&output->sway_output->damage_destroy.link);
202 wl_list_remove(&output->sway_output->damage_frame.link);
203
204 output->sway_output->swayc = NULL;
205 output->sway_output = NULL;
206
207 if (output->parent) {
208 container_remove_child(output);
209 }
210}
211
106void output_for_each_workspace(struct sway_container *output, 212void output_for_each_workspace(struct sway_container *output,
107 void (*f)(struct sway_container *con, void *data), void *data) { 213 void (*f)(struct sway_container *con, void *data), void *data) {
108 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 214 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index c27ff2c3..5602f0a0 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -39,6 +39,7 @@ void root_create(void) {
39 wl_list_init(&root_container.sway_root->drag_icons); 39 wl_list_init(&root_container.sway_root->drag_icons);
40 wl_signal_init(&root_container.sway_root->events.new_container); 40 wl_signal_init(&root_container.sway_root->events.new_container);
41 root_container.sway_root->scratchpad = create_list(); 41 root_container.sway_root->scratchpad = create_list();
42 root_container.sway_root->saved_workspaces = create_list();
42 43
43 root_container.sway_root->output_layout_change.notify = 44 root_container.sway_root->output_layout_change.notify =
44 output_layout_handle_change; 45 output_layout_handle_change;
@@ -50,6 +51,7 @@ void root_destroy(void) {
50 // sway_root 51 // sway_root
51 wl_list_remove(&root_container.sway_root->output_layout_change.link); 52 wl_list_remove(&root_container.sway_root->output_layout_change.link);
52 list_free(root_container.sway_root->scratchpad); 53 list_free(root_container.sway_root->scratchpad);
54 list_free(root_container.sway_root->saved_workspaces);
53 wlr_output_layout_destroy(root_container.sway_root->output_layout); 55 wlr_output_layout_destroy(root_container.sway_root->output_layout);
54 free(root_container.sway_root); 56 free(root_container.sway_root);
55 57
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7bf7325a..ba4a880f 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -35,7 +35,7 @@ void view_init(struct sway_view *view, enum sway_view_type type,
35 wl_signal_init(&view->events.unmap); 35 wl_signal_init(&view->events.unmap);
36} 36}
37 37
38void view_free(struct sway_view *view) { 38void view_destroy(struct sway_view *view) {
39 if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { 39 if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) {
40 return; 40 return;
41 } 41 }
@@ -75,14 +75,14 @@ void view_free(struct sway_view *view) {
75 * destroying flag will make the view get freed when the transaction is 75 * destroying flag will make the view get freed when the transaction is
76 * finished. 76 * finished.
77 */ 77 */
78void view_destroy(struct sway_view *view) { 78void view_begin_destroy(struct sway_view *view) {
79 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { 79 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
80 return; 80 return;
81 } 81 }
82 view->destroying = true; 82 view->destroying = true;
83 83
84 if (!view->swayc) { 84 if (!view->swayc) {
85 view_free(view); 85 view_destroy(view);
86 } 86 }
87} 87}
88 88
@@ -560,7 +560,9 @@ void view_unmap(struct sway_view *view) {
560 } 560 }
561 561
562 bool was_fullscreen = view->swayc->is_fullscreen; 562 bool was_fullscreen = view->swayc->is_fullscreen;
563 struct sway_container *surviving_ancestor = container_destroy(view->swayc); 563 struct sway_container *parent = view->swayc->parent;
564 container_begin_destroy(view->swayc);
565 struct sway_container *surviving_ancestor = container_reap_empty(parent);
564 566
565 // If the workspace wasn't reaped 567 // If the workspace wasn't reaped
566 if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { 568 if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) {
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index cf50ee09..93c4b3d3 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -79,6 +79,65 @@ struct sway_container *workspace_create(struct sway_container *output,
79 return workspace; 79 return workspace;
80} 80}
81 81
82void workspace_destroy(struct sway_container *workspace) {
83 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
84 return;
85 }
86 if (!sway_assert(workspace->destroying,
87 "Tried to free workspace which wasn't marked as destroying")) {
88 return;
89 }
90 if (!sway_assert(workspace->ntxnrefs == 0, "Tried to free workspace "
91 "which is still referenced by transactions")) {
92 return;
93 }
94 // sway_workspace
95 struct sway_workspace *ws = workspace->sway_workspace;
96 list_foreach(ws->output_priority, free);
97 list_free(ws->output_priority);
98 list_free(ws->floating);
99 free(ws);
100
101 // swayc
102 free(workspace->name);
103 free(workspace->formatted_title);
104 wlr_texture_destroy(workspace->title_focused);
105 wlr_texture_destroy(workspace->title_focused_inactive);
106 wlr_texture_destroy(workspace->title_unfocused);
107 wlr_texture_destroy(workspace->title_urgent);
108 list_free(workspace->children);
109 list_free(workspace->current.children);
110 list_free(workspace->outputs);
111 free(workspace);
112}
113
114void workspace_begin_destroy(struct sway_container *workspace) {
115 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
116 return;
117 }
118 wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name);
119 wl_signal_emit(&workspace->events.destroy, workspace);
120 ipc_event_workspace(NULL, workspace, "empty"); // intentional
121
122 workspace->destroying = true;
123 container_set_dirty(workspace);
124
125 if (workspace->parent) {
126 container_remove_child(workspace);
127 }
128}
129
130void workspace_consider_destroy(struct sway_container *ws) {
131 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
132 return;
133 }
134 struct sway_seat *seat = input_manager_current_seat(input_manager);
135 if (ws->children->length == 0 && ws->sway_workspace->floating->length == 0
136 && seat_get_active_child(seat, ws->parent) != ws) {
137 workspace_begin_destroy(ws);
138 }
139}
140
82char *prev_workspace_name = NULL; 141char *prev_workspace_name = NULL;
83 142
84void next_name_map(struct sway_container *ws, void *data) { 143void next_name_map(struct sway_container *ws, void *data) {
@@ -421,9 +480,7 @@ bool workspace_switch(struct sway_container *workspace,
421 // no op. We therefore need to send the IPC event and clean up the old 480 // no op. We therefore need to send the IPC event and clean up the old
422 // workspace here. 481 // workspace here.
423 ipc_event_workspace(active_ws, workspace, "focus"); 482 ipc_event_workspace(active_ws, workspace, "focus");
424 if (!workspace_is_visible(active_ws) && workspace_is_empty(active_ws)) { 483 workspace_consider_destroy(active_ws);
425 container_destroy(active_ws);
426 }
427 } 484 }
428 seat_set_focus(seat, next); 485 seat_set_focus(seat, next);
429 struct sway_container *output = container_parent(workspace, C_OUTPUT); 486 struct sway_container *output = container_parent(workspace, C_OUTPUT);