scene/surface: fix enter/leave events with multiple scenes

Currently if there are multiple scenes present with distinct outputs the
logic intended to avoid redundant enter/leave events does not work as
intended. A surface not being visible on any output in one scene does
not mean it is not visible on any output in any scene.

Implementing the desired policy to avoid redundant enter/leave events
requires sharing a bit of state between all scenes, which is now handled
by the new wlr_surface_send_leave_lazy() function.
This commit is contained in:
Isaac Freund 2026-03-27 10:42:29 +01:00
parent 8e44489bf7
commit fc6410448e
No known key found for this signature in database
GPG key ID: 86DED400DDFD7A11

View file

@ -97,41 +97,21 @@ static void handle_scene_buffer_outputs_update(
struct wlr_scene_outputs_update_event *event = data; struct wlr_scene_outputs_update_event *event = data;
struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node);
struct wlr_scene_output *scene_output;
wl_list_for_each(scene_output, &scene->outputs, link) {
wlr_surface_queue_lazy_leave(surface->surface, scene_output->output);
}
for (size_t i = 0; i < event->size; i++) {
wlr_surface_send_enter(surface->surface, event->active[i]->output);
}
wlr_surface_process_lazy_leaves(surface->surface);
// If the surface is no longer visible on any output, keep the last sent // If the surface is no longer visible on any output, keep the last sent
// preferred configuration to avoid unnecessary redraws // preferred configuration to avoid unnecessary redraws
if (event->size == 0) { if (event->size == 0) {
return; return;
} }
// To avoid sending redundant leave/enter events when a surface is hidden and then shown
// without moving to a different output the following policy is implemented:
//
// 1. When a surface transitions from being visible on >0 outputs to being visible on 0 outputs
// don't send any leave events.
//
// 2. When a surface transitions from being visible on 0 outputs to being visible on >0 outputs
// send leave events for all entered outputs on which the surface is no longer visible as
// well as enter events for any outputs not already entered.
struct wlr_surface_output *entered_output, *tmp;
wl_list_for_each_safe(entered_output, tmp, &surface->surface->current_outputs, link) {
bool active = false;
for (size_t i = 0; i < event->size; i++) {
if (entered_output->output == event->active[i]->output) {
active = true;
break;
}
}
if (!active) {
wlr_surface_send_leave(surface->surface, entered_output->output);
}
}
for (size_t i = 0; i < event->size; i++) {
// This function internally checks if an enter event was already sent for the output
// to avoid sending redundant events.
wlr_surface_send_enter(surface->surface, event->active[i]->output);
}
double scale = get_surface_preferred_buffer_scale(surface->surface); double scale = get_surface_preferred_buffer_scale(surface->surface);
wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_fractional_scale_v1_notify_scale(surface->surface, scale);
wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale));