forked from swaywm/wlroots
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A new wlr_scene API has been added, following the design ideas from [1]. The new API contains the minimal set of features required to make the API useful. The goal is to design a solid fundation and add more features in the future. [1]: swaywm#1826 (comment)
- Loading branch information
Showing
3 changed files
with
378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* This an unstable interface of wlroots. No guarantees are made regarding the | ||
* future consistency of this API. | ||
*/ | ||
#ifndef WLR_USE_UNSTABLE | ||
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" | ||
#endif | ||
|
||
#ifndef WLR_TYPES_WLR_SCENE_H | ||
#define WLR_TYPES_WLR_SCENE_H | ||
|
||
/** | ||
* The scene-graph API provides a declarative way to display surfaces. The | ||
* compositor creates a scene, adds surfaces, then renders the scene on | ||
* outputs. | ||
* | ||
* The scene-graph API only supports basic 2D composition operations (like the | ||
* KMS API or the Wayland protocol does). For anything more complicated, | ||
* compositors need to implement custom rendering logic. | ||
*/ | ||
|
||
#include <pixman.h> | ||
#include <wayland-server-core.h> | ||
#include <wlr/types/wlr_surface.h> | ||
|
||
struct wlr_output; | ||
|
||
enum wlr_scene_node_type { | ||
WLR_SCENE_NODE_ROOT, | ||
WLR_SCENE_NODE_SURFACE, | ||
}; | ||
|
||
struct wlr_scene_node_state { | ||
struct wl_list link; // wlr_scene_node_state.children | ||
|
||
struct wl_list children; // wlr_scene_node_state.link | ||
|
||
int x, y; // relative to parent | ||
}; | ||
|
||
/** A node is an object in the scene. */ | ||
struct wlr_scene_node { | ||
enum wlr_scene_node_type type; | ||
struct wlr_scene_node *parent; | ||
struct wlr_scene_node_state state; | ||
|
||
struct { | ||
struct wl_signal destroy; | ||
} events; | ||
}; | ||
|
||
/** The root scene-graph node. */ | ||
struct wlr_scene { | ||
struct wlr_scene_node node; | ||
}; | ||
|
||
/** A scene-graph node displaying a single surface. */ | ||
struct wlr_scene_surface { | ||
struct wlr_scene_node node; | ||
struct wlr_surface *surface; | ||
|
||
// private state | ||
|
||
struct wl_listener surface_destroy; | ||
}; | ||
|
||
/** | ||
* Immediately destroy the scene-graph node. | ||
*/ | ||
void wlr_scene_node_destroy(struct wlr_scene_node *node); | ||
/** | ||
* Set the position of the node relative to its parent. | ||
*/ | ||
void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y); | ||
/** | ||
* Move the node right above the specified sibling. | ||
*/ | ||
void wlr_scene_node_place_above(struct wlr_scene_node *node, | ||
struct wlr_scene_node *sibling); | ||
/** | ||
* Move the node right below the specified sibling. | ||
*/ | ||
void wlr_scene_node_place_below(struct wlr_scene_node *node, | ||
struct wlr_scene_node *sibling); | ||
/** | ||
* Call `iterator` on each surface in the scene-graph, with the surface's | ||
* position in layout coordinates. The function is called from root to leaves | ||
* (in rendering order). | ||
*/ | ||
void wlr_scene_node_for_each_surface(struct wlr_scene_node *node, | ||
wlr_surface_iterator_func_t iterator, void *user_data); | ||
|
||
/** | ||
* Create a new scene-graph. | ||
*/ | ||
struct wlr_scene *wlr_scene_create(void); | ||
/** | ||
* Manually render the scene-graph on an output. The compositor needs to call | ||
* wlr_renderer_begin before and wlr_renderer_end after calling this function. | ||
* Damage is given in output-buffer-local coordinates and can be set to NULL to | ||
* disable damage tracking. | ||
*/ | ||
void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, | ||
int lx, int ly, pixman_region32_t *damage); | ||
|
||
/** | ||
* Add a node displaying a single surface to the scene-graph. | ||
* | ||
* The child sub-surfaces are ignored. | ||
*/ | ||
struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent, | ||
struct wlr_surface *surface); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,263 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
#include <wlr/backend.h> | ||
#include <wlr/render/wlr_renderer.h> | ||
#include <wlr/types/wlr_matrix.h> | ||
#include <wlr/types/wlr_scene.h> | ||
#include <wlr/types/wlr_surface.h> | ||
#include "util/signal.h" | ||
|
||
static struct wlr_scene *scene_root_from_node(struct wlr_scene_node *node) { | ||
assert(node->type == WLR_SCENE_NODE_ROOT); | ||
return (struct wlr_scene *)node; | ||
} | ||
|
||
static struct wlr_scene_surface *scene_surface_from_node( | ||
struct wlr_scene_node *node) { | ||
assert(node->type == WLR_SCENE_NODE_SURFACE); | ||
return (struct wlr_scene_surface *)node; | ||
} | ||
|
||
static void scene_node_state_init(struct wlr_scene_node_state *state) { | ||
wl_list_init(&state->children); | ||
wl_list_init(&state->link); | ||
} | ||
|
||
static void scene_node_state_finish(struct wlr_scene_node_state *state) { | ||
wl_list_remove(&state->link); | ||
} | ||
|
||
static void scene_node_init(struct wlr_scene_node *node, | ||
enum wlr_scene_node_type type, struct wlr_scene_node *parent) { | ||
assert(type == WLR_SCENE_NODE_ROOT || parent != NULL); | ||
assert(parent == NULL || parent->type == WLR_SCENE_NODE_ROOT); | ||
|
||
node->type = type; | ||
node->parent = parent; | ||
scene_node_state_init(&node->state); | ||
wl_signal_init(&node->events.destroy); | ||
|
||
if (parent != NULL) { | ||
wl_list_insert(parent->state.children.prev, &node->state.link); | ||
} | ||
} | ||
|
||
static void scene_node_finish(struct wlr_scene_node *node) { | ||
wlr_signal_emit_safe(&node->events.destroy, NULL); | ||
|
||
struct wlr_scene_node *child, *child_tmp; | ||
wl_list_for_each_safe(child, child_tmp, | ||
&node->state.children, state.link) { | ||
wlr_scene_node_destroy(child); | ||
} | ||
|
||
scene_node_state_finish(&node->state); | ||
} | ||
|
||
void wlr_scene_node_destroy(struct wlr_scene_node *node) { | ||
if (node == NULL) { | ||
return; | ||
} | ||
|
||
scene_node_finish(node); | ||
|
||
switch (node->type) { | ||
case WLR_SCENE_NODE_ROOT:; | ||
struct wlr_scene *scene = scene_root_from_node(node); | ||
free(scene); | ||
break; | ||
case WLR_SCENE_NODE_SURFACE:; | ||
struct wlr_scene_surface *scene_surface = scene_surface_from_node(node); | ||
wl_list_remove(&scene_surface->surface_destroy.link); | ||
free(scene_surface); | ||
break; | ||
} | ||
} | ||
|
||
struct wlr_scene *wlr_scene_create(void) { | ||
struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene)); | ||
if (scene == NULL) { | ||
return NULL; | ||
} | ||
scene_node_init(&scene->node, WLR_SCENE_NODE_ROOT, NULL); | ||
|
||
return scene; | ||
} | ||
|
||
static void scene_surface_handle_surface_destroy(struct wl_listener *listener, | ||
void *data) { | ||
struct wlr_scene_surface *scene_surface = | ||
wl_container_of(listener, scene_surface, surface_destroy); | ||
wlr_scene_node_destroy(&scene_surface->node); | ||
} | ||
|
||
struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent, | ||
struct wlr_surface *surface) { | ||
struct wlr_scene_surface *scene_surface = | ||
calloc(1, sizeof(struct wlr_scene_surface)); | ||
if (scene_surface == NULL) { | ||
return NULL; | ||
} | ||
scene_node_init(&scene_surface->node, WLR_SCENE_NODE_SURFACE, parent); | ||
|
||
scene_surface->surface = surface; | ||
|
||
scene_surface->surface_destroy.notify = scene_surface_handle_surface_destroy; | ||
wl_signal_add(&surface->events.destroy, &scene_surface->surface_destroy); | ||
|
||
return scene_surface; | ||
} | ||
|
||
void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { | ||
node->state.x = x; | ||
node->state.y = y; | ||
} | ||
|
||
void wlr_scene_node_place_above(struct wlr_scene_node *node, | ||
struct wlr_scene_node *sibling) { | ||
assert(node->parent == sibling->parent); | ||
|
||
wl_list_remove(&node->state.link); | ||
wl_list_insert(&sibling->state.link, &node->state.link); | ||
} | ||
|
||
void wlr_scene_node_place_below(struct wlr_scene_node *node, | ||
struct wlr_scene_node *sibling) { | ||
assert(node->parent == sibling->parent); | ||
|
||
wl_list_remove(&node->state.link); | ||
wl_list_insert(sibling->state.link.prev, &node->state.link); | ||
} | ||
|
||
static void scene_node_for_each_surface(struct wlr_scene_node *node, | ||
int lx, int ly, wlr_surface_iterator_func_t user_iterator, | ||
void *user_data) { | ||
lx += node->state.x; | ||
ly += node->state.y; | ||
|
||
if (node->type == WLR_SCENE_NODE_SURFACE) { | ||
struct wlr_scene_surface *scene_surface = scene_surface_from_node(node); | ||
user_iterator(scene_surface->surface, lx, ly, user_data); | ||
} | ||
|
||
struct wlr_scene_node *child; | ||
wl_list_for_each(child, &node->state.children, state.link) { | ||
scene_node_for_each_surface(child, lx, ly, user_iterator, user_data); | ||
} | ||
} | ||
|
||
void wlr_scene_node_for_each_surface(struct wlr_scene_node *node, | ||
wlr_surface_iterator_func_t user_iterator, void *user_data) { | ||
scene_node_for_each_surface(node, 0, 0, user_iterator, user_data); | ||
} | ||
|
||
static int scale_length(int length, int offset, float scale) { | ||
return round((offset + length) * scale) - round(offset * scale); | ||
} | ||
|
||
static void scale_box(struct wlr_box *box, float scale) { | ||
box->width = scale_length(box->width, box->x, scale); | ||
box->height = scale_length(box->height, box->y, scale); | ||
box->x = round(box->x * scale); | ||
box->y = round(box->y * scale); | ||
} | ||
|
||
static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { | ||
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); | ||
assert(renderer); | ||
|
||
struct wlr_box box = { | ||
.x = rect->x1, | ||
.y = rect->y1, | ||
.width = rect->x2 - rect->x1, | ||
.height = rect->y2 - rect->y1, | ||
}; | ||
|
||
int ow, oh; | ||
wlr_output_transformed_resolution(output, &ow, &oh); | ||
|
||
enum wl_output_transform transform = | ||
wlr_output_transform_invert(output->transform); | ||
wlr_box_transform(&box, &box, transform, ow, oh); | ||
|
||
wlr_renderer_scissor(renderer, &box); | ||
} | ||
|
||
static void render_texture(struct wlr_output *output, | ||
pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
const struct wlr_box *box, const float matrix[static 9]) { | ||
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); | ||
assert(renderer); | ||
|
||
pixman_region32_t damage; | ||
pixman_region32_init(&damage); | ||
pixman_region32_init_rect(&damage, box->x, box->y, box->width, box->height); | ||
pixman_region32_intersect(&damage, &damage, output_damage); | ||
if (pixman_region32_not_empty(&damage)) { | ||
int nrects; | ||
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
for (int i = 0; i < nrects; ++i) { | ||
scissor_output(output, &rects[i]); | ||
wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0); | ||
} | ||
} | ||
pixman_region32_fini(&damage); | ||
} | ||
|
||
struct render_data { | ||
struct wlr_output *output; | ||
pixman_region32_t *damage; | ||
}; | ||
|
||
static void render_surface_iterator(struct wlr_surface *surface, | ||
int x, int y, void *_data) { | ||
struct render_data *data = _data; | ||
struct wlr_output *output = data->output; | ||
pixman_region32_t *output_damage = data->damage; | ||
|
||
struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
if (texture == NULL) { | ||
return; | ||
} | ||
|
||
struct wlr_box box = { | ||
.x = x, | ||
.y = y, | ||
.width = surface->current.width, | ||
.height = surface->current.height, | ||
}; | ||
scale_box(&box, output->scale); | ||
|
||
float matrix[9]; | ||
enum wl_output_transform transform = | ||
wlr_output_transform_invert(surface->current.transform); | ||
wlr_matrix_project_box(matrix, &box, transform, 0.0, | ||
output->transform_matrix); | ||
|
||
render_texture(output, output_damage, texture, &box, matrix); | ||
} | ||
|
||
void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, | ||
int lx, int ly, pixman_region32_t *damage) { | ||
pixman_region32_t full_region; | ||
pixman_region32_init_rect(&full_region, 0, 0, output->width, output->height); | ||
if (damage == NULL) { | ||
damage = &full_region; | ||
} | ||
|
||
struct wlr_renderer *renderer = | ||
wlr_backend_get_renderer(output->backend); | ||
assert(renderer); | ||
|
||
if (output->enabled && pixman_region32_not_empty(damage)) { | ||
struct render_data data = { | ||
.output = output, | ||
.damage = damage, | ||
}; | ||
scene_node_for_each_surface(&scene->node, lx, ly, | ||
render_surface_iterator, &data); | ||
wlr_renderer_scissor(renderer, NULL); | ||
} | ||
|
||
pixman_region32_fini(&full_region); | ||
} |