diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 80af4aaee..dd7edb7a5 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -23,7 +23,6 @@ struct sway_transaction_instruction; struct sway_view; -struct sway_node; /** * Find all dirty containers, create and commit a transaction containing them, @@ -31,11 +30,6 @@ struct sway_node; */ void transaction_commit_dirty(void); -/** - * Remove a node that will be destroyed from transactions and dirty node lists. - */ -void transaction_remove_node(struct sway_node *node); - /* * Same as transaction_commit_dirty, but signalling that this is a * client-initiated change has already taken effect. diff --git a/include/sway/output.h b/include/sway/output.h index 43c3dc9ad..f6354e0ed 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -84,6 +84,8 @@ struct sway_output *output_create(struct wlr_output *wlr_output); void output_destroy(struct sway_output *output); +void output_begin_destroy(struct sway_output *output); + struct sway_output *output_from_wlr_output(struct wlr_output *output); struct sway_output *output_get_in_direction(struct sway_output *reference, diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 4b2841042..e18fd00ac 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -155,6 +155,8 @@ struct sway_container *container_create(struct sway_view *view); void container_destroy(struct sway_container *con); +void container_begin_destroy(struct sway_container *con); + /** * Search a container's descendants a container based on test criteria. Returns * the first container that passes the test. diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 7b6e936c7..e2dbcdf05 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -39,6 +39,7 @@ struct sway_node { struct sway_transaction_instruction *instruction; size_t ntxnrefs; + bool destroying; // If true, indicates that the container has pending state that differs from // the current. diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index d999b5024..6151a0234 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -101,6 +101,8 @@ struct sway_view { struct wl_listener foreign_close_request; struct wl_listener foreign_destroy; + bool destroying; + list_t *executed_criteria; // struct criteria * union { @@ -294,6 +296,8 @@ bool view_init(struct sway_view *view, enum sway_view_type type, void view_destroy(struct sway_view *view); +void view_begin_destroy(struct sway_view *view); + /** * Map a view, ie. make it visible in the tree. * diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 77991b275..27ed649fd 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -62,7 +62,9 @@ struct sway_workspace *workspace_create(struct sway_output *output, void workspace_destroy(struct sway_workspace *workspace); -bool workspace_consider_destroy(struct sway_workspace *ws); +void workspace_begin_destroy(struct sway_workspace *workspace); + +void workspace_consider_destroy(struct sway_workspace *ws); char *workspace_next_name(const char *output_name); diff --git a/sway/commands/move.c b/sway/commands/move.c index 8e52e4bc8..90e8585b4 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -611,16 +611,14 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, if (old_parent) { container_reap_empty(old_parent); } else if (old_ws) { - if (workspace_consider_destroy(old_ws)) { - old_ws = NULL; - } + workspace_consider_destroy(old_ws); } // arrange windows if (root->fullscreen_global) { arrange_root(); } else { - if (old_ws) { + if (old_ws && !old_ws->node.destroying) { arrange_workspace(old_ws); } arrange_node(node_get_parent(destination)); @@ -755,9 +753,7 @@ static struct cmd_results *cmd_move_in_direction( if (old_parent) { container_reap_empty(old_parent); } else if (old_ws) { - if (workspace_consider_destroy(old_ws)) { - old_ws = NULL; - } + workspace_consider_destroy(old_ws); } struct sway_workspace *new_ws = container->pending.workspace; @@ -765,9 +761,7 @@ static struct cmd_results *cmd_move_in_direction( if (root->fullscreen_global) { arrange_root(); } else { - if (old_ws) { - arrange_workspace(old_ws); - } + arrange_workspace(old_ws); if (new_ws != old_ws) { arrange_workspace(new_ws); } diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 5a330554a..2362e1ba0 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -153,7 +153,7 @@ static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { // same output free(ctx->fallback_name); ctx->fallback_name = strdup(ws->name); - if (!ws->output) { + if (!ws->output || ws->output->node.destroying) { // If the output is being destroyed it would be pointless to track // If the output is being disabled, we'll find out if it's still // disabled when we try to match it. diff --git a/sway/desktop/output.c b/sway/desktop/output.c index eeb203ffc..12dc9cc7a 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -428,7 +428,7 @@ void force_modeset(void) { apply_stored_output_configs(); } -static void output_teardown(struct sway_output *output) { +static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->layout_destroy.link); wl_list_remove(&output->destroy.link); @@ -444,6 +444,7 @@ static void output_teardown(struct sway_output *output) { if (output->enabled) { output_disable(output); } + output_begin_destroy(output); wl_list_remove(&output->link); output->wlr_output->data = NULL; @@ -452,19 +453,17 @@ static void output_teardown(struct sway_output *output) { wl_event_source_remove(output->repaint_timer); output->repaint_timer = NULL; - output_destroy(output); - request_modeset(); } static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); - output_teardown(output); + begin_destroy(output); } static void handle_layout_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, layout_destroy); - output_teardown(output); + begin_destroy(output); } static void handle_present(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 73e860ea1..325a30226 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -59,6 +59,22 @@ static void transaction_destroy(struct sway_transaction *transaction) { if (node->instruction == instruction) { node->instruction = NULL; } + if (node->destroying && node->ntxnrefs == 0) { + switch (node->type) { + case N_ROOT: + sway_assert(false, "Never reached"); + break; + case N_OUTPUT: + output_destroy(node->sway_output); + break; + case N_WORKSPACE: + workspace_destroy(node->sway_workspace); + break; + case N_CONTAINER: + container_destroy(node->sway_container); + break; + } + } free(instruction); } list_free(transaction->instructions); @@ -223,7 +239,7 @@ static void apply_container_state(struct sway_container *container, if (view) { if (view->saved_surface_tree) { - if (container->node.ntxnrefs == 1) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { view_remove_saved_buffer(view); } } @@ -772,6 +788,9 @@ static bool should_configure(struct sway_node *node, if (!node_is_view(node)) { return false; } + if (node->destroying) { + return false; + } if (!instruction->server_request) { return false; } @@ -806,7 +825,7 @@ static void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_node *node = instruction->node; - bool hidden = node_is_view(node) && + bool hidden = node_is_view(node) && !node->destroying && !view_is_visible(node->sway_container->view); if (should_configure(node, instruction)) { instruction->serial = view_configure(node->sway_container->view, @@ -948,39 +967,3 @@ void transaction_commit_dirty(void) { void transaction_commit_dirty_client(void) { _transaction_commit_dirty(false); } - -static void _transaction_remove_node(struct sway_transaction *transaction, - struct sway_node *node) { - if (!transaction || !node) { - return; - } - for (int idx = 0; idx < transaction->instructions->length; idx++) { - struct sway_transaction_instruction *instruction = - transaction->instructions->items[idx]; - struct sway_node *n = instruction->node; - if (n != node) { - continue; - } - - n->ntxnrefs--; - n->instruction = NULL; - free(instruction); - list_del(transaction->instructions, idx); - idx--; - } -} - -void transaction_remove_node(struct sway_node *node) { - _transaction_remove_node(server.pending_transaction, node); - _transaction_remove_node(server.queued_transaction, node); - - for (int idx = 0; idx < server.dirty_nodes->length; idx++) { - struct sway_node *n = server.dirty_nodes->items[idx]; - if (n != node) { - continue; - } - n->dirty = false; - list_del(server.dirty_nodes, idx); - idx--; - } -} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index be19a5d6b..3852806e4 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -535,7 +535,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { if (view->xdg_decoration) { view->xdg_decoration->view = NULL; } - view_destroy(view); + view_begin_destroy(view); } struct sway_view *view_from_wlr_xdg_surface( diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index fef0ff781..76e63ce1f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -482,7 +482,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->associate.link); wl_list_remove(&xwayland_view->dissociate.link); wl_list_remove(&xwayland_view->override_redirect.link); - view_destroy(&xwayland_view->view); + view_begin_destroy(&xwayland_view->view); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 2767bc231..0385d7c17 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -55,7 +55,7 @@ static void handle_destroy( struct sway_container *con = wl_container_of( listener, con, output_handler_destroy); - container_destroy(con); + container_begin_destroy(con); } static bool handle_point_accepts_input( @@ -501,6 +501,36 @@ void container_update_title_bar(struct sway_container *con) { } void container_destroy(struct sway_container *con) { + if (!sway_assert(con->node.destroying, + "Tried to free container which wasn't marked as destroying")) { + return; + } + if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container " + "which is still referenced by transactions")) { + return; + } + free(con->title); + free(con->formatted_title); + free(con->title_format); + list_free(con->pending.children); + list_free(con->current.children); + + list_free_items_and_destroy(con->marks); + + if (con->view && con->view->container == con) { + con->view->container = NULL; + wlr_scene_node_destroy(&con->output_handler->node); + if (con->view->destroying) { + view_destroy(con->view); + } + } + + scene_node_disown_children(con->content_tree); + wlr_scene_node_destroy(&con->scene_tree->node); + free(con); +} + +void container_begin_destroy(struct sway_container *con) { if (con->view) { ipc_event_window(con, "close"); } @@ -517,6 +547,9 @@ void container_destroy(struct sway_container *con) { container_end_mouse_operation(con); + con->node.destroying = true; + node_set_dirty(&con->node); + if (con->scratchpad) { root_scratchpad_remove_container(con); } @@ -534,28 +567,6 @@ void container_destroy(struct sway_container *con) { wl_list_remove(&con->output_leave.link); wl_list_remove(&con->output_handler_destroy.link); } - - transaction_remove_node(&con->node); - if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container " - "which is still referenced by transactions")) { - return; - } - free(con->title); - free(con->formatted_title); - free(con->title_format); - list_free(con->pending.children); - list_free(con->current.children); - - list_free_items_and_destroy(con->marks); - - if (con->view && con->view->container == con) { - con->view->container = NULL; - wlr_scene_node_destroy(&con->output_handler->node); - } - - scene_node_disown_children(con->content_tree); - wlr_scene_node_destroy(&con->scene_tree->node); - free(con); } void container_reap_empty(struct sway_container *con) { @@ -568,7 +579,7 @@ void container_reap_empty(struct sway_container *con) { return; } struct sway_container *parent = con->pending.parent; - container_destroy(con); + container_begin_destroy(con); con = parent; } if (ws) { @@ -584,7 +595,7 @@ struct sway_container *container_flatten(struct sway_container *container) { struct sway_container *child = container->pending.children->items[0]; struct sway_container *parent = container->pending.parent; container_replace(container, child); - container_destroy(container); + container_begin_destroy(container); container = parent; } return container; diff --git a/sway/tree/output.c b/sway/tree/output.c index b969c8105..b02c1a2ce 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -2,7 +2,6 @@ #include #include #include -#include "sway/desktop/transaction.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -236,7 +235,7 @@ static void output_evacuate(struct sway_output *output) { } if (workspace_num_sticky_containers(workspace) == 0) { - workspace_destroy(workspace); + workspace_begin_destroy(workspace); continue; } } @@ -254,6 +253,27 @@ static void output_evacuate(struct sway_output *output) { } } +void output_destroy(struct sway_output *output) { + if (!sway_assert(output->node.destroying, + "Tried to free output which wasn't marked as destroying")) { + return; + } + if (!sway_assert(output->wlr_output == NULL, + "Tried to free output which still had a wlr_output")) { + return; + } + if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output " + "which is still referenced by transactions")) { + return; + } + + destroy_scene_layers(output); + list_free(output->workspaces); + list_free(output->current.workspaces); + wlr_color_transform_unref(output->color_transform); + free(output); +} + void output_disable(struct sway_output *output) { if (!sway_assert(output->enabled, "Expected an enabled output")) { return; @@ -275,24 +295,15 @@ void output_disable(struct sway_output *output) { output_evacuate(output); } -void output_destroy(struct sway_output *output) { +void output_begin_destroy(struct sway_output *output) { if (!sway_assert(!output->enabled, "Expected a disabled output")) { return; } + sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); wl_signal_emit_mutable(&output->node.events.destroy, &output->node); - transaction_remove_node(&output->node); - - if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output " - "which is still referenced by transactions")) { - return; - } - - destroy_scene_layers(output); - list_free(output->workspaces); - list_free(output->current.workspaces); - wlr_color_transform_unref(output->color_transform); - free(output); + output->node.destroying = true; + node_set_dirty(&output->node); } struct sway_output *output_from_wlr_output(struct wlr_output *output) { diff --git a/sway/tree/view.c b/sway/tree/view.c index d4f72211b..7bf185fea 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -70,10 +70,11 @@ bool view_init(struct sway_view *view, enum sway_view_type type, } void view_destroy(struct sway_view *view) { - if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { + if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { return; } - if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { + if (!sway_assert(view->destroying, + "Tried to free view which wasn't marked as destroying")) { return; } if (!sway_assert(view->container == NULL, @@ -94,6 +95,17 @@ void view_destroy(struct sway_view *view) { } } +void view_begin_destroy(struct sway_view *view) { + if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { + return; + } + view->destroying = true; + + if (!view->container) { + view_destroy(view); + } +} + const char *view_get_title(struct sway_view *view) { if (view->impl->get_string_prop) { return view->impl->get_string_prop(view, VIEW_PROP_TITLE); @@ -923,19 +935,17 @@ void view_unmap(struct sway_view *view) { struct sway_container *parent = view->container->pending.parent; struct sway_workspace *ws = view->container->pending.workspace; - container_destroy(view->container); + container_begin_destroy(view->container); if (parent) { container_reap_empty(parent); } else if (ws) { - if (workspace_consider_destroy(ws)) { - ws = NULL; - } + workspace_consider_destroy(ws); } if (root->fullscreen_global) { // Container may have been a child of the root fullscreen container arrange_root(); - } else if (ws) { + } else if (ws && !ws->node.destroying) { arrange_workspace(ws); workspace_detect_urgent(ws); } @@ -1089,6 +1099,9 @@ void view_update_title(struct sway_view *view, bool force) { } bool view_is_visible(struct sway_view *view) { + if (view->container->node.destroying) { + return false; + } struct sway_workspace *workspace = view->container->pending.workspace; if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { bool fs_global_descendant = false; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 44256bcff..f2be4cd13 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -5,7 +5,6 @@ #include #include #include "stringop.h" -#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -135,15 +134,10 @@ struct sway_workspace *workspace_create(struct sway_output *output, } void workspace_destroy(struct sway_workspace *workspace) { - sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); - ipc_event_workspace(NULL, workspace, "empty"); // intentional - wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); - - if (workspace->output) { - workspace_detach(workspace); + if (!sway_assert(workspace->node.destroying, + "Tried to free workspace which wasn't marked as destroying")) { + return; } - transaction_remove_node(&workspace->node); - if (!sway_assert(workspace->node.ntxnrefs == 0, "Tried to free workspace " "which is still referenced by transactions")) { return; @@ -164,25 +158,36 @@ void workspace_destroy(struct sway_workspace *workspace) { free(workspace); } -bool workspace_consider_destroy(struct sway_workspace *ws) { +void workspace_begin_destroy(struct sway_workspace *workspace) { + sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); + ipc_event_workspace(NULL, workspace, "empty"); // intentional + wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); + + if (workspace->output) { + workspace_detach(workspace); + } + workspace->node.destroying = true; + node_set_dirty(&workspace->node); +} + +void workspace_consider_destroy(struct sway_workspace *ws) { if (ws->tiling->length || ws->floating->length) { - return false; + return; } if (ws->output && output_get_active_workspace(ws->output) == ws) { - return false; + return; } struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { struct sway_node *node = seat_get_focus_inactive(seat, &root->node); if (node == &ws->node) { - return false; + return; } } - workspace_destroy(ws); - return true; + workspace_begin_destroy(ws); } static bool workspace_valid_on_output(const char *output_name, @@ -591,6 +596,9 @@ bool workspace_switch(struct sway_workspace *workspace) { } bool workspace_is_visible(struct sway_workspace *ws) { + if (ws->node.destroying) { + return false; + } return output_get_active_workspace(ws->output) == ws; }