scene_graph: Maintain wlr_scene_nodes for the sway tree.

This commit is contained in:
Alexander Orzechowski 2022-08-31 23:42:15 -04:00 committed by Kirill Primak
parent a33ede4c43
commit 25da7b237d
13 changed files with 286 additions and 6 deletions

View file

@ -4,6 +4,7 @@
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#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;

View file

@ -3,6 +3,7 @@
#include <stdint.h>
#include <sys/types.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
#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;

View file

@ -1,6 +1,7 @@
#ifndef _SWAY_NODE_H
#define _SWAY_NODE_H
#include <stdbool.h>
#include <wlr/types/wlr_scene.h>
#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

View file

@ -4,6 +4,7 @@
#include <wayland-util.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/render/wlr_texture.h>
#include <wlr/types/wlr_scene.h>
#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

View file

@ -2,6 +2,7 @@
#define _SWAY_VIEW_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
#include "config.h"
#if HAVE_XWAYLAND
#include <wlr/xwayland.h>
@ -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;

View file

@ -2,6 +2,7 @@
#define _SWAY_WORKSPACE_H
#include <stdbool.h>
#include <wlr/types/wlr_scene.h>
#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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#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);
}

View file

@ -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) {

View file

@ -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);