aboutsummaryrefslogtreecommitdiff
path: root/sway/tree
diff options
context:
space:
mode:
authorRyan Dwyer <ryandwyer1@gmail.com>2018-06-23 16:24:11 +1000
committerRyan Dwyer <ryandwyer1@gmail.com>2018-06-23 16:24:11 +1000
commit38398e2d77d57dc06b67ec88a54091c897915602 (patch)
treec80935807865fd96ab7d037070287d4dfaba1863 /sway/tree
parent1c89f32533534f6e78c81c95578f40df45bb9016 (diff)
Implement atomic layout updates for tree operations
This implements atomic layout updates for when views map, reparent or unmap.
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) {
}
}
+/**
+ * If a container has been deleted from the pending tree state, we must add it
+ * to the transaction so it can be freed afterwards. To do this, we iterate the
+ * server's destroying_containers list and add all of them. We may add more than
+ * what we need to, but this is easy and has no negative consequences.
+ */
+static void add_deleted_containers(struct sway_transaction *transaction) {
+ for (int i = 0; i < server.destroying_containers->length; ++i) {
+ struct sway_container *child = server.destroying_containers->items[i];
+ transaction_add_container(transaction, child);
+ }
+}
+
static void arrange_children_of(struct sway_container *parent,
struct sway_transaction *transaction);
@@ -158,6 +171,7 @@ static void arrange_floating(struct sway_container *floating,
}
transaction_add_container(transaction, floater);
}
+ transaction_add_container(transaction, floating);
}
static void arrange_children_of(struct sway_container *parent,
@@ -290,7 +304,16 @@ void arrange_windows(struct sway_container *container,
case C_TYPES:
break;
}
- transaction_add_damage(transaction, container_get_box(container));
+ // Add damage for whatever container arrange_windows() was called with,
+ // unless it was called with the special floating container, in which case
+ // we'll damage the entire output.
+ if (container->type == C_CONTAINER && container->layout == L_FLOATING) {
+ struct sway_container *output = container_parent(container, C_OUTPUT);
+ transaction_add_damage(transaction, container_get_box(output));
+ } else {
+ transaction_add_damage(transaction, container_get_box(container));
+ }
+ add_deleted_containers(transaction);
}
void 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 @@
#include "sway/ipc-server.h"
#include "sway/output.h"
#include "sway/server.h"
-#include "sway/tree/arrange.h"
#include "sway/tree/layout.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
@@ -113,10 +112,11 @@ struct sway_container *container_create(enum sway_container_type type) {
c->layout = L_NONE;
c->type = type;
c->alpha = 1.0f;
+ c->instructions = create_list();
if (type != C_VIEW) {
c->children = create_list();
- //c->pending.children = create_list();
+ c->current.children = create_list();
}
wl_signal_init(&c->events.destroy);
@@ -133,43 +133,68 @@ struct sway_container *container_create(enum sway_container_type type) {
return c;
}
-static void _container_destroy(struct sway_container *cont) {
- if (cont == NULL) {
- return;
- }
-
- wl_signal_emit(&cont->events.destroy, cont);
+static void container_workspace_free(struct sway_workspace *ws) {
+ list_foreach(ws->output_priority, free);
+ list_free(ws->output_priority);
+ ws->floating->destroying = true;
+ container_free(ws->floating);
+ free(ws);
+}
- struct sway_container *parent = cont->parent;
- if (cont->children != NULL && cont->children->length) {
- // remove children until there are no more, container_destroy calls
- // container_remove_child, which removes child from this container
- while (cont->children != NULL && cont->children->length > 0) {
- struct sway_container *child = cont->children->items[0];
- ipc_event_window(child, "close");
- container_remove_child(child);
- _container_destroy(child);
- }
- }
- if (cont->marks) {
- list_foreach(cont->marks, free);
- list_free(cont->marks);
- }
- if (parent) {
- parent = container_remove_child(cont);
+void container_free(struct sway_container *cont) {
+ if (!sway_assert(cont->destroying,
+ "Tried to free container which wasn't marked as destroying")) {
+ return;
}
- if (cont->name) {
- free(cont->name);
+ if (!sway_assert(cont->instructions->length == 0,
+ "Tried to free container with pending instructions")) {
+ return;
}
-
+ free(cont->name);
wlr_texture_destroy(cont->title_focused);
wlr_texture_destroy(cont->title_focused_inactive);
wlr_texture_destroy(cont->title_unfocused);
wlr_texture_destroy(cont->title_urgent);
+ for (int i = 0; i < server.destroying_containers->length; ++i) {
+ if (server.destroying_containers->items[i] == cont) {
+ list_del(server.destroying_containers, i);
+ break;
+ }
+ }
+
+ list_free(cont->instructions);
list_free(cont->children);
- //list_free(cont->pending.children);
- cont->children = NULL;
+ list_free(cont->current.children);
+
+ switch (cont->type) {
+ case C_ROOT:
+ break;
+ case C_OUTPUT:
+ cont->sway_output->swayc = NULL;
+ break;
+ case C_WORKSPACE:
+ container_workspace_free(cont->sway_workspace);
+ break;
+ case C_CONTAINER:
+ break;
+ case C_VIEW:
+ {
+ struct sway_view *view = cont->sway_view;
+ view->swayc = NULL;
+ free(view->title_format);
+ view->title_format = NULL;
+
+ if (view->destroying) {
+ view_free(view);
+ }
+ }
+ break;
+ case C_TYPES:
+ sway_assert(false, "Didn't expect to see C_TYPES here");
+ break;
+ }
+
free(cont);
}
@@ -186,7 +211,6 @@ static struct sway_container *container_workspace_destroy(
}
wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
- ipc_event_window(workspace, "close");
struct sway_container *parent = workspace->parent;
if (!workspace_is_empty(workspace) && output) {
@@ -209,25 +233,6 @@ static struct sway_container *container_workspace_destroy(
container_move_to(floating->children->items[i],
new_workspace->sway_workspace->floating);
}
- arrange_and_commit(new_workspace);
- }
-
- struct sway_workspace *sway_workspace = workspace->sway_workspace;
-
- // This emits the destroy event and also destroys the swayc.
- _container_destroy(workspace);
-
- // Clean up the floating container
- sway_workspace->floating->parent = NULL;
- _container_destroy(sway_workspace->floating);
-
- list_foreach(sway_workspace->output_priority, free);
- list_free(sway_workspace->output_priority);
-
- free(sway_workspace);
-
- if (output) {
- output_damage_whole(output->sway_output);
}
return parent;
@@ -266,14 +271,13 @@ static struct sway_container *container_output_destroy(
container_add_child(new_output, workspace);
ipc_event_workspace(workspace, NULL, "move");
} else {
- container_workspace_destroy(workspace);
+ container_destroy(workspace);
}
container_sort_workspaces(new_output);
}
}
}
- arrange_and_commit(&root_container);
wl_list_remove(&output->sway_output->mode.link);
wl_list_remove(&output->sway_output->transform.link);
@@ -285,12 +289,8 @@ static struct sway_container *container_output_destroy(
output->sway_output->swayc = NULL;
wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
- _container_destroy(output);
- return &root_container;
-}
-static void container_root_finish(struct sway_container *con) {
- wlr_log(L_ERROR, "TODO: destroy the root container");
+ return &root_container;
}
bool container_reap_empty(struct sway_container *con) {
@@ -306,13 +306,13 @@ bool container_reap_empty(struct sway_container *con) {
case C_WORKSPACE:
if (!workspace_is_visible(con) && workspace_is_empty(con)) {
wlr_log(L_DEBUG, "Destroying workspace via reaper");
- container_workspace_destroy(con);
+ container_destroy(con);
return true;
}
break;
case C_CONTAINER:
if (con->children->length == 0) {
- _container_destroy(con);
+ container_destroy(con);
return true;
}
case C_VIEW:
@@ -349,46 +349,48 @@ struct sway_container *container_flatten(struct sway_container *container) {
return container;
}
+/**
+ * container_destroy() is the first step in destroying a container. We'll emit
+ * events, detach it from the tree and mark it as destroying. The container will
+ * remain in memory until it's no longer used by a transaction, then it will be
+ * freed via container_free().
+ */
struct sway_container *container_destroy(struct sway_container *con) {
if (con == NULL) {
return NULL;
}
+ if (con->destroying) {
+ return NULL;
+ }
- struct sway_container *parent = con->parent;
+ // The below functions move their children to somewhere else.
+ if (con->type == C_OUTPUT) {
+ container_output_destroy(con);
+ } else if (con->type == C_WORKSPACE) {
+ // Workspaces will refuse to be destroyed if they're the last workspace
+ // on their output.
+ if (!container_workspace_destroy(con)) {
+ return NULL;
+ }
+ }
- switch (con->type) {
- case C_ROOT:
- container_root_finish(con);
- break;
- case C_OUTPUT:
- // dont try to reap the root after this
- container_output_destroy(con);
- break;
- case C_WORKSPACE:
- // dont try to reap the output after this
- container_workspace_destroy(con);
- break;
- case C_CONTAINER:
- if (con->children->length) {
- for (int i = 0; i < con->children->length; ++i) {
- struct sway_container *child = con->children->items[0];
- ipc_event_window(child, "close");
- container_remove_child(child);
- container_add_child(parent, child);
- }
- }
- ipc_event_window(con, "close");
- _container_destroy(con);
- break;
- case C_VIEW:
- _container_destroy(con);
- break;
- case C_TYPES:
- wlr_log(L_ERROR, "container_destroy called on an invalid "
- "container");
- break;
+ // At this point the container being destroyed shouldn't have any children
+ // unless sway is terminating.
+ if (!server.terminating) {
+ if (!sway_assert(!con->children || con->children->length == 0,
+ "Didn't expect to see children here")) {
+ return NULL;
+ }
}
+ wl_signal_emit(&con->events.destroy, con);
+ ipc_event_window(con, "close");
+
+ struct sway_container *parent = container_remove_child(con);
+
+ con->destroying = true;
+ list_add(server.destroying_containers, con);
+
return container_reap_empty_recursive(parent);
}
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) {
root_container.type = C_ROOT;
root_container.layout = L_NONE;
root_container.name = strdup("root");
+ root_container.instructions = create_list();
root_container.children = create_list();
+ root_container.current.children = create_list();
wl_signal_init(&root_container.events.destroy);
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) {
}
container_sort_workspaces(output);
- arrange_and_commit(&root_container);
}
struct sway_container *output_create(
@@ -66,7 +65,6 @@ struct sway_container *output_create(
struct sway_container *output = container_create(C_OUTPUT);
output->sway_output = sway_output;
- sway_output->swayc = output;
output->name = strdup(name);
if (output->name == NULL) {
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,
view->impl = impl;
view->executed_criteria = create_list();
view->marks = create_list();
- view->instructions = create_list();
wl_signal_init(&view->events.unmap);
}
-void view_destroy(struct sway_view *view) {
- if (view == NULL) {
+void view_free(struct sway_view *view) {
+ if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) {
return;
}
-
- if (view->surface != NULL) {
- view_unmap(view);
+ if (!sway_assert(view->destroying,
+ "Tried to free view which wasn't marked as destroying")) {
+ return;
}
-
- if (!sway_assert(view->instructions->length == 0,
- "Tried to destroy view with pending instructions")) {
+ if (!sway_assert(view->swayc == NULL,
+ "Tried to free view which still has a swayc "
+ "(might have a pending transaction?)")) {
return;
}
-
list_free(view->executed_criteria);
- for (int i = 0; i < view->marks->length; ++i) {
- free(view->marks->items[i]);
- }
+ list_foreach(view->marks, free);
list_free(view->marks);
- list_free(view->instructions);
-
wlr_texture_destroy(view->marks_focused);
wlr_texture_destroy(view->marks_focused_inactive);
wlr_texture_destroy(view->marks_unfocused);
wlr_texture_destroy(view->marks_urgent);
- container_destroy(view->swayc);
-
- if (view->impl->destroy) {
- view->impl->destroy(view);
+ if (view->impl->free) {
+ view->impl->free(view);
} else {
free(view);
}
}
+/**
+ * The view may or may not be involved in a transaction. For example, a view may
+ * unmap then attempt to destroy itself before we've applied the new layout. If
+ * an unmapping view is still involved in a transaction then it'll still have a
+ * swayc.
+ *
+ * If there's no transaction we can simply free the view. Otherwise the
+ * destroying flag will make the view get freed when the transaction is
+ * finished.
+ */
+void view_destroy(struct sway_view *view) {
+ if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
+ return;
+ }
+ view->destroying = true;
+
+ if (!view->swayc) {
+ view_free(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);
@@ -356,6 +369,9 @@ static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
void view_for_each_surface(struct sway_view *view,
wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (!view->surface) {
+ return;
+ }
if (view->impl->for_each_surface) {
view->impl->for_each_surface(view, iterator, user_data);
} else {
@@ -523,11 +539,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
view_handle_container_reparent(&view->container_reparent, NULL);
}
-void view_unmap(struct sway_view *view) {
- if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
- return;
- }
-
+struct sway_container *view_unmap(struct sway_view *view) {
wl_signal_emit(&view->events.unmap, view);
if (view->is_fullscreen) {
@@ -535,22 +547,10 @@ void view_unmap(struct sway_view *view) {
ws->sway_workspace->fullscreen = NULL;
}
- container_damage_whole(view->swayc);
-
wl_list_remove(&view->surface_new_subsurface.link);
wl_list_remove(&view->container_reparent.link);
- struct sway_container *parent = container_destroy(view->swayc);
-
- view->swayc = NULL;
- view->surface = NULL;
-
- if (view->title_format) {
- free(view->title_format);
- view->title_format = NULL;
- }
-
- arrange_and_commit(parent);
+ return container_destroy(view->swayc);
}
void view_update_position(struct sway_view *view, double lx, double ly) {
@@ -924,7 +924,7 @@ void view_update_marks_textures(struct sway_view *view) {
}
bool view_is_visible(struct sway_view *view) {
- if (!view->swayc) {
+ if (!view->swayc || view->swayc->destroying) {
return false;
}
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) {
}
bool workspace_is_visible(struct sway_container *ws) {
+ if (ws->destroying) {
+ return false;
+ }
struct sway_container *output = container_parent(ws, C_OUTPUT);
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus = seat_get_focus_inactive(seat, output);