From f73b8e1ea8f060af22788f100d6b9d5368b8dd57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Mon, 1 Dec 2025 17:23:46 -0500 Subject: [PATCH 1/4] util: add helper to get time in nanoseconds. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sergio Gómez --- include/util/time.h | 5 +++++ util/time.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/include/util/time.h b/include/util/time.h index dd164bf2f..fd2bf9626 100644 --- a/include/util/time.h +++ b/include/util/time.h @@ -6,6 +6,11 @@ static const long NSEC_PER_SEC = 1000000000; +/** + * Get the current time, in nanoseconds. + */ +int64_t get_current_time_nsec(void); + /** * Get the current time, in milliseconds. */ diff --git a/util/time.c b/util/time.c index 1a8f32969..7db0893b4 100644 --- a/util/time.c +++ b/util/time.c @@ -22,6 +22,12 @@ int64_t get_current_time_msec(void) { return timespec_to_msec(&now); } +int64_t get_current_time_nsec(void) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return timespec_to_nsec(&now); +} + void timespec_sub(struct timespec *r, const struct timespec *a, const struct timespec *b) { r->tv_sec = a->tv_sec - b->tv_sec; From c78bce9cc20105680fef44e1fee7c18e243eec85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Wed, 26 Nov 2025 10:55:35 -0500 Subject: [PATCH 2/4] fifo-v1: new protocol implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement fifo-v1 protocol: the capability for clients to explicitly queue wl_surface commits. The design of the wl_surface interface defines a double buffer, which is intended to support only (from the point of view of the client) what is sometimes called the 'mailbox' presentation mode: each request on a wl_surface operates on an accumulated pending state until a .commit request is issued, immediately and atomically replacing the wl_surface's current state. This means that only the state related to the latest .commit request will be latched onto. fifo-v1 is a protocol that aims to support an important use case, namely the FIFO display presentation mode, which allows to queue the accumulated pending state until the next latching deadline occurs. Signed-off-by: Sergio Gómez --- include/wlr/types/wlr_fifo_v1.h | 107 ++++++++++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_fifo_v1.c | 364 ++++++++++++++++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 include/wlr/types/wlr_fifo_v1.h create mode 100644 types/wlr_fifo_v1.c diff --git a/include/wlr/types/wlr_fifo_v1.h b/include/wlr/types/wlr_fifo_v1.h new file mode 100644 index 000000000..4ea9c18a5 --- /dev/null +++ b/include/wlr/types/wlr_fifo_v1.h @@ -0,0 +1,107 @@ +/* + * 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_FIFO_V1_H +#define WLR_TYPES_WLR_FIFO_V1_H + +#include + +#include +#include + +struct wlr_fifo_manager_v1_new_fifo_event { + struct wlr_fifo_v1 *fifo; +}; + +struct wlr_fifo_manager_v1 { + struct wl_global *global; + struct wl_display *display; + + struct { + struct wl_signal new_fifo; // struct wlr_fifo_manager_v1_new_fifo_event + + /** + * Signals that the fifo manager is being destroyed. + */ + struct wl_signal destroy; + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_fifo_v1_state { + /* + * This field is used to set the fifo barrier on the surface. + * Set when the client makes a .set_barrier request. */ + bool set_barrier; + /* + * This field is used to lock a commit until the fifo barrier on the surface is cleared. + * Set when the client makes a .wait_barrier request. */ + bool wait_barrier; +}; + +struct wlr_fifo_v1 { + struct wlr_fifo_manager_v1 *manager; + + struct wl_resource *resource; + struct wlr_addon addon; + + struct wlr_surface *surface; + struct wlr_output *output; + + // list of commit requests waiting on the fifo barrier + struct wl_list commits; // fifo_commit.link + + // per-commit state used with the wlr_surface_synced mechanism + struct wlr_fifo_v1_state current, pending; + + // per-wlr_fifo_v1 instance state + bool barrier_set; + uint64_t last_output_present_nsec; + bool surface_occluded_source_armed; + + struct { + /** + * Signals that the fifo object is being destroyed. + */ + struct wl_signal destroy; + } events; + + struct { + struct wl_listener surface_client_commit; + struct wl_listener surface_commit; + struct wl_listener output_present; + struct wl_listener output_destroy; + struct wl_listener fifo_manager_destroy; + + // used to advance the queue when the surface is occluded + struct wl_event_source *surface_occluded_source; + + struct wlr_surface_synced synced; + } WLR_PRIVATE; + + struct wl_list link; // wlr_scene.fifo_surfaces +}; + +/** + * Create the wp_fifo_manager_v1_interface global, which can be used by clients to + * queue commits on a wl_surface for presentation. + */ +struct wlr_fifo_manager_v1 *wlr_fifo_manager_v1_create(struct wl_display *display, + uint32_t version); + +/** + * Used to set the output to which the fifo will be applied. + * If output is NULL, the fifo will be unset for a previously set output. + * Returns true on success, false on failure. + */ +void wlr_fifo_v1_set_output(struct wlr_fifo_v1 *fifo, struct wlr_output *output); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 613d18018..f9bba7ff6 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -36,6 +36,7 @@ protocols = { 'ext-image-copy-capture-v1': wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', 'ext-data-control-v1': wl_protocol_dir / 'staging/ext-data-control/ext-data-control-v1.xml', + 'fifo-v1': wl_protocol_dir / 'staging/fifo/fifo-v1.xml', 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', 'security-context-v1': wl_protocol_dir / 'staging/security-context/security-context-v1.xml', diff --git a/types/meson.build b/types/meson.build index 402fd3e11..e9edd24b7 100644 --- a/types/meson.build +++ b/types/meson.build @@ -76,6 +76,7 @@ wlr_files += files( 'wlr_presentation_time.c', 'wlr_primary_selection_v1.c', 'wlr_primary_selection.c', + 'wlr_fifo_v1.c', 'wlr_region.c', 'wlr_relative_pointer_v1.c', 'wlr_screencopy_v1.c', diff --git a/types/wlr_fifo_v1.c b/types/wlr_fifo_v1.c new file mode 100644 index 000000000..9ec112157 --- /dev/null +++ b/types/wlr_fifo_v1.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include +#include +#include +#include "fifo-v1-protocol.h" +#include "util/time.h" + +#define FIFO_MANAGER_VERSION 1 + +struct fifo_commit { + struct wlr_fifo_v1 *fifo; + struct wl_list link; // wlr_fifo_v1.fifo_commits + bool barrier_pending; + uint32_t seq; +}; + +static void surface_synced_move_state(void *_dst, void *_src) { + struct wlr_fifo_v1_state *dst = _dst, *src = _src; + dst->set_barrier = src->set_barrier; + dst->wait_barrier = src->wait_barrier; + src->wait_barrier = false; + src->set_barrier = false; +} + +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_fifo_v1_state), + .move_state = surface_synced_move_state, +}; + +static bool is_surface_buffer_valid(const struct wlr_surface *const surface) { + if (!surface->buffer || (surface->pending.committed & WLR_SURFACE_STATE_BUFFER && + surface->pending.buffer == NULL)) { + return false; + } + + return true; +} + +static void commit_destroy(struct fifo_commit *commit) { + wl_list_remove(&commit->link); + wlr_surface_unlock_cached(commit->fifo->surface, commit->seq); + free(commit); +} + +static void fifo_signal_barrier(struct wlr_fifo_v1 *fifo) { + // dequeue and unlock commits until we find one with a .set_barrier request, + // in which case leave the barrier condition set. + struct fifo_commit *commit, *tmp; + bool barrier_pending = false; + wl_list_for_each_safe(commit, tmp, &fifo->commits, link) { + barrier_pending = commit->barrier_pending; + commit_destroy(commit); + if (barrier_pending) { + break; + } + } + + if (!barrier_pending) { + fifo->barrier_set = false; + } +} + +static void fifo_reset(struct wlr_fifo_v1 *fifo) { + struct fifo_commit *commit, *tmp_co; + wl_list_for_each_safe(commit, tmp_co, &fifo->commits, link) { + commit_destroy(commit); + } + if (fifo->output) { + fifo->output_present.notify = NULL; + wl_list_remove(&fifo->output_present.link); + fifo->output_destroy.notify = NULL; + wl_list_remove(&fifo->output_destroy.link); + } + fifo->output = NULL; + fifo->pending = (struct wlr_fifo_v1_state){0}; + fifo->current = (struct wlr_fifo_v1_state){0}; + fifo->barrier_set = false; + fifo->surface_occluded_source_armed = false; + wl_event_source_timer_update(fifo->surface_occluded_source, 0); + fifo->last_output_present_nsec = 0; +} + +static void fifo_handle_output_destroy(struct wl_listener *listener, void *data) { + struct wlr_fifo_v1 *fifo = + wl_container_of(listener, fifo, output_destroy); + fifo_reset(fifo); +} + +static int handle_timer(void *data) { + struct wlr_fifo_v1 *fifo = data; + if (fifo->barrier_set) { + fifo_signal_barrier(fifo); + } + wl_event_source_timer_update(fifo->surface_occluded_source, 25); + return 0; +} + +static void fifo_handle_output_present(struct wl_listener *listener, void *data) { + struct wlr_fifo_v1 *fifo = + wl_container_of(listener, fifo, output_present); + struct wlr_output_event_present *event = data; + + if (!fifo->surface->buffer || fifo->surface_occluded_source_armed) { + return; + } + + // We use the output.present event to advance the queue. + if (fifo->barrier_set) { + fifo_signal_barrier(fifo); + } + fifo->last_output_present_nsec = timespec_to_nsec(&event->when); +} + +static void fifo_handle_commit(struct wl_listener *listener, void *data) { + struct wlr_fifo_v1 *fifo = + wl_container_of(listener, fifo, surface_commit); + if (fifo->current.set_barrier) { + fifo->barrier_set = true; + } +} + +static bool should_queue_commit(struct wlr_fifo_v1 *fifo) { + return fifo->pending.wait_barrier && fifo->barrier_set; +} + +static void fifo_handle_client_commit(struct wl_listener *listener, void *data) { + struct wlr_fifo_v1 *fifo = + wl_container_of(listener, fifo, surface_client_commit); + + if (!fifo->surface || !is_surface_buffer_valid(fifo->surface) || + !should_queue_commit(fifo)) { + return; + } + + struct fifo_commit *commit = calloc(1, sizeof(*commit)); + if (!commit) { + wl_client_post_no_memory(wl_resource_get_client(fifo->resource)); + return; + } + commit->fifo = fifo; + + // If the commit, in addition to a .wait_barrier request, has a .set_barrier one, + // mark it so that we can set again the barrier when dequeing the commit. + if (fifo->pending.set_barrier) { + commit->barrier_pending = true; + } + commit->seq = wlr_surface_lock_pending(fifo->surface); + wl_list_insert(fifo->commits.prev, &commit->link); +} + +static const struct wp_fifo_v1_interface fifo_implementation; +static struct wlr_fifo_v1 *fifo_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_fifo_v1_interface, + &fifo_implementation)); + return wl_resource_get_user_data(resource); +} + +static void fifo_handle_wait_barrier(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_fifo_v1 *fifo = + fifo_v1_from_resource(resource); + if (!fifo->surface) { + wl_resource_post_error(resource, + WP_FIFO_V1_ERROR_SURFACE_DESTROYED, + "the associated surface no longer exists"); + return; + } + fifo->pending.wait_barrier = true; +} + +static void fifo_handle_set_barrier(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_fifo_v1 *fifo = + fifo_v1_from_resource(resource); + if (!fifo->surface) { + wl_resource_post_error(resource, + WP_FIFO_V1_ERROR_SURFACE_DESTROYED, + "the associated surface no longer exists"); + return; + } + fifo->pending.set_barrier = true; +} + +static void fifo_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_fifo_v1 *fifo = fifo_v1_from_resource(resource); + wlr_surface_synced_finish(&fifo->synced); + wl_signal_emit_mutable(&fifo->events.destroy, NULL); + fifo_reset(fifo); + wl_event_source_remove(fifo->surface_occluded_source); + wlr_addon_finish(&fifo->addon); + wl_list_remove(&fifo->surface_client_commit.link); + wl_list_remove(&fifo->surface_commit.link); + free(fifo); +} + +static void fifo_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void surface_fifo_addon_handle_destroy(struct wlr_addon *addon) { + struct wlr_fifo_v1 *fifo = wl_container_of(addon, fifo, addon); + wl_resource_destroy(fifo->resource); +} + +static const struct wlr_addon_interface surface_fifo_addon_impl = { + .name = "wp_fifo_v1", + .destroy = surface_fifo_addon_handle_destroy, +}; + +static const struct wp_fifo_v1_interface fifo_implementation = { + .destroy = fifo_handle_destroy, + .set_barrier = fifo_handle_set_barrier, + .wait_barrier = fifo_handle_wait_barrier +}; + +static const struct wp_fifo_manager_v1_interface fifo_manager_impl; +static struct wlr_fifo_manager_v1 *fifo_manager_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_fifo_manager_v1_interface, + &fifo_manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void fifo_manager_handle_get_fifo(struct wl_client *wl_client, struct wl_resource *manager_resource, + uint32_t id, struct wl_resource *surface_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + if (wlr_addon_find(&surface->addons, NULL, &surface_fifo_addon_impl) != NULL) { + wl_resource_post_error(manager_resource, + WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS, + "A wp_fifo_v1 object already exists for this surface"); + return; + } + + struct wlr_fifo_v1 *fifo = calloc(1, sizeof(*fifo)); + if (!fifo) { + wl_client_post_no_memory(wl_client); + return; + } + fifo->surface = surface; + struct wlr_fifo_manager_v1 *fifo_manager = + fifo_manager_v1_from_resource(manager_resource); + fifo->manager = fifo_manager; + fifo_manager->display = wl_client_get_display(wl_client); + + if (!wlr_surface_synced_init(&fifo->synced, surface, + &surface_synced_impl, &fifo->pending, &fifo->current)) { + free(fifo); + wl_client_post_no_memory(wl_client); + return; + } + + fifo->resource = wl_resource_create(wl_client, &wp_fifo_v1_interface, + wl_resource_get_version(manager_resource), id); + if (fifo->resource == NULL) { + wlr_surface_synced_finish(&fifo->synced); + free(fifo); + wl_client_post_no_memory(wl_client); + return; + } + wl_resource_set_implementation(fifo->resource, &fifo_implementation, fifo, + fifo_handle_resource_destroy); + + wl_list_init(&fifo->commits); + wl_signal_init(&fifo->events.destroy); + + fifo->surface_client_commit.notify = fifo_handle_client_commit; + wl_signal_add(&surface->events.client_commit, &fifo->surface_client_commit); + fifo->surface_commit.notify = fifo_handle_commit; + wl_signal_add(&surface->events.commit, &fifo->surface_commit); + + wlr_addon_init(&fifo->addon, &surface->addons, NULL, &surface_fifo_addon_impl); + + // If the surface is occluded, and there is no other surface updating the output's contents, + // then we won't receive any output events. For this case, we introduce a timer ticking at + // a heuristically defined value (40hz) so that we can advance the queue. + // + // The timer is armed only when the surface is occluded, and re-armed when it expires. + fifo->surface_occluded_source = + wl_event_loop_add_timer(wl_display_get_event_loop(fifo->manager->display), + handle_timer, fifo); + if (!fifo->surface_occluded_source) { + wl_resource_destroy(fifo->resource); + return; + } + + wl_signal_emit_mutable(&fifo->manager->events.new_fifo, + &(struct wlr_fifo_manager_v1_new_fifo_event){.fifo = fifo}); +} + +static void fifo_manager_handle_destroy(struct wl_client *wl_client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct wp_fifo_manager_v1_interface fifo_manager_impl = { + .get_fifo = fifo_manager_handle_get_fifo, + .destroy = fifo_manager_handle_destroy, +}; + +static void fifo_manager_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) { + struct wlr_fifo_manager_v1 *fifo_manager = data; + struct wl_resource *resource = + wl_resource_create(wl_client, &wp_fifo_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(wl_client); + return; + } + wl_resource_set_implementation(resource, &fifo_manager_impl, fifo_manager, NULL); +} + +static void fifo_manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_fifo_manager_v1 *fifo_manager = + wl_container_of(listener, fifo_manager, display_destroy); + wl_signal_emit_mutable(&fifo_manager->events.destroy, NULL); + wl_list_remove(&fifo_manager->display_destroy.link); + wl_global_destroy(fifo_manager->global); + free(fifo_manager); +} + +struct wlr_fifo_manager_v1 *wlr_fifo_manager_v1_create(struct wl_display *display, uint32_t version) { + assert(version <= FIFO_MANAGER_VERSION); + + struct wlr_fifo_manager_v1 *fifo_manager = calloc(1, sizeof(*fifo_manager)); + if (!fifo_manager) { + return NULL; + } + + fifo_manager->global = wl_global_create(display, &wp_fifo_manager_v1_interface, + version, fifo_manager, fifo_manager_bind); + if (!fifo_manager->global) { + free(fifo_manager); + return NULL; + } + + wl_signal_init(&fifo_manager->events.destroy); + wl_signal_init(&fifo_manager->events.new_fifo); + + fifo_manager->display_destroy.notify = fifo_manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &fifo_manager->display_destroy); + + return fifo_manager; +} + +void wlr_fifo_v1_set_output(struct wlr_fifo_v1 *fifo, struct wlr_output *output) { + fifo_reset(fifo); + + if (!output) { + // Here we arm the timer for the first time. + // When it expires, it might be re-armed at handle_timer(). + if (!fifo->surface_occluded_source_armed) { + wl_event_source_timer_update(fifo->surface_occluded_source, 25); + fifo->surface_occluded_source_armed = true; + } + return; + } + + fifo->output = output; + fifo->output_present.notify = fifo_handle_output_present; + wl_signal_add(&fifo->output->events.present, &fifo->output_present); + fifo->output_destroy.notify = fifo_handle_output_destroy; + wl_signal_add(&fifo->output->events.destroy, &fifo->output_destroy); +} From 9de6dc096403dfd3d2ebf9646163e9b1388ea563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Wed, 26 Nov 2025 10:56:16 -0500 Subject: [PATCH 3/4] fifo-v1: add scene implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sergio Gómez --- include/wlr/types/wlr_scene.h | 12 ++++++++ types/scene/surface.c | 7 +++++ types/scene/wlr_scene.c | 56 +++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 46635f4bf..52d99d643 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -104,6 +104,7 @@ struct wlr_scene { struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; struct wlr_color_manager_v1 *color_manager_v1; + struct wlr_fifo_manager_v1 *fifo_manager_v1; bool restack_xwayland_surfaces; @@ -112,6 +113,8 @@ struct wlr_scene { struct wl_listener gamma_control_manager_v1_destroy; struct wl_listener gamma_control_manager_v1_set_gamma; struct wl_listener color_manager_v1_destroy; + struct wl_listener fifo_manager_v1_destroy; + struct wl_listener fifo_manager_v1_new_fifo; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; @@ -125,6 +128,8 @@ struct wlr_scene_surface { struct wlr_scene_buffer *buffer; struct wlr_surface *surface; + struct wlr_fifo_v1 *fifo; + struct { struct wlr_box clip; @@ -137,6 +142,7 @@ struct wlr_scene_surface { struct wl_listener frame_done; struct wl_listener surface_destroy; struct wl_listener surface_commit; + struct wl_listener fifo_v1_destroy; } WLR_PRIVATE; }; @@ -381,6 +387,12 @@ void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, */ void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager); +/** + * Handles fifo_v1 for all surfaces and their primary outputs in the scene. + */ +void wlr_scene_set_fifo_manager_v1(struct wlr_scene *scene, + struct wlr_fifo_manager_v1 *fifo_manager); + /** * Add a node displaying nothing but its children. */ diff --git a/types/scene/surface.c b/types/scene/surface.c index bce8c74a6..bac89b556 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,9 @@ static void handle_scene_buffer_outputs_update( // If the surface is no longer visible on any output, keep the last sent // preferred configuration to avoid unnecessary redraws if (wl_list_empty(&surface->surface->current_outputs)) { + if (surface->fifo) { + wlr_fifo_v1_set_output(surface->fifo, NULL); + } return; } @@ -112,6 +116,9 @@ static void handle_scene_buffer_outputs_update( wlr_color_manager_v1_set_surface_preferred_image_description(scene->color_manager_v1, surface->surface, &img_desc); } + if (surface->fifo) { + wlr_fifo_v1_set_output(surface->fifo, surface->buffer->primary_output->output); + } } static void handle_scene_buffer_output_enter( diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index e7d628d01..41a58f117 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1640,6 +1641,61 @@ void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_ma wl_signal_add(&manager->events.destroy, &scene->color_manager_v1_destroy); } +static void scene_surface_handle_fifo_v1_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, fifo_v1_destroy); + wl_list_remove(&surface->fifo_v1_destroy.link); + surface->fifo = NULL; +} + +static void fifo_set_output(struct wlr_scene_buffer *scene_buffer, int x, int y, void *data) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + if (!scene_surface) { + return; + } + + struct wlr_fifo_v1 *fifo = data; + if (scene_surface->surface == fifo->surface) { + scene_surface->fifo = fifo; + scene_surface->fifo_v1_destroy.notify = scene_surface_handle_fifo_v1_destroy; + wl_signal_add(&fifo->events.destroy, &scene_surface->fifo_v1_destroy); + wlr_fifo_v1_set_output(fifo, scene_buffer->primary_output ? + scene_buffer->primary_output->output : NULL); + } +} + +static void scene_handle_fifo_manager_v1_new_fifo(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, fifo_manager_v1_new_fifo); + struct wlr_fifo_manager_v1_new_fifo_event *event = data; + + wlr_scene_node_for_each_buffer(&scene->tree.node, fifo_set_output, event->fifo); +} + +static void scene_handle_fifo_manager_v1_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, fifo_manager_v1_destroy); + wl_list_remove(&scene->fifo_manager_v1_destroy.link); + wl_list_init(&scene->fifo_manager_v1_destroy.link); + wl_list_remove(&scene->fifo_manager_v1_new_fifo.link); + wl_list_init(&scene->fifo_manager_v1_new_fifo.link); + scene->fifo_manager_v1 = NULL; +} + +void wlr_scene_set_fifo_manager_v1(struct wlr_scene *scene, + struct wlr_fifo_manager_v1 *fifo_manager) { + assert(scene->fifo_manager_v1 == NULL); + scene->fifo_manager_v1 = fifo_manager; + scene->fifo_manager_v1_new_fifo.notify = scene_handle_fifo_manager_v1_new_fifo; + wl_signal_add(&fifo_manager->events.new_fifo, &scene->fifo_manager_v1_new_fifo); + scene->fifo_manager_v1_destroy.notify = scene_handle_fifo_manager_v1_destroy; + wl_signal_add(&fifo_manager->events.destroy, &scene->fifo_manager_v1_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); From 8a65e9404e80163200c319a5aa7cb94535695c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Fri, 9 Jan 2026 09:44:24 -0500 Subject: [PATCH 4/4] scene: include disabled nodes in scene buffer iterator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sergio Gómez --- types/scene/wlr_scene.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 41a58f117..17056e852 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1351,10 +1351,6 @@ bool wlr_scene_node_coords(struct wlr_scene_node *node, static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { - if (!node->enabled) { - return; - } - lx += node->x; ly += node->y;