diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index bca0b269..1238653d 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -76,6 +76,8 @@ struct wlr_scene_surface { // private state + int prev_width, prev_height; + struct wl_listener surface_destroy; struct wl_listener surface_commit; }; @@ -201,6 +203,10 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_node *parent); * Add a node displaying a single surface to the scene-graph. * * The child sub-surfaces are ignored. + * + * wlr_surface_send_enter()/wlr_surface_send_leave() will be called + * automatically based on the position of the surface and outputs in + * the scene. */ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent, struct wlr_surface *surface); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b1db7b8f..d2f3f4a7 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -39,6 +39,13 @@ static struct wlr_scene_buffer *scene_buffer_from_node( 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); @@ -85,11 +92,11 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { 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 *scene = scene_root_from_node(node); - - struct wlr_scene_output *scene_output, *scene_output_tmp; + 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); } @@ -102,8 +109,16 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { 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:; @@ -146,11 +161,60 @@ static void scene_surface_handle_surface_destroy(struct wl_listener *listener, wlr_scene_node_destroy(&scene_surface->node); } -static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { - while (node->parent != NULL) { - node = node->parent; +// 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, + }; + + 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); + + // These enter/leave functions are a noop if the event has already been + // sent for the given output. + struct wlr_box intersection; + if (wlr_box_intersection(&intersection, &surface_box, &output_box)) { + 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); } - return scene_root_from_node(node); +} + +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, @@ -163,12 +227,21 @@ static void scene_surface_handle_surface_commit(struct wl_listener *listener, return; } + struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); + int lx, ly; - if (!wlr_scene_node_coords(&scene_surface->node, &lx, &ly)) { - return; + 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; } - struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); + if (!enabled) { + return; + } struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { @@ -212,6 +285,8 @@ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent scene_node_damage_whole(&scene_surface->node); + scene_node_update_surface_outputs(&scene_surface->node); + return scene_surface; } @@ -438,6 +513,8 @@ void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { 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, @@ -511,6 +588,8 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node, 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, @@ -822,9 +901,19 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, 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); } @@ -849,6 +938,8 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, 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 {