#include #include #include #include #include #include #include #include #include #include #include #include #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_tree *scene_tree_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_TREE); return (struct wlr_scene_tree *)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); return (struct wlr_scene_rect *)node; } static struct wlr_scene_buffer *scene_buffer_from_node( struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_BUFFER); return (struct wlr_scene_buffer *)node; } static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { while (node->parent != NULL) { node = node->parent; } return scene_root_from_node(node); } static void scene_node_state_init(struct wlr_scene_node_state *state) { wl_list_init(&state->children); wl_list_init(&state->link); state->enabled = true; } 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); 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); } static void scene_node_damage_whole(struct wlr_scene_node *node); void wlr_scene_node_destroy(struct wlr_scene_node *node) { if (node == NULL) { return; } scene_node_damage_whole(node); scene_node_finish(node); struct wlr_scene *scene = scene_node_get_root(node); struct wlr_scene_output *scene_output; switch (node->type) { case WLR_SCENE_NODE_ROOT:; struct wlr_scene_output *scene_output_tmp; wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { wlr_scene_output_destroy(scene_output); } wl_list_remove(&scene->presentation_destroy.link); free(scene); break; case WLR_SCENE_NODE_TREE:; struct wlr_scene_tree *tree = scene_tree_from_node(node); free(tree); 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); free(scene_surface); break; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); free(scene_rect); break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = scene_buffer_from_node(node); wl_list_remove(&scene_buffer->pending_link); wlr_texture_destroy(scene_buffer->texture); wlr_buffer_unlock(scene_buffer->buffer); free(scene_buffer); 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); wl_list_init(&scene->outputs); wl_list_init(&scene->presentation_destroy.link); wl_list_init(&scene->pending_buffers); return scene; } struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_node *parent) { struct wlr_scene_tree *tree = calloc(1, sizeof(struct wlr_scene_tree)); if (tree == NULL) { return NULL; } scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, 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); } // 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); } } } static void scene_node_update_surface_outputs_iterator( 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); } struct wlr_scene_node *child; wl_list_for_each(child, &node->state.children, state.link) { scene_node_update_surface_outputs_iterator(child, lx + child->state.x, ly + child->state.y, scene); } } static void scene_node_update_surface_outputs(struct wlr_scene_node *node) { struct wlr_scene *scene = scene_node_get_root(node); int lx, ly; wlr_scene_node_coords(node, &lx, &ly); scene_node_update_surface_outputs_iterator(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_surface_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 = calloc(1, sizeof(struct wlr_scene_rect)); if (scene_rect == NULL) { return NULL; } scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent); scene_rect->width = width; scene_rect->height = height; memcpy(scene_rect->color, color, sizeof(scene_rect->color)); scene_node_damage_whole(&scene_rect->node); return scene_rect; } void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) { if (rect->width == width && rect->height == height) { return; } scene_node_damage_whole(&rect->node); rect->width = width; rect->height = height; scene_node_damage_whole(&rect->node); } void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) { if (memcmp(rect->color, color, sizeof(rect->color)) == 0) { return; } memcpy(rect->color, color, sizeof(rect->color)); scene_node_damage_whole(&rect->node); } struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_node *parent, struct wlr_buffer *buffer) { struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); if (scene_buffer == NULL) { return NULL; } scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); scene_buffer->buffer = wlr_buffer_lock(buffer); scene_node_damage_whole(&scene_buffer->node); struct wlr_scene *scene = scene_node_get_root(parent); wl_list_insert(&scene->pending_buffers, &scene_buffer->pending_link); return scene_buffer; } void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, const struct wlr_fbox *box) { struct wlr_fbox *cur = &scene_buffer->src_box; if ((wlr_fbox_empty(box) && wlr_fbox_empty(cur)) || (box != NULL && memcmp(cur, box, sizeof(*box)) == 0)) { return; } if (box != NULL) { memcpy(cur, box, sizeof(*box)); } else { memset(cur, 0, sizeof(*cur)); } scene_node_damage_whole(&scene_buffer->node); } void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, int width, int height) { if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) { return; } scene_node_damage_whole(&scene_buffer->node); scene_buffer->dst_width = width; scene_buffer->dst_height = height; scene_node_damage_whole(&scene_buffer->node); } void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, enum wl_output_transform transform) { if (scene_buffer->transform == transform) { return; } scene_node_damage_whole(&scene_buffer->node); scene_buffer->transform = transform; scene_node_damage_whole(&scene_buffer->node); } static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(scene_buffer->buffer); if (client_buffer != NULL) { return client_buffer->texture; } if (scene_buffer->texture != NULL) { return scene_buffer->texture; } scene_buffer->texture = wlr_texture_from_buffer(renderer, scene_buffer->buffer); return scene_buffer->texture; } static void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) { *width = 0; *height = 0; switch (node->type) { 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; *height = scene_rect->height; break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = scene_buffer_from_node(node); if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { *width = scene_buffer->dst_width; *height = scene_buffer->dst_height; } else { if (scene_buffer->transform & WL_OUTPUT_TRANSFORM_90) { *height = scene_buffer->buffer->width; *width = scene_buffer->buffer->height; } else { *width = scene_buffer->buffer->width; *height = scene_buffer->buffer->height; } } break; } } 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 _scene_node_damage_whole(struct wlr_scene_node *node, struct wlr_scene *scene, int lx, int ly) { if (!node->state.enabled) { return; } struct wlr_scene_node *child; wl_list_for_each(child, &node->state.children, state.link) { _scene_node_damage_whole(child, scene, lx + child->state.x, ly + child->state.y); } int width, height; scene_node_get_size(node, &width, &height); struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { struct wlr_box box = { .x = lx - scene_output->x, .y = ly - scene_output->y, .width = width, .height = height, }; scale_box(&box, scene_output->output->scale); wlr_output_damage_add_box(scene_output->damage, &box); } } static void scene_node_damage_whole(struct wlr_scene_node *node) { struct wlr_scene *scene = scene_node_get_root(node); if (wl_list_empty(&scene->outputs)) { return; } int lx, ly; if (!wlr_scene_node_coords(node, &lx, &ly)) { return; } _scene_node_damage_whole(node, scene, lx, ly); } void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { if (node->state.enabled == enabled) { return; } // One of these damage_whole() calls will short-circuit and be a no-op scene_node_damage_whole(node); node->state.enabled = enabled; scene_node_damage_whole(node); } void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { if (node->state.x == x && node->state.y == y) { return; } scene_node_damage_whole(node); node->state.x = x; node->state.y = y; scene_node_damage_whole(node); scene_node_update_surface_outputs(node); } void wlr_scene_node_place_above(struct wlr_scene_node *node, struct wlr_scene_node *sibling) { assert(node != sibling); assert(node->parent == sibling->parent); if (node->state.link.prev == &sibling->state.link) { return; } wl_list_remove(&node->state.link); wl_list_insert(&sibling->state.link, &node->state.link); scene_node_damage_whole(node); scene_node_damage_whole(sibling); } void wlr_scene_node_place_below(struct wlr_scene_node *node, struct wlr_scene_node *sibling) { assert(node != sibling); assert(node->parent == sibling->parent); if (node->state.link.next == &sibling->state.link) { return; } wl_list_remove(&node->state.link); wl_list_insert(sibling->state.link.prev, &node->state.link); scene_node_damage_whole(node); scene_node_damage_whole(sibling); } void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { struct wlr_scene_node *current_top = wl_container_of( node->parent->state.children.prev, current_top, state.link); if (node == current_top) { return; } wlr_scene_node_place_above(node, current_top); } void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { struct wlr_scene_node *current_bottom = wl_container_of( node->parent->state.children.next, current_bottom, state.link); if (node == current_bottom) { return; } wlr_scene_node_place_below(node, current_bottom); } void wlr_scene_node_reparent(struct wlr_scene_node *node, struct wlr_scene_node *new_parent) { assert(node->type != WLR_SCENE_NODE_ROOT && new_parent != NULL); if (node->parent == new_parent) { return; } /* Ensure that a node cannot become its own ancestor */ for (struct wlr_scene_node *ancestor = new_parent; ancestor != NULL; ancestor = ancestor->parent) { assert(ancestor != node); } scene_node_damage_whole(node); wl_list_remove(&node->state.link); node->parent = new_parent; wl_list_insert(new_parent->state.children.prev, &node->state.link); scene_node_damage_whole(node); scene_node_update_surface_outputs(node); } bool wlr_scene_node_coords(struct wlr_scene_node *node, int *lx_ptr, int *ly_ptr) { int lx = 0, ly = 0; bool enabled = true; while (node != NULL) { lx += node->state.x; ly += node->state.y; enabled = enabled && node->state.enabled; node = node->parent; } *lx_ptr = lx; *ly_ptr = ly; return enabled; } 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) { if (!node->state.enabled) { return; } lx += node->state.x; ly += node->state.y; if (node->type == WLR_SCENE_NODE_SURFACE) { struct wlr_scene_surface *scene_surface = wlr_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); } struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, double lx, double ly, double *nx, double *ny) { if (!node->state.enabled) { return NULL; } // TODO: optimize by storing a bounding box in each node? lx -= node->state.x; ly -= node->state.y; struct wlr_scene_node *child; wl_list_for_each_reverse(child, &node->state.children, state.link) { struct wlr_scene_node *node = wlr_scene_node_at(child, lx, ly, nx, ny); if (node != NULL) { return node; } } bool intersects = false; switch (node->type) { 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: case WLR_SCENE_NODE_BUFFER:; int width, height; scene_node_get_size(node, &width, &height); intersects = lx >= 0 && lx < width && ly >= 0 && ly < height; break; } if (intersects) { if (nx != NULL) { *nx = lx; } if (ny != NULL) { *ny = ly; } return node; } return NULL; } static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { struct wlr_renderer *renderer = output->renderer; 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_rect(struct wlr_output *output, pixman_region32_t *output_damage, const float color[static 4], const struct wlr_box *box, const float matrix[static 9]) { struct wlr_renderer *renderer = output->renderer; 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); 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_rect(renderer, box, color, matrix); } pixman_region32_fini(&damage); } static void render_texture(struct wlr_output *output, pixman_region32_t *output_damage, struct wlr_texture *texture, const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9]) { struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_fbox default_src_box = {0}; if (wlr_fbox_empty(src_box)) { default_src_box.width = dst_box->width; default_src_box.height = dst_box->height; src_box = &default_src_box; } pixman_region32_t damage; pixman_region32_init(&damage); pixman_region32_init_rect(&damage, dst_box->x, dst_box->y, dst_box->width, dst_box->height); pixman_region32_intersect(&damage, &damage, output_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_subtexture_with_matrix(renderer, texture, src_box, matrix, 1.0); } pixman_region32_fini(&damage); } struct render_data { struct wlr_output *output; pixman_region32_t *damage; // May be NULL struct wlr_presentation *presentation; }; static void render_node_iterator(struct wlr_scene_node *node, 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_box dst_box = { .x = x, .y = y, }; scene_node_get_size(node, &dst_box.width, &dst_box.height); scale_box(&dst_box, output->scale); struct wlr_texture *texture; float matrix[9]; enum wl_output_transform transform; switch (node->type) { case WLR_SCENE_NODE_ROOT: 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); render_rect(output, output_damage, scene_rect->color, &dst_box, output->transform_matrix); break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = scene_buffer_from_node(node); struct wlr_renderer *renderer = output->renderer; texture = scene_buffer_get_texture(scene_buffer, renderer); if (texture == NULL) { return; } transform = wlr_output_transform_invert(scene_buffer->transform); wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, output->transform_matrix); render_texture(output, output_damage, texture, &scene_buffer->src_box, &dst_box, matrix); break; } } static void scene_node_for_each_node(struct wlr_scene_node *node, int lx, int ly, wlr_scene_node_iterator_func_t user_iterator, void *user_data) { if (!node->state.enabled) { return; } lx += node->state.x; ly += node->state.y; user_iterator(node, lx, ly, user_data); struct wlr_scene_node *child; wl_list_for_each(child, &node->state.children, state.link) { scene_node_for_each_node(child, lx, ly, user_iterator, user_data); } } 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 = output->renderer; assert(renderer); if (output->enabled && pixman_region32_not_empty(damage)) { struct render_data data = { .output = output, .damage = damage, .presentation = scene->presentation, }; scene_node_for_each_node(&scene->node, -lx, -ly, render_node_iterator, &data); wlr_renderer_scissor(renderer, NULL); } pixman_region32_fini(&full_region); } static void scene_handle_presentation_destroy(struct wl_listener *listener, void *data) { struct wlr_scene *scene = wl_container_of(listener, scene, presentation_destroy); wl_list_remove(&scene->presentation_destroy.link); wl_list_init(&scene->presentation_destroy.link); scene->presentation = NULL; } void wlr_scene_set_presentation(struct wlr_scene *scene, struct wlr_presentation *presentation) { assert(scene->presentation == NULL); scene->presentation = presentation; scene->presentation_destroy.notify = scene_handle_presentation_destroy; wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); } static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); wlr_scene_output_destroy(scene_output); } static const struct wlr_addon_interface output_addon_impl = { .name = "wlr_scene_output", .destroy = scene_output_handle_destroy, }; struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, struct wlr_output *output) { struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output)); if (scene_output == NULL) { return NULL; } scene_output->damage = wlr_output_damage_create(output); if (scene_output->damage == NULL) { free(scene_output); return NULL; } scene_output->output = output; scene_output->scene = scene; wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl); wl_list_insert(&scene->outputs, &scene_output->link); wlr_output_damage_add_whole(scene_output->damage); return scene_output; } static void scene_output_send_leave_iterator(struct wlr_surface *surface, int sx, int sy, void *data) { struct wlr_output *output = data; wlr_surface_send_leave(surface, output); } void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wlr_addon_finish(&scene_output->addon); wl_list_remove(&scene_output->link); wlr_scene_output_for_each_surface(scene_output, scene_output_send_leave_iterator, scene_output->output); free(scene_output); } struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, struct wlr_output *output) { struct wlr_addon *addon = wlr_addon_find(&output->addons, scene, &output_addon_impl); if (addon == NULL) { return NULL; } struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); return scene_output; } void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly) { if (scene_output->x == lx && scene_output->y == ly) { return; } scene_output->x = lx; scene_output->y = ly; wlr_output_damage_add_whole(scene_output->damage); scene_node_update_surface_outputs(&scene_output->scene->node); } struct check_scanout_data { // in struct wlr_box viewport_box; // out struct wlr_scene_node *node; size_t n; }; static void check_scanout_iterator(struct wlr_scene_node *node, int x, int y, void *_data) { struct check_scanout_data *data = _data; struct wlr_box node_box = { .x = x, .y = y }; scene_node_get_size(node, &node_box.width, &node_box.height); struct wlr_box intersection; if (!wlr_box_intersection(&intersection, &data->viewport_box, &node_box)) { return; } data->n++; if (data->viewport_box.x == node_box.x && data->viewport_box.y == node_box.y && data->viewport_box.width == node_box.width && data->viewport_box.height == node_box.height) { data->node = node; } } static bool scene_output_scanout(struct wlr_scene_output *scene_output) { struct wlr_output *output = scene_output->output; struct wlr_box viewport_box = { .x = scene_output->x, .y = scene_output->y }; wlr_output_effective_resolution(output, &viewport_box.width, &viewport_box.height); struct check_scanout_data check_scanout_data = { .viewport_box = viewport_box, }; scene_node_for_each_node(&scene_output->scene->node, 0, 0, check_scanout_iterator, &check_scanout_data); if (check_scanout_data.n != 1 || check_scanout_data.node == NULL) { return false; } 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 = scene_buffer_from_node(node); if (scene_buffer->buffer == NULL || !wlr_fbox_empty(&scene_buffer->src_box) || scene_buffer->transform != output->transform) { return false; } buffer = scene_buffer->buffer; break; default: return false; } wlr_output_attach_buffer(output, buffer); if (!wlr_output_test(output)) { wlr_output_rollback(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); } } return wlr_output_commit(output); } bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { struct wlr_output *output = scene_output->output; struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); bool scanout = scene_output_scanout(scene_output); if (scanout != scene_output->prev_scanout) { wlr_log(WLR_DEBUG, "Direct scan-out %s", scanout ? "enabled" : "disabled"); // When exiting direct scan-out, damage everything wlr_output_damage_add_whole(scene_output->damage); } scene_output->prev_scanout = scanout; if (scanout) { return true; } bool needs_frame; pixman_region32_t damage; pixman_region32_init(&damage); if (!wlr_output_damage_attach_render(scene_output->damage, &needs_frame, &damage)) { pixman_region32_fini(&damage); return false; } if (!needs_frame) { pixman_region32_fini(&damage); wlr_output_rollback(output); return true; } // Try to import new buffers as textures struct wlr_scene_buffer *scene_buffer, *scene_buffer_tmp; wl_list_for_each_safe(scene_buffer, scene_buffer_tmp, &scene_output->scene->pending_buffers, pending_link) { scene_buffer_get_texture(scene_buffer, renderer); wl_list_remove(&scene_buffer->pending_link); wl_list_init(&scene_buffer->pending_link); } wlr_renderer_begin(renderer, output->width, output->height); int nrects; pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); for (int i = 0; i < nrects; ++i) { scissor_output(output, &rects[i]); wlr_renderer_clear(renderer, (float[4]){ 0.0, 0.0, 0.0, 1.0 }); } wlr_scene_render_output(scene_output->scene, output, scene_output->x, scene_output->y, &damage); wlr_output_render_software_cursors(output, &damage); wlr_renderer_end(renderer); pixman_region32_fini(&damage); int tr_width, tr_height; wlr_output_transformed_resolution(output, &tr_width, &tr_height); enum wl_output_transform transform = wlr_output_transform_invert(output->transform); pixman_region32_t frame_damage; pixman_region32_init(&frame_damage); wlr_region_transform(&frame_damage, &scene_output->damage->current, transform, tr_width, tr_height); wlr_output_set_damage(output, &frame_damage); pixman_region32_fini(&frame_damage); return wlr_output_commit(output); } static void scene_output_send_frame_done_iterator(struct wlr_scene_node *node, struct wlr_output *output, struct timespec *now) { if (!node->state.enabled) { 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 == output) { wlr_surface_send_frame_done(scene_surface->surface, now); } } struct wlr_scene_node *child; wl_list_for_each(child, &node->state.children, state.link) { scene_output_send_frame_done_iterator(child, output, now); } } void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, struct timespec *now) { scene_output_send_frame_done_iterator(&scene_output->scene->node, scene_output->output, now); } static void scene_output_for_each_surface(const struct wlr_box *output_box, struct wlr_scene_node *node, int lx, int ly, wlr_surface_iterator_func_t user_iterator, void *user_data) { if (!node->state.enabled) { return; } lx += node->state.x; ly += node->state.y; if (node->type == WLR_SCENE_NODE_SURFACE) { struct wlr_box node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); struct wlr_box intersection; if (wlr_box_intersection(&intersection, output_box, &node_box)) { struct wlr_scene_surface *scene_surface = wlr_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_output_for_each_surface(output_box, child, lx, ly, user_iterator, user_data); } } void wlr_scene_output_for_each_surface(struct wlr_scene_output *scene_output, wlr_surface_iterator_func_t iterator, void *user_data) { struct wlr_box box = { .x = scene_output->x, .y = scene_output->y }; wlr_output_effective_resolution(scene_output->output, &box.width, &box.height); scene_output_for_each_surface(&box, &scene_output->scene->node, 0, 0, iterator, user_data); }