diff options
author | Ryan Dwyer <ryandwyer1@gmail.com> | 2018-08-20 15:54:30 +1000 |
---|---|---|
committer | Ryan Dwyer <ryandwyer1@gmail.com> | 2018-08-24 22:17:28 +1000 |
commit | b6058703fa240780d66fac8ef96982c66b2b0263 (patch) | |
tree | 5e056a7859751c68c0cfb425fc6f37599c3f7400 /sway/tree/container.c | |
parent | Merge pull request #2470 from ianyfan/completions (diff) | |
download | sway-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.
Diffstat (limited to 'sway/tree/container.c')
-rw-r--r-- | sway/tree/container.c | 284 |
1 files changed, 38 insertions, 246 deletions
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 | ||
107 | static void container_workspace_free(struct sway_workspace *ws) { | 107 | void 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 | |||
114 | void 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 | |||
163 | static struct sway_container *container_destroy_noreaping( | ||
164 | struct sway_container *con); | ||
165 | |||
166 | static 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 | |||
214 | static 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 | |||
222 | static 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 | /** | 144 | void 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")) { |
283 | static 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 | |||
328 | bool 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 | ||
357 | struct sway_container *container_reap_empty_recursive( | 169 | struct 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 | */ | ||
388 | struct 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 | |||
402 | static void container_close_func(struct sway_container *container, void *data) { | 194 | static 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); |