diff --git a/examples/scene-graph.c b/examples/scene-graph.c index f57ef1f2d..98f98cfd4 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -99,7 +99,7 @@ static void surface_handle_commit(struct wl_listener *listener, void *data) { static void surface_handle_destroy(struct wl_listener *listener, void *data) { struct surface *surface = wl_container_of(listener, surface, destroy); - wlr_scene_node_destroy(&surface->scene_surface->node); + wlr_scene_node_destroy(&surface->scene_surface->buffer->node); wlr_scene_node_destroy(&surface->border->node); wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->link); @@ -129,7 +129,7 @@ static void server_handle_new_surface(struct wl_listener *listener, surface->scene_surface = wlr_scene_surface_create(&server->scene->node, wlr_surface); - wlr_scene_node_set_position(&surface->scene_surface->node, + wlr_scene_node_set_position(&surface->scene_surface->buffer->node, pos + border_width, pos + border_width); } diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 020881506..f33025745 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -43,7 +43,6 @@ typedef void (*wlr_scene_buffer_iterator_func_t)( enum wlr_scene_node_type { WLR_SCENE_NODE_ROOT, WLR_SCENE_NODE_TREE, - WLR_SCENE_NODE_SURFACE, WLR_SCENE_NODE_RECT, WLR_SCENE_NODE_BUFFER, }; @@ -93,21 +92,17 @@ struct wlr_scene_tree { /** A scene-graph node displaying a single surface. */ struct wlr_scene_surface { - struct wlr_scene_node node; + struct wlr_scene_buffer *buffer; struct wlr_surface *surface; - /** - * The output that the largest area of this surface is displayed on. - * This may be NULL if the surface is not currently displayed on any - * outputs. This is the output that should be used for frame callbacks, - * presentation feedback, etc. - */ - struct wlr_output *primary_output; - // private state - int prev_width, prev_height; + struct wlr_addon addon; + struct wl_listener output_enter; + struct wl_listener output_leave; + struct wl_listener output_present; + struct wl_listener frame_done; struct wl_listener surface_destroy; struct wl_listener surface_commit; }; @@ -276,10 +271,15 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_node *parent); struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent, struct wlr_surface *surface); -struct wlr_scene_surface *wlr_scene_surface_from_node(struct wlr_scene_node *node); - struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *node); +/** + * If this buffer is backed by a surface, then the wlr_scene_surface is + * returned. If not, NULL will be returned. + */ +struct wlr_scene_surface *wlr_scene_surface_from_buffer( + struct wlr_scene_buffer *scene_buffer); + /** * Add a node displaying a solid-colored rectangle to the scene-graph. */ diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 783cc1828..bf3fb4339 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -341,10 +341,17 @@ static struct tinywl_view *desktop_view_at( * surface in the surface tree of a tinywl_view. */ struct wlr_scene_node *node = wlr_scene_node_at( &server->scene->node, lx, ly, sx, sy); - if (node == NULL || node->type != WLR_SCENE_NODE_SURFACE) { + if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { return NULL; } - *surface = wlr_scene_surface_from_node(node)->surface; + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_buffer(scene_buffer); + if (!scene_surface) { + return NULL; + } + + *surface = scene_surface->surface; /* Find the node corresponding to the tinywl_view at the root of this * surface tree, it is the only one for which we set the data field. */ while (node != NULL && node->data == NULL) { diff --git a/types/meson.build b/types/meson.build index 199815a90..2900561e1 100644 --- a/types/meson.build +++ b/types/meson.build @@ -8,6 +8,7 @@ wlr_files += files( 'output/render.c', 'output/transform.c', 'scene/subsurface_tree.c', + 'scene/surface.c', 'scene/wlr_scene.c', 'scene/output_layout.c', 'scene/xdg_shell.c', diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c index 8384db8fb..1cb2977c6 100644 --- a/types/scene/subsurface_tree.c +++ b/types/scene/subsurface_tree.c @@ -81,9 +81,9 @@ static void subsurface_tree_reconfigure( } if (prev != NULL) { - wlr_scene_node_place_above(&subsurface_tree->scene_surface->node, prev); + wlr_scene_node_place_above(&subsurface_tree->scene_surface->buffer->node, prev); } - prev = &subsurface_tree->scene_surface->node; + prev = &subsurface_tree->scene_surface->buffer->node; wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { diff --git a/types/scene/surface.c b/types/scene/surface.c new file mode 100644 index 000000000..e6a8b6b6c --- /dev/null +++ b/types/scene/surface.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include "types/wlr_scene.h" + +static void handle_scene_buffer_output_enter( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, output_enter); + struct wlr_scene_output *output = data; + + wlr_surface_send_enter(surface->surface, output->output); +} + +static void handle_scene_buffer_output_leave( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, output_leave); + struct wlr_scene_output *output = data; + + wlr_surface_send_leave(surface->surface, output->output); +} + +static void handle_scene_buffer_output_present( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, output_present); + struct wlr_scene_output *scene_output = data; + + if (surface->buffer->primary_output == scene_output) { + struct wlr_scene *root = scene_node_get_root(&surface->buffer->node); + struct wlr_presentation *presentation = root->presentation; + + if (presentation) { + wlr_presentation_surface_sampled_on_output( + presentation, surface->surface, scene_output->output); + } + } +} + +static void handle_scene_buffer_frame_done( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, frame_done); + struct timespec *now = data; + + wlr_surface_send_frame_done(surface->surface, now); +} + +static void scene_surface_handle_surface_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, surface_destroy); + + wlr_scene_node_destroy(&surface->buffer->node); +} + +static void set_buffer_with_surface_state(struct wlr_scene_buffer *scene_buffer, + struct wlr_surface *surface) { + struct wlr_surface_state *state = &surface->current; + + struct wlr_fbox src_box; + wlr_surface_get_buffer_source_box(surface, &src_box); + wlr_scene_buffer_set_source_box(scene_buffer, &src_box); + + wlr_scene_buffer_set_dest_size(scene_buffer, state->width, state->height); + wlr_scene_buffer_set_transform(scene_buffer, state->transform); + + if (surface->buffer) { + wlr_scene_buffer_set_buffer_with_damage(scene_buffer, + &surface->buffer->base, &surface->buffer_damage); + } else { + wlr_scene_buffer_set_buffer(scene_buffer, NULL); + } +} + +static void handle_scene_surface_surface_commit( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, surface_commit); + struct wlr_scene_buffer *scene_buffer = surface->buffer; + + set_buffer_with_surface_state(scene_buffer, surface->surface); + + // Even if the surface hasn't submitted damage, schedule a new frame if + // the client has requested a wl_surface.frame callback. Check if the node + // is visible. If not, the client will never receive a frame_done event + // anyway so it doesn't make sense to schedule here. + int lx, ly; + bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly); + + if (!wl_list_empty(&surface->surface->current.frame_callback_list) && + surface->buffer->primary_output != NULL && enabled) { + wlr_output_schedule_frame(surface->buffer->primary_output->output); + } +} + +static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buffer, + int sx, int sy) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_buffer(scene_buffer); + + return wlr_surface_point_accepts_input(scene_surface->surface, sx, sy); +} + +static void surface_addon_destroy(struct wlr_addon *addon) { + struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); + + wlr_addon_finish(&surface->addon); + + wl_list_remove(&surface->output_enter.link); + wl_list_remove(&surface->output_leave.link); + wl_list_remove(&surface->output_present.link); + wl_list_remove(&surface->frame_done.link); + wl_list_remove(&surface->surface_destroy.link); + wl_list_remove(&surface->surface_commit.link); + + free(surface); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wlr_scene_surface", + .destroy = surface_addon_destroy, +}; + +struct wlr_scene_surface *wlr_scene_surface_from_buffer( + struct wlr_scene_buffer *scene_buffer) { + struct wlr_addon *addon = wlr_addon_find(&scene_buffer->node.addons, + scene_buffer, &surface_addon_impl); + if (!addon) { + return NULL; + } + + struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); + return surface; +} + +struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent, + struct wlr_surface *wlr_surface) { + struct wlr_scene_surface *surface = calloc(1, sizeof(*surface)); + if (surface == NULL) { + return NULL; + } + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(parent, NULL); + if (!scene_buffer) { + free(surface); + return NULL; + } + + surface->buffer = scene_buffer; + surface->surface = wlr_surface; + scene_buffer->point_accepts_input = scene_buffer_point_accepts_input; + + surface->output_enter.notify = handle_scene_buffer_output_enter; + wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter); + + surface->output_leave.notify = handle_scene_buffer_output_leave; + wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave); + + surface->output_present.notify = handle_scene_buffer_output_present; + wl_signal_add(&scene_buffer->events.output_present, &surface->output_present); + + surface->frame_done.notify = handle_scene_buffer_frame_done; + wl_signal_add(&scene_buffer->events.frame_done, &surface->frame_done); + + surface->surface_destroy.notify = scene_surface_handle_surface_destroy; + wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy); + + surface->surface_commit.notify = handle_scene_surface_surface_commit; + wl_signal_add(&wlr_surface->events.commit, &surface->surface_commit); + + wlr_addon_init(&surface->addon, &scene_buffer->node.addons, + scene_buffer, &surface_addon_impl); + + set_buffer_with_surface_state(scene_buffer, wlr_surface); + + return surface; +} diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index f4fab207c..2435edefe 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -18,12 +18,6 @@ static struct wlr_scene *scene_root_from_node(struct wlr_scene_node *node) { return (struct wlr_scene *)node; } -struct wlr_scene_surface *wlr_scene_surface_from_node( - struct wlr_scene_node *node) { - assert(node->type == WLR_SCENE_NODE_SURFACE); - return (struct wlr_scene_surface *)node; -} - static struct wlr_scene_rect *scene_rect_from_node( struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_RECT); @@ -96,18 +90,6 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { wl_list_remove(&scene->presentation_destroy.link); break; - case WLR_SCENE_NODE_SURFACE:; - struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_node(node); - - wl_list_for_each(scene_output, &scene->outputs, link) { - // This is a noop if wlr_surface_send_enter() wasn't previously called for - // the given output. - wlr_surface_send_leave(scene_surface->surface, scene_output->output); - } - - wl_list_remove(&scene_surface->surface_commit.link); - wl_list_remove(&scene_surface->surface_destroy.link); - break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); @@ -161,58 +143,8 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_node *parent) { return tree; } -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); -} - static void scene_node_get_size(struct wlr_scene_node *node, int *lx, int *ly); -// This function must be called whenever the coordinates/dimensions of a scene -// surface or scene output change. It is not necessary to call when a scene -// surface's node is enabled/disabled or obscured by other nodes. To quote the -// protocol: "The surface might be hidden even if no leave event has been sent." -static void scene_surface_update_outputs( - struct wlr_scene_surface *scene_surface, - int lx, int ly, struct wlr_scene *scene) { - struct wlr_box surface_box = { - .x = lx, - .y = ly, - .width = scene_surface->surface->current.width, - .height = scene_surface->surface->current.height, - }; - - int largest_overlap = 0; - scene_surface->primary_output = NULL; - - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - struct wlr_box output_box = { - .x = scene_output->x, - .y = scene_output->y, - }; - wlr_output_effective_resolution(scene_output->output, - &output_box.width, &output_box.height); - - struct wlr_box intersection; - if (wlr_box_intersection(&intersection, &surface_box, &output_box)) { - int overlap = intersection.width * intersection.height; - if (overlap > largest_overlap) { - largest_overlap = overlap; - scene_surface->primary_output = scene_output->output; - } - - // These enter/leave functions are a noop if the event has already been - // sent for the given output. - wlr_surface_send_enter(scene_surface->surface, scene_output->output); - } else { - wlr_surface_send_leave(scene_surface->surface, scene_output->output); - } - } -} - // This function must be called whenever the coordinates/dimensions of a scene // buffer or scene output change. It is not necessary to call when a scene // buffer's node is enabled/disabled or obscured by other nodes. @@ -258,11 +190,7 @@ static void scene_buffer_update_outputs( static void _scene_node_update_outputs( struct wlr_scene_node *node, int lx, int ly, struct wlr_scene *scene) { - if (node->type == WLR_SCENE_NODE_SURFACE) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_node(node); - scene_surface_update_outputs(scene_surface, lx, ly, scene); - } else if (node->type == WLR_SCENE_NODE_BUFFER) { + if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); scene_buffer_update_outputs(scene_buffer, lx, ly, scene); @@ -282,86 +210,6 @@ static void scene_node_update_outputs(struct wlr_scene_node *node) { _scene_node_update_outputs(node, lx, ly, scene); } -static void scene_surface_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct wlr_scene_surface *scene_surface = - wl_container_of(listener, scene_surface, surface_commit); - struct wlr_surface *surface = scene_surface->surface; - - struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); - - int lx, ly; - bool enabled = wlr_scene_node_coords(&scene_surface->node, &lx, &ly); - - if (surface->current.width != scene_surface->prev_width || - surface->current.height != scene_surface->prev_height) { - scene_surface_update_outputs(scene_surface, lx, ly, scene); - scene_surface->prev_width = surface->current.width; - scene_surface->prev_height = surface->current.height; - } - - if (!enabled) { - return; - } - - // Even if the surface hasn't submitted damage, schedule a new frame if - // the client has requested a wl_surface.frame callback. - if (!wl_list_empty(&surface->current.frame_callback_list) && - scene_surface->primary_output != NULL) { - wlr_output_schedule_frame(scene_surface->primary_output); - } - - if (!pixman_region32_not_empty(&surface->buffer_damage)) { - return; - } - - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - struct wlr_output *output = scene_output->output; - - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_surface_get_effective_damage(surface, &damage); - - pixman_region32_translate(&damage, - lx - scene_output->x, ly - scene_output->y); - - wlr_region_scale(&damage, &damage, output->scale); - if (ceil(output->scale) > surface->current.scale) { - // When scaling up a surface it'll become blurry, so we need to - // expand the damage region. - wlr_region_expand(&damage, &damage, - ceil(output->scale) - surface->current.scale); - } - wlr_output_damage_add(scene_output->damage, &damage); - pixman_region32_fini(&damage); - } -} - -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); - - scene_surface->surface_commit.notify = scene_surface_handle_surface_commit; - wl_signal_add(&surface->events.commit, &scene_surface->surface_commit); - - scene_node_damage_whole(&scene_surface->node); - - scene_node_update_outputs(&scene_surface->node); - - return scene_surface; -} - struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_node *parent, int width, int height, const float color[static 4]) { struct wlr_scene_rect *scene_rect = @@ -589,12 +437,6 @@ static void scene_node_get_size(struct wlr_scene_node *node, case WLR_SCENE_NODE_ROOT: case WLR_SCENE_NODE_TREE: return; - case WLR_SCENE_NODE_SURFACE:; - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_node(node); - *width = scene_surface->surface->current.width; - *height = scene_surface->surface->current.height; - break; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); *width = scene_rect->width; @@ -838,10 +680,6 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, case WLR_SCENE_NODE_ROOT: case WLR_SCENE_NODE_TREE: break; - case WLR_SCENE_NODE_SURFACE:; - struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_node(node); - intersects = wlr_surface_point_accepts_input(scene_surface->surface, lx, ly); - break; case WLR_SCENE_NODE_RECT:; int width, height; scene_node_get_size(node, &width, &height); @@ -948,9 +786,6 @@ static void render_texture(struct wlr_output *output, struct render_data { struct wlr_scene_output *scene_output; pixman_region32_t *damage; - - // May be NULL - struct wlr_presentation *presentation; }; static void render_node_iterator(struct wlr_scene_node *node, @@ -975,30 +810,6 @@ static void render_node_iterator(struct wlr_scene_node *node, case WLR_SCENE_NODE_TREE: /* Root or tree node has nothing to render itself */ break; - case WLR_SCENE_NODE_SURFACE:; - struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_node(node); - struct wlr_surface *surface = scene_surface->surface; - - texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - return; - } - - transform = wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, - output->transform_matrix); - - struct wlr_fbox src_box = {0}; - wlr_surface_get_buffer_source_box(surface, &src_box); - - render_texture(output, output_damage, texture, - &src_box, &dst_box, matrix); - - if (data->presentation != NULL && scene_surface->primary_output == output) { - wlr_presentation_surface_sampled_on_output(data->presentation, - surface, output); - } - break; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); @@ -1141,11 +952,7 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, static void scene_node_remove_output(struct wlr_scene_node *node, struct wlr_scene_output *output) { - if (node->type == WLR_SCENE_NODE_SURFACE) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_node(node); - wlr_surface_send_leave(scene_surface->surface, output->output); - } else if (node->type == WLR_SCENE_NODE_BUFFER) { + if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); uint64_t mask = 1ull << output->index; @@ -1246,15 +1053,6 @@ static bool scene_output_scanout(struct wlr_scene_output *scene_output) { struct wlr_scene_node *node = check_scanout_data.node; struct wlr_buffer *buffer; switch (node->type) { - case WLR_SCENE_NODE_SURFACE:; - struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_node(node); - if (scene_surface->surface->buffer == NULL || - scene_surface->surface->current.viewport.has_src || - scene_surface->surface->current.transform != output->transform) { - return false; - } - buffer = &scene_surface->surface->buffer->base; - break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (scene_buffer->buffer == NULL || @@ -1274,18 +1072,6 @@ static bool scene_output_scanout(struct wlr_scene_output *scene_output) { return false; } - struct wlr_presentation *presentation = scene_output->scene->presentation; - if (presentation != NULL && node->type == WLR_SCENE_NODE_SURFACE) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_node(node); - // Since outputs may overlap, we still need to check this even though - // we know that the surface size matches the size of this output. - if (scene_surface->primary_output == output) { - wlr_presentation_surface_sampled_on_output(presentation, - scene_surface->surface, output); - } - } - if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); @@ -1340,7 +1126,6 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { struct render_data data = { .scene_output = scene_output, .damage = &damage, - .presentation = scene_output->scene->presentation, }; scene_node_for_each_node(&scene_output->scene->node, -scene_output->x, -scene_output->y, @@ -1373,14 +1158,6 @@ static void scene_node_send_frame_done(struct wlr_scene_node *node, return; } - if (node->type == WLR_SCENE_NODE_SURFACE) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_node(node); - if (scene_surface->primary_output == scene_output->output) { - wlr_surface_send_frame_done(scene_surface->surface, now); - } - } - if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);