Created
June 27, 2026 06:30
-
-
Save hwsmm/9e3c1ade25d903e1c1ca9a358ed8a4c8 to your computer and use it in GitHub Desktop.
patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/include/util/time.h b/include/util/time.h | |
| index dd164bf2..fd2bf962 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/include/wlr/types/wlr_fifo_v1.h b/include/wlr/types/wlr_fifo_v1.h | |
| new file mode 100644 | |
| index 00000000..4ea9c18a | |
| --- /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 <wayland-server-core.h> | |
| + | |
| +#include <wlr/types/wlr_compositor.h> | |
| +#include <wlr/types/wlr_scene.h> | |
| + | |
| +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/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h | |
| index 6c627b1f..a40204f5 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; | |
| @@ -135,6 +140,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; | |
| }; | |
| @@ -383,6 +389,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/protocol/meson.build b/protocol/meson.build | |
| index 30aeb68c..eb8ca605 100644 | |
| --- a/protocol/meson.build | |
| +++ b/protocol/meson.build | |
| @@ -37,6 +37,7 @@ protocols = { | |
| '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', | |
| 'ext-workspace-v1': wl_protocol_dir / 'staging/ext-workspace/ext-workspace-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 26958f04..bd29ac42 100644 | |
| --- a/types/meson.build | |
| +++ b/types/meson.build | |
| @@ -77,6 +77,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/scene/surface.c b/types/scene/surface.c | |
| index 89c54a9e..bbce19b7 100644 | |
| --- a/types/scene/surface.c | |
| +++ b/types/scene/surface.c | |
| @@ -5,6 +5,7 @@ | |
| #include <wlr/types/wlr_color_representation_v1.h> | |
| #include <wlr/types/wlr_compositor.h> | |
| #include <wlr/types/wlr_scene.h> | |
| +#include <wlr/types/wlr_fifo_v1.h> | |
| #include <wlr/types/wlr_fractional_scale_v1.h> | |
| #include <wlr/types/wlr_linux_drm_syncobj_v1.h> | |
| #include <wlr/types/wlr_output.h> | |
| @@ -134,6 +135,9 @@ static void handle_scene_buffer_outputs_update( | |
| // No reason to update the preferred configuration if we aren't sending leave/enter events. | |
| if (suspend) { | |
| + if (surface->fifo) { | |
| + wlr_fifo_v1_set_output(surface->fifo, NULL); | |
| + } | |
| return; | |
| } | |
| @@ -153,6 +157,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_sample( | |
| diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c | |
| index 54c09bbf..62c44330 100644 | |
| --- a/types/scene/wlr_scene.c | |
| +++ b/types/scene/wlr_scene.c | |
| @@ -8,6 +8,7 @@ | |
| #include <wlr/types/wlr_color_management_v1.h> | |
| #include <wlr/types/wlr_compositor.h> | |
| #include <wlr/types/wlr_damage_ring.h> | |
| +#include <wlr/types/wlr_fifo_v1.h> | |
| #include <wlr/types/wlr_gamma_control_v1.h> | |
| #include <wlr/types/wlr_linux_dmabuf_v1.h> | |
| #include <wlr/types/wlr_presentation_time.h> | |
| @@ -1655,6 +1656,72 @@ 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 scene_fifo_set_output(struct wlr_scene_node *node, struct wlr_fifo_v1 *fifo) { | |
| + if (node->type == WLR_SCENE_NODE_TREE) { | |
| + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); | |
| + struct wlr_scene_node *child; | |
| + wl_list_for_each(child, &scene_tree->children, link) { | |
| + scene_fifo_set_output(child, fifo); | |
| + } | |
| + } | |
| + if (node->type != WLR_SCENE_NODE_BUFFER) { | |
| + return; | |
| + } | |
| + | |
| + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); | |
| + struct wlr_scene_surface *scene_surface = | |
| + wlr_scene_surface_try_from_buffer(scene_buffer); | |
| + if (!scene_surface) { | |
| + return; | |
| + } | |
| + | |
| + 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; | |
| + | |
| + scene_fifo_set_output(&scene->tree.node, 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); | |
| diff --git a/types/wlr_fifo_v1.c b/types/wlr_fifo_v1.c | |
| new file mode 100644 | |
| index 00000000..31bb2cad | |
| --- /dev/null | |
| +++ b/types/wlr_fifo_v1.c | |
| @@ -0,0 +1,363 @@ | |
| +#include <assert.h> | |
| +#include <stdlib.h> | |
| +#include <wlr/types/wlr_compositor.h> | |
| +#include <wlr/types/wlr_output.h> | |
| +#include <wlr/types/wlr_fifo_v1.h> | |
| +#include <wlr/util/log.h> | |
| +#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 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_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 || !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 fifo_destroy(struct wlr_fifo_v1 *fifo) { | |
| + if (!fifo) { | |
| + return; | |
| + } | |
| + struct wl_resource *fifo_resource = fifo->resource; | |
| + fifo_handle_resource_destroy(fifo->resource); | |
| + wl_resource_set_user_data(fifo_resource, NULL); | |
| +} | |
| + | |
| +static void surface_fifo_addon_handle_destroy(struct wlr_addon *addon) { | |
| + struct wlr_fifo_v1 *fifo = wl_container_of(addon, fifo, addon); | |
| + fifo_destroy(fifo); | |
| +} | |
| + | |
| +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_client_post_no_memory(wl_client); | |
| + 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); | |
| +} | |
| diff --git a/util/time.c b/util/time.c | |
| index 1a8f3296..7db0893b 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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment