aboutsummaryrefslogtreecommitdiffstats
path: root/sway/tree/container.c
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 /sway/tree/container.c
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.
Diffstat (limited to 'sway/tree/container.c')
-rw-r--r--sway/tree/container.c284
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
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);