diff --git a/include/sway/output.h b/include/sway/output.h index 3ee4ca7ac..d1388695c 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "config.h" #include "sway/tree/node.h" #include "sway/tree/view.h" @@ -18,7 +19,21 @@ struct sway_output_state { struct sway_output { struct sway_node node; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + } layers; + + // when a container is fullscreen, in case the fullscreen surface is + // translucent (can see behind) we must make sure that the background is a + // solid color in order to conform to the wayland protocol. This rect + // ensures that when looking through a surface, all that will be seen + // is black. + struct wlr_scene_rect *fullscreen_background; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; struct sway_server *server; struct wl_list link; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 751612e2c..271649a45 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "list.h" #include "sway/tree/node.h" @@ -68,6 +69,29 @@ struct sway_container { struct sway_node node; struct sway_view *view; + struct wlr_scene_tree *scene_tree; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_rect *border; + struct wlr_scene_rect *background; + + struct wlr_scene_buffer *title_buffer; + struct wlr_scene_buffer *marks_buffer; + } title_bar; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_rect *top; + struct wlr_scene_rect *bottom; + struct wlr_scene_rect *left; + struct wlr_scene_rect *right; + } border; + + struct wlr_scene_tree *content_tree; + struct sway_container_state current; struct sway_container_state pending; diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 470ee3b5e..ce3a96b6a 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -1,6 +1,7 @@ #ifndef _SWAY_NODE_H #define _SWAY_NODE_H #include +#include #include "list.h" #define MIN_SANE_W 100 @@ -74,4 +75,15 @@ list_t *node_get_children(struct sway_node *node); bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); +// when destroying a sway tree, it's not known which order the tree will be +// destroyed. To prevent freeing of scene_nodes recursing up the tree, +// let's use this helper function to disown them to the staging node. +void scene_node_disown_children(struct wlr_scene_tree *tree); + +// a helper function used to allocate tree nodes. If an allocation failure +// occurs a flag is flipped that can be checked later to destroy a parent +// of this scene node preventing memory leaks. +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed); + #endif diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index af4124a18..200f1c73a 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "sway/tree/container.h" #include "sway/tree/node.h" #include "config.h" @@ -16,6 +17,31 @@ struct sway_root { struct wlr_output_layout *output_layout; struct wl_listener output_layout_change; + + // scene node layout: + // - root + // - staging + // - layer shell stuff + // - tiling + // - floating + // - fullscreen stuff + // - seat stuff + // - ext_session_lock + struct wlr_scene *root_scene; + + // since wlr_scene nodes can't be orphaned and must always + // have a parent, use this staging scene_tree so that a + // node always have a valid parent. Nothing in this + // staging node will be visible. + struct wlr_scene_tree *staging; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *floating; + struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *fullscreen_global; + } layers; + #if HAVE_XWAYLAND struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index b63936ad6..48dc53368 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -2,6 +2,7 @@ #define _SWAY_VIEW_H #include #include +#include #include "config.h" #if HAVE_XWAYLAND #include @@ -69,6 +70,9 @@ struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *content_tree; + struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views struct sway_xdg_decoration *xdg_decoration; diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index b3d93a813..e64f219f0 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -2,6 +2,7 @@ #define _SWAY_WORKSPACE_H #include +#include #include "sway/tree/container.h" #include "sway/tree/node.h" @@ -22,6 +23,12 @@ struct sway_workspace_state { struct sway_workspace { struct sway_node node; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + } layers; + struct sway_container *fullscreen; char *name; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8962c0aef..cb2b3f13f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -791,6 +791,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->mode.link); wl_list_remove(&output->present.link); + wlr_scene_output_destroy(output->scene_output); + output->scene_output = NULL; output->wlr_output->data = NULL; output->wlr_output = NULL; @@ -898,11 +900,24 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } - struct sway_output *output = output_create(wlr_output); - if (!output) { + // Create the scene output here so we're not accidentally creating one for + // the fallback output + struct wlr_scene_output *scene_output = + wlr_scene_output_create(root->root_scene, wlr_output); + if (!scene_output) { + sway_log(SWAY_ERROR, "Failed to create a scene output"); return; } + + struct sway_output *output = output_create(wlr_output); + if (!output) { + sway_log(SWAY_ERROR, "Failed to create a sway output"); + wlr_scene_output_destroy(scene_output); + return; + } + output->server = server; + output->scene_output = scene_output; output->damage = wlr_output_damage_create(wlr_output); wl_signal_add(&wlr_output->events.destroy, &output->destroy); diff --git a/sway/tree/container.c b/sway/tree/container.c index bf7085cbd..d75b2d16d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -30,6 +30,23 @@ #include "log.h" #include "stringop.h" +static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, + bool *failed) { + if (*failed) { + return NULL; + } + + // just pass in random values. These can and will be overwritten when + // they need to be used. + struct wlr_scene_rect *rect = wlr_scene_rect_create( + parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1}); + if (!rect) { + *failed = true; + } + + return rect; +} + struct sway_container *container_create(struct sway_view *view) { struct sway_container *c = calloc(1, sizeof(struct sway_container)); if (!c) { @@ -37,14 +54,49 @@ struct sway_container *container_create(struct sway_view *view) { return NULL; } node_init(&c->node, N_CONTAINER, c); - c->pending.layout = L_NONE; - c->view = view; - c->alpha = 1.0f; - if (!view) { + // Container tree structure + // - scene tree + // - title bar + // - border + // - background + // - title text + // - marks text + // - border + // - border top/bottom/left/right + // - content_tree (we put the content node here so when we disable the + // border everything gets disabled. We only render the content iff there + // is a border as well) + bool alloc_failure = false; + c->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); + + c->title_bar.tree = alloc_scene_tree(c->scene_node, &alloc_failure); + c->title_bar.border = alloc_rect_node(c->title_bar.node, &alloc_failure); + c->title_bar.background = alloc_rect_node(c->title_bar.node, &alloc_failure); + + c->border.tree = alloc_scene_tree(c->scene_node, &alloc_failure); + c->content_tree = alloc_scene_tree(c->border.node, &alloc_failure); + + if (view) { + // only containers with views can have borders + c->border.top = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.bottom = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.left = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.right = alloc_rect_node(c->border.tree, &alloc_failure); + } else { c->pending.children = create_list(); c->current.children = create_list(); } + + if (alloc_failure) { + wlr_scene_node_destroy(&c->scene_tree->node); + free(c); + return NULL; + } + + c->pending.layout = L_NONE; + c->view = view; + c->alpha = 1.0f; c->marks = create_list(); c->outputs = create_list(); @@ -88,6 +140,8 @@ void container_destroy(struct sway_container *con) { } } + scene_node_disown_children(con->content_node); + wlr_scene_node_destroy(con->scene_node); free(con); } diff --git a/sway/tree/node.c b/sway/tree/node.c index bc7e2aa59..a08df5096 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -159,3 +159,26 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { } return false; } + +void scene_node_disown_children(struct wlr_scene_tree *tree) { + struct wlr_scene_node *child, *tmp_child; + wl_list_for_each_safe(child, tmp_child, &tree->children, link) { + wlr_scene_node_reparent(child, root->staging); + } +} + +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed) { + // fallthrough + if (*failed) { + return NULL; + } + + struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + if (!tree) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + *failed = true; + } + + return tree; +} diff --git a/sway/tree/output.c b/sway/tree/output.c index 5a3fd8b6e..2984a5a32 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -88,10 +88,53 @@ static void restore_workspaces(struct sway_output *output) { output_sort_workspaces(output); } +static void destroy_scene_layers(struct sway_output *output) { + wlr_scene_node_destroy(&output->fullscreen_background->node); + + scene_node_disown_children(output->layers.tiling); + scene_node_disown_children(output->layers.fullscreen); + + size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *); + for (size_t i = 0; i < num_layers; i++) { + struct wlr_scene_tree *tree = + ((struct wlr_scene_tree **) &output->layers)[i]; + + if (tree) { + wlr_scene_node_destroy(&tree->node); + } + } +} + struct sway_output *output_create(struct wlr_output *wlr_output) { struct sway_output *output = calloc(1, sizeof(struct sway_output)); node_init(&output->node, N_OUTPUT, output); + + bool alloc_failure = false; + size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *); + for (size_t i = 0; i < num_layers; i++) { + ((struct wlr_scene_tree **) &output->layers)[i] = + alloc_scene_tree(root->staging, &alloc_failure); + } + + if (!alloc_failure) { + output->fullscreen_background = wlr_scene_rect_create( + output->layers.fullscreen, 0, 0, (float[4]){0., 0., 0., 1.}); + + if (!output->fullscreen_background) { + sway_log(SWAY_ERROR, "Unable to allocate a background rect"); + alloc_failure = true; + } + } + + if (alloc_failure) { + destroy_scene_layers(output); + wlr_scene_output_destroy(output->scene_output); + free(output); + return NULL; + } + output->wlr_output = wlr_output; + wlr_output->data = output; output->detected_subpixel = wlr_output->subpixel; output->scale_filter = SCALE_FILTER_NEAREST; @@ -239,6 +282,8 @@ void output_destroy(struct sway_output *output) { "which is still referenced by transactions")) { return; } + + destroy_scene_layers(output); list_free(output->workspaces); list_free(output->current.workspaces); wl_event_source_remove(output->repaint_timer); diff --git a/sway/tree/root.c b/sway/tree/root.c index 7df0b2378..b7dbf2490 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" @@ -29,7 +30,34 @@ struct sway_root *root_create(void) { sway_log(SWAY_ERROR, "Unable to allocate sway_root"); return NULL; } + + struct wlr_scene *root_scene = wlr_scene_create(); + if (!root_scene) { + sway_log(SWAY_ERROR, "Unable to allocate root scene node"); + free(root); + return NULL; + } + node_init(&root->node, N_ROOT, root); + root->root_scene = root_scene; + + bool alloc_failure = false; + root->staging = alloc_scene_tree(&root_scene->tree, &alloc_failure); + + size_t num_layers = sizeof(root->layers) / sizeof(struct wlr_scene_tree *); + for (size_t i = 0; i < num_layers; i++) { + ((struct wlr_scene_tree **) &root->layers)[i] = + alloc_scene_tree(&root_scene->tree, &alloc_failure); + } + + if (alloc_failure) { + wlr_scene_node_destroy(&root_scene->tree.node); + free(root); + return NULL; + } + + wlr_scene_node_set_enabled(&root->staging->node, false); + root->output_layout = wlr_output_layout_create(); wl_list_init(&root->all_outputs); #if HAVE_XWAYLAND @@ -51,6 +79,7 @@ void root_destroy(struct sway_root *root) { wl_list_remove(&root->output_layout_change.link); list_free(root->scratchpad); list_free(root->outputs); + wlr_scene_node_destroy(&root->root_scene->tree.node); wlr_output_layout_destroy(root->output_layout); free(root); } diff --git a/sway/tree/view.c b/sway/tree/view.c index 9a24cfbe5..ac6993ffd 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -35,6 +35,14 @@ bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { + bool alloc_failure = false; + view->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); + view->content_tree = alloc_scene_tree(view->scene_tree, &alloc_failure); + if (alloc_failure) { + wlr_scene_node_destroy(&view->scene_tree->node); + return false; + } + view->type = type; view->impl = impl; view->executed_criteria = create_list(); @@ -64,6 +72,7 @@ void view_destroy(struct sway_view *view) { } list_free(view->executed_criteria); + wlr_scene_node_destroy(&view->scene_tree->node); free(view->title_format); if (view->impl->destroy) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index c84320bda..c0aa651a0 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -69,6 +69,18 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); + + bool alloc_failure = false; + ws->layers.tiling = alloc_scene_tree(root->staging, &alloc_failure); + ws->layers.fullscreen = alloc_scene_tree(root->staging, &alloc_failure); + + if (alloc_failure) { + wlr_scene_node_destroy(&ws->layers.tiling->node); + wlr_scene_node_destroy(&ws->layers.fullscreen->node); + free(ws); + return NULL; + } + ws->name = name ? strdup(name) : NULL; ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); @@ -129,6 +141,11 @@ void workspace_destroy(struct sway_workspace *workspace) { return; } + scene_node_disown_children(workspace->layers.tiling); + scene_node_disown_children(workspace->layers.fullscreen); + wlr_scene_node_destroy(&workspace->layers.tiling->node); + wlr_scene_node_destroy(&workspace->layers.fullscreen->node); + free(workspace->name); free(workspace->representation); list_free_items_and_destroy(workspace->output_priority);