mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-02-05 04:06:11 -05:00
Merge branch 'commit_timing_v1' into 'master'
commit-timing-v1: new protocol implementation See merge request wlroots/wlroots!4617
This commit is contained in:
commit
d04d3dce4d
9 changed files with 575 additions and 0 deletions
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
91
include/wlr/types/wlr_commit_timing_v1.h
Normal file
91
include/wlr/types/wlr_commit_timing_v1.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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_TIMING_H
|
||||
#define WLR_TYPES_WLR_TIMING_H
|
||||
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
|
||||
struct wlr_commit_timing_manager_v1_new_timer_event {
|
||||
struct wlr_commit_timer_v1 *timer;
|
||||
};
|
||||
|
||||
struct wlr_commit_timer_v1_commit {
|
||||
struct wlr_commit_timer_v1 *timer;
|
||||
|
||||
/**
|
||||
* Commits' wlr_surface pending sequence when locking through
|
||||
* wlr_surface_lock_pending(). Used to unlock the commit when the 'unlock_timer' goes off.
|
||||
*/
|
||||
uint32_t pending_seq;
|
||||
|
||||
/**
|
||||
* Timer for when this commit should be unlocked for presentation.
|
||||
*/
|
||||
struct wl_event_source *unlock_source;
|
||||
|
||||
struct wl_list link; // wlr_commit_timer_v1::commits
|
||||
};
|
||||
|
||||
struct wlr_commit_timer_v1_state {
|
||||
uint64_t base_present_nsec;
|
||||
int32_t refresh;
|
||||
uint64_t timestamp_nsec; // holds the timestamp for the .set_timestamp protocol request
|
||||
};
|
||||
|
||||
struct wlr_commit_timer_v1 {
|
||||
struct wlr_commit_timing_manager_v1 *timing_manager;
|
||||
|
||||
struct wl_resource *resource;
|
||||
struct wl_display *wl_display;
|
||||
struct wlr_addon addon;
|
||||
|
||||
struct wlr_surface *surface;
|
||||
struct wlr_output *output;
|
||||
|
||||
struct wlr_commit_timer_v1_state state;
|
||||
|
||||
struct {
|
||||
struct wl_signal destroy;
|
||||
} events;
|
||||
|
||||
struct {
|
||||
struct wl_listener client_commit;
|
||||
struct wl_listener output_present;
|
||||
struct wl_listener output_destroy;
|
||||
} WLR_PRIVATE;
|
||||
|
||||
struct wl_list commits; // wlr_commit_timer_v1_commit.link
|
||||
|
||||
struct wl_list scene_link; // wlr_scene.commit_timers
|
||||
};
|
||||
|
||||
struct wlr_commit_timing_manager_v1 {
|
||||
struct wl_global *global;
|
||||
|
||||
struct wl_listener display_destroy;
|
||||
|
||||
struct {
|
||||
struct wl_signal new_timer;
|
||||
struct wl_signal destroy;
|
||||
} events;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the output from which we will get the refresh cycle timings for this wlr_commit_timer_v1.
|
||||
*/
|
||||
void wlr_commit_timer_v1_set_output(struct wlr_commit_timer_v1 *timer, struct wlr_output *output);
|
||||
|
||||
/**
|
||||
* Create the wp_commit_timing_manager_v1_interface global, which can be used by clients to
|
||||
* set timestamps for surface commit request presentation.
|
||||
*/
|
||||
struct wlr_commit_timing_manager_v1 *wlr_commit_timing_manager_v1_create(struct wl_display *display,
|
||||
uint32_t version);
|
||||
|
||||
#endif
|
||||
|
|
@ -104,6 +104,8 @@ 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_commit_timing_manager_v1 *commit_timing_manager_v1;
|
||||
struct wl_list commit_timers; // wlr_commit_timer_v1.scene_link
|
||||
|
||||
bool restack_xwayland_surfaces;
|
||||
|
||||
|
|
@ -112,6 +114,9 @@ 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 commit_timing_manager_v1_destroy;
|
||||
struct wl_listener commit_timing_manager_v1_new_timer;
|
||||
struct wl_listener commit_timer_v1_destroy;
|
||||
|
||||
enum wlr_scene_debug_damage_option debug_damage_option;
|
||||
bool direct_scanout;
|
||||
|
|
@ -381,6 +386,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 commit_timing_manager_v1 for all surfaces and their primary outputs in the scene.
|
||||
*/
|
||||
void wlr_scene_set_commit_timing_manager_v1(struct wlr_scene *scene,
|
||||
struct wlr_commit_timing_manager_v1 *timing_manager);
|
||||
|
||||
/**
|
||||
* Add a node displaying nothing but its children.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ protocols = {
|
|||
'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
|
||||
'color-management-v1': wl_protocol_dir / 'staging/color-management/color-management-v1.xml',
|
||||
'color-representation-v1': wl_protocol_dir / 'staging/color-representation/color-representation-v1.xml',
|
||||
'commit-timing-v1': wl_protocol_dir / 'staging/commit-timing/commit-timing-v1.xml',
|
||||
'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
|
||||
'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
|
||||
'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ wlr_files += files(
|
|||
'wlr_presentation_time.c',
|
||||
'wlr_primary_selection_v1.c',
|
||||
'wlr_primary_selection.c',
|
||||
'wlr_commit_timing_v1.c',
|
||||
'wlr_region.c',
|
||||
'wlr_relative_pointer_v1.c',
|
||||
'wlr_screencopy_v1.c',
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <wlr/types/wlr_alpha_modifier_v1.h>
|
||||
#include <wlr/types/wlr_color_management_v1.h>
|
||||
#include <wlr/types/wlr_color_representation_v1.h>
|
||||
#include <wlr/types/wlr_commit_timing_v1.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include <wlr/types/wlr_fractional_scale_v1.h>
|
||||
|
|
@ -112,6 +113,14 @@ static void handle_scene_buffer_outputs_update(
|
|||
wlr_color_manager_v1_set_surface_preferred_image_description(scene->color_manager_v1,
|
||||
surface->surface, &img_desc);
|
||||
}
|
||||
|
||||
struct wlr_commit_timer_v1 *timer;
|
||||
wl_list_for_each(timer, &scene->commit_timers, scene_link) {
|
||||
if (timer->surface == surface->surface && surface->buffer->active_outputs &&
|
||||
timer->output != surface->buffer->primary_output->output) {
|
||||
wlr_commit_timer_v1_set_output(timer, surface->buffer->primary_output->output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_scene_buffer_output_enter(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <wlr/render/drm_syncobj.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_color_management_v1.h>
|
||||
#include <wlr/types/wlr_commit_timing_v1.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_damage_ring.h>
|
||||
#include <wlr/types/wlr_gamma_control_v1.h>
|
||||
|
|
@ -181,6 +182,7 @@ struct wlr_scene *wlr_scene_create(void) {
|
|||
scene_tree_init(&scene->tree, NULL);
|
||||
|
||||
wl_list_init(&scene->outputs);
|
||||
wl_list_init(&scene->commit_timers);
|
||||
wl_list_init(&scene->linux_dmabuf_v1_destroy.link);
|
||||
wl_list_init(&scene->gamma_control_manager_v1_destroy.link);
|
||||
wl_list_init(&scene->gamma_control_manager_v1_set_gamma.link);
|
||||
|
|
@ -1653,6 +1655,78 @@ 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_handle_commit_timer_v1_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_scene *scene =
|
||||
wl_container_of(listener, scene, commit_timer_v1_destroy);
|
||||
struct wlr_commit_timer_v1 *timer = data;
|
||||
struct wlr_surface *surface = timer->surface;
|
||||
|
||||
struct wlr_commit_timer_v1 *tmp_timer;
|
||||
wl_list_for_each_safe(timer, tmp_timer, &scene->commit_timers, scene_link) {
|
||||
if (timer->surface == surface) {
|
||||
wl_list_remove(&timer->scene_link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_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_commit_timer_v1 *timer = data;
|
||||
if (scene_surface->surface == timer->surface) {
|
||||
struct wlr_scene_output *primary_output = scene_surface->buffer->primary_output;
|
||||
if (primary_output) {
|
||||
wlr_commit_timer_v1_set_output(timer, primary_output->output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scene_handle_commit_timing_manager_v1_new_timer(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_scene *scene =
|
||||
wl_container_of(listener, scene, commit_timing_manager_v1_new_timer);
|
||||
struct wlr_commit_timing_manager_v1_new_timer_event *event = data;
|
||||
struct wlr_commit_timer_v1 *timer = event->timer;
|
||||
|
||||
wl_list_insert(&scene->commit_timers, &timer->scene_link);
|
||||
scene->commit_timer_v1_destroy.notify = scene_handle_commit_timer_v1_destroy;
|
||||
wl_signal_add(&timer->events.destroy, &scene->commit_timer_v1_destroy);
|
||||
|
||||
wlr_scene_node_for_each_buffer(&scene->tree.node, timer_set_output, event->timer);
|
||||
}
|
||||
|
||||
static void scene_handle_commit_timing_manager_v1_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_scene *scene =
|
||||
wl_container_of(listener, scene, commit_timing_manager_v1_destroy);
|
||||
wl_list_remove(&scene->commit_timing_manager_v1_destroy.link);
|
||||
wl_list_init(&scene->commit_timing_manager_v1_destroy.link);
|
||||
wl_list_remove(&scene->commit_timing_manager_v1_new_timer.link);
|
||||
wl_list_init(&scene->commit_timing_manager_v1_new_timer.link);
|
||||
scene->commit_timing_manager_v1 = NULL;
|
||||
}
|
||||
|
||||
void wlr_scene_set_commit_timing_manager_v1(struct wlr_scene *scene,
|
||||
struct wlr_commit_timing_manager_v1 *commit_timing) {
|
||||
assert(scene->commit_timing_manager_v1 == NULL);
|
||||
|
||||
scene->commit_timing_manager_v1 = commit_timing;
|
||||
|
||||
scene->commit_timing_manager_v1_new_timer.notify =
|
||||
scene_handle_commit_timing_manager_v1_new_timer;
|
||||
wl_signal_add(&commit_timing->events.new_timer,
|
||||
&scene->commit_timing_manager_v1_new_timer);
|
||||
scene->commit_timing_manager_v1_destroy.notify =
|
||||
scene_handle_commit_timing_manager_v1_destroy;
|
||||
wl_signal_add(&commit_timing->events.destroy,
|
||||
&scene->commit_timing_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);
|
||||
|
|
|
|||
377
types/wlr_commit_timing_v1.c
Normal file
377
types/wlr_commit_timing_v1.c
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <wlr/types/wlr_commit_timing_v1.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "commit-timing-v1-protocol.h"
|
||||
#include "util/time.h"
|
||||
|
||||
#define TIMING_MANAGER_VERSION 1
|
||||
|
||||
static void commit_destroy(struct wlr_commit_timer_v1_commit *commit) {
|
||||
wl_event_source_remove(commit->unlock_source);
|
||||
// the remove from the list must happen before unlocking the commit, since the commit might end
|
||||
// up calling wlr_commit_timer_v1_set_output(), which traverses this list.
|
||||
wl_list_remove(&commit->link);
|
||||
wlr_surface_unlock_cached(commit->timer->surface, commit->pending_seq);
|
||||
free(commit);
|
||||
}
|
||||
|
||||
static int handle_commit_timerfd(int fd, uint32_t mask, void *data) {
|
||||
struct wlr_commit_timer_v1_commit *commit = data;
|
||||
commit_destroy(commit);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timer_handle_output_present(struct wl_listener *listener, void *data) {
|
||||
struct wlr_commit_timer_v1 *timer =
|
||||
wl_container_of(listener, timer, output_present);
|
||||
struct wlr_output_event_present *event = data;
|
||||
|
||||
// we constantly check if the refresh rate has changed, to reset accordingly
|
||||
if (timer->state.refresh != event->output->refresh) {
|
||||
wlr_commit_timer_v1_set_output(timer, event->output);
|
||||
}
|
||||
|
||||
// we need to have just one presentation time so that, together with the refresh rate, we
|
||||
// can know the refresh cycle offset for future presentations.
|
||||
if (event->presented && !timer->state.base_present_nsec) {
|
||||
timer->state.base_present_nsec = timespec_to_nsec(&event->when);
|
||||
}
|
||||
}
|
||||
|
||||
static bool target_is_in_past(uint64_t target_nsec) {
|
||||
return target_nsec < (uint64_t)get_current_time_nsec();
|
||||
}
|
||||
|
||||
static int mhz_to_nsec(int mhz) {
|
||||
assert(mhz != 0);
|
||||
return 1000000000000LL / mhz;
|
||||
}
|
||||
|
||||
static uint64_t timer_get_target_nsec(const struct wlr_commit_timer_v1 *timer) {
|
||||
struct wlr_output *output = timer->output;
|
||||
uint64_t target_nsec = timer->state.timestamp_nsec;
|
||||
|
||||
// ignore the request if the output is not stable yet, or the timestamp is invalid
|
||||
if (!output || !timer->state.base_present_nsec || !target_nsec ||
|
||||
target_is_in_past(target_nsec)) {
|
||||
target_nsec = 0;
|
||||
goto out;
|
||||
}
|
||||
// if output has no refresh rate, use requested timestamp as is
|
||||
if (!output->refresh) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint64_t refresh_nsec = mhz_to_nsec(output->refresh);
|
||||
uint64_t cycle_phase_nsec = timer->state.base_present_nsec % refresh_nsec;
|
||||
|
||||
uint64_t round_to_nearest_refresh_nsec = target_nsec;
|
||||
round_to_nearest_refresh_nsec -= cycle_phase_nsec;
|
||||
round_to_nearest_refresh_nsec += refresh_nsec/2;
|
||||
round_to_nearest_refresh_nsec -= (round_to_nearest_refresh_nsec % refresh_nsec);
|
||||
round_to_nearest_refresh_nsec += cycle_phase_nsec;
|
||||
|
||||
target_nsec = round_to_nearest_refresh_nsec;
|
||||
|
||||
if (timer->state.refresh) {
|
||||
// Subtract 1 refresh cycle to the target time.
|
||||
// This guarantees that the surface commit is unlocked before the
|
||||
// compositor receives the .frame event for the refresh cycle we want to target.
|
||||
target_nsec -= mhz_to_nsec(timer->state.refresh);
|
||||
}
|
||||
|
||||
target_nsec -= 500000; // subtract a 500us slop so that we don't miss the target
|
||||
|
||||
// check if adjusted target time is in the past
|
||||
if (target_is_in_past(target_nsec)) {
|
||||
target_nsec = 0;
|
||||
}
|
||||
out:
|
||||
return target_nsec;
|
||||
}
|
||||
|
||||
static void timer_handle_client_commit(struct wl_listener *listener, void *data) {
|
||||
struct wlr_commit_timer_v1 *timer =
|
||||
wl_container_of(listener, timer, client_commit);
|
||||
struct wlr_commit_timer_v1_commit *commit = NULL;
|
||||
|
||||
uint64_t target_nsec = timer_get_target_nsec(timer);
|
||||
timer->state.timestamp_nsec = 0; // reset the timestamp
|
||||
// if the target time is invalid, or if it's too close to the current time (<1ms),
|
||||
// don't bother.
|
||||
if (!target_nsec || target_nsec - get_current_time_nsec() < 1000000) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
commit = calloc(1, sizeof(*commit));
|
||||
if (!commit) {
|
||||
wl_client_post_no_memory(wl_resource_get_client(timer->resource));
|
||||
goto out;
|
||||
}
|
||||
commit->timer = timer;
|
||||
|
||||
int timerfd_unlock_commit_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
|
||||
struct itimerspec itspec = { .it_interval = 0 };
|
||||
timespec_from_nsec(&itspec.it_value, target_nsec);
|
||||
timerfd_settime(timerfd_unlock_commit_fd, TFD_TIMER_ABSTIME, &itspec, NULL);
|
||||
commit->unlock_source =
|
||||
wl_event_loop_add_fd(wl_display_get_event_loop(timer->wl_display),
|
||||
timerfd_unlock_commit_fd, WL_EVENT_READABLE, handle_commit_timerfd, commit);
|
||||
if (!commit->unlock_source) {
|
||||
wl_client_post_no_memory(wl_resource_get_client(timer->resource));
|
||||
close(timerfd_unlock_commit_fd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
commit->pending_seq = wlr_surface_lock_pending(timer->surface);
|
||||
|
||||
wl_list_insert(&timer->commits, &commit->link);
|
||||
|
||||
return;
|
||||
out:
|
||||
free(commit);
|
||||
}
|
||||
|
||||
static const struct wp_commit_timer_v1_interface timer_impl;
|
||||
|
||||
static struct wlr_commit_timer_v1 *wlr_commit_timer_v1_from_resource(struct wl_resource *resource) {
|
||||
assert(wl_resource_instance_of(resource, &wp_commit_timer_v1_interface,
|
||||
&timer_impl));
|
||||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static void timer_handle_set_timestamp(struct wl_client *client,
|
||||
struct wl_resource *resource, uint32_t tv_sec_hi, uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec) {
|
||||
struct wlr_commit_timer_v1 *timer = wlr_commit_timer_v1_from_resource(resource);
|
||||
|
||||
if (timer->state.timestamp_nsec) {
|
||||
wl_resource_post_error(resource,
|
||||
WP_COMMIT_TIMER_V1_ERROR_TIMESTAMP_EXISTS,
|
||||
"surface already has a timestamp");
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t tv_sec = (uint64_t)tv_sec_hi<<32 | tv_sec_lo;
|
||||
// check for overflow
|
||||
if (tv_nsec >= NSEC_PER_SEC ||
|
||||
tv_sec > (UINT64_MAX - tv_nsec) / NSEC_PER_SEC) {
|
||||
wl_resource_post_error(resource,
|
||||
WP_COMMIT_TIMER_V1_ERROR_INVALID_TIMESTAMP,
|
||||
"invalid timestamp");
|
||||
return;
|
||||
}
|
||||
|
||||
timer->state.timestamp_nsec = tv_sec * NSEC_PER_SEC + tv_nsec;
|
||||
}
|
||||
|
||||
static void timer_handle_destroy(struct wl_client *client, struct wl_resource *resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static const struct wp_commit_timer_v1_interface timer_impl = {
|
||||
.destroy = timer_handle_destroy,
|
||||
.set_timestamp = timer_handle_set_timestamp
|
||||
};
|
||||
|
||||
static void surface_addon_destroy(struct wlr_addon *addon) {
|
||||
struct wlr_commit_timer_v1 *timer = wl_container_of(addon, timer, addon);
|
||||
wl_resource_destroy(timer->resource);
|
||||
}
|
||||
|
||||
static const struct wlr_addon_interface surface_addon_impl = {
|
||||
.name = "wp_commit_timer_v1",
|
||||
.destroy = surface_addon_destroy,
|
||||
};
|
||||
|
||||
static void timer_reset(struct wlr_commit_timer_v1 *timer) {
|
||||
struct wlr_commit_timer_v1_commit *commit, *tmp_co;
|
||||
wl_list_for_each_safe(commit, tmp_co, &timer->commits, link) {
|
||||
commit_destroy(commit);
|
||||
}
|
||||
if (timer->output) {
|
||||
timer->output_present.notify = NULL;
|
||||
wl_list_remove(&timer->output_present.link);
|
||||
timer->output_destroy.notify = NULL;
|
||||
wl_list_remove(&timer->output_destroy.link);
|
||||
timer->output = NULL;
|
||||
}
|
||||
memset(&timer->state, 0, sizeof(timer->state));
|
||||
}
|
||||
|
||||
static void timer_handle_output_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_commit_timer_v1 *timer =
|
||||
wl_container_of(listener, timer, output_destroy);
|
||||
timer_reset(timer);
|
||||
}
|
||||
|
||||
static void timer_handle_resource_destroy(struct wl_resource *resource) {
|
||||
struct wlr_commit_timer_v1 *timer = wlr_commit_timer_v1_from_resource(resource);
|
||||
timer_reset(timer);
|
||||
wlr_addon_finish(&timer->addon);
|
||||
wl_list_remove(&timer->client_commit.link);
|
||||
wl_signal_emit_mutable(&timer->events.destroy, timer);
|
||||
free(timer);
|
||||
}
|
||||
|
||||
static struct wlr_commit_timer_v1 *commit_timer_create(struct wl_client *wl_client, uint32_t version,
|
||||
uint32_t id, struct wlr_surface *surface) {
|
||||
struct wlr_commit_timer_v1 *timer = calloc(1, sizeof(*timer));
|
||||
if (timer == NULL) {
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
timer->resource = wl_resource_create(wl_client, &wp_commit_timer_v1_interface, version, id);
|
||||
if (timer->resource == NULL) {
|
||||
goto err_alloc;
|
||||
}
|
||||
wl_resource_set_implementation(timer->resource, &timer_impl, timer,
|
||||
timer_handle_resource_destroy);
|
||||
|
||||
wl_list_init(&timer->commits);
|
||||
|
||||
/* we will use the wl_display to add a timer to the wl_event_loop */
|
||||
timer->wl_display = wl_client_get_display(wl_client);
|
||||
|
||||
timer->surface = surface;
|
||||
timer->client_commit.notify = timer_handle_client_commit;
|
||||
wl_signal_add(&timer->surface->events.client_commit, &timer->client_commit);
|
||||
|
||||
wlr_log(WLR_DEBUG, "New wlr_commit_timer_v1 %p (res %p)", timer, timer->resource);
|
||||
|
||||
return timer;
|
||||
|
||||
err_alloc:
|
||||
free(timer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct wp_commit_timing_manager_v1_interface timing_manager_impl;
|
||||
static struct wlr_commit_timing_manager_v1 *wlr_commit_timing_manager_v1_from_resource(struct wl_resource *resource) {
|
||||
assert(wl_resource_instance_of(resource, &wp_commit_timing_manager_v1_interface,
|
||||
&timing_manager_impl));
|
||||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static void timing_manager_handle_get_timer(struct wl_client *wl_client,
|
||||
struct wl_resource *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_addon_impl) != NULL) {
|
||||
wl_resource_post_error(resource,
|
||||
WP_COMMIT_TIMING_MANAGER_V1_ERROR_COMMIT_TIMER_EXISTS,
|
||||
"A wp_commit_timer_v1 object already exists for this surface");
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_commit_timer_v1 *timer =
|
||||
commit_timer_create(wl_client, wl_resource_get_version(resource), id, surface);
|
||||
if (!timer) {
|
||||
wl_client_post_no_memory(wl_client);
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_addon_init(&timer->addon, &surface->addons, NULL, &surface_addon_impl);
|
||||
wl_signal_init(&timer->events.destroy);
|
||||
|
||||
struct wlr_commit_timing_manager_v1 *manager =
|
||||
wlr_commit_timing_manager_v1_from_resource(resource);
|
||||
|
||||
timer->timing_manager = manager;
|
||||
|
||||
// it is possible that at this time we have no outputs assigned to the surface yet
|
||||
struct wlr_surface_output *surface_output = NULL;
|
||||
if (!wl_list_empty(&surface->current_outputs)) {
|
||||
wl_list_for_each(surface_output, &surface->current_outputs, link) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
wlr_commit_timer_v1_set_output(timer, surface_output ? surface_output->output : NULL);
|
||||
|
||||
wl_signal_emit_mutable(&timer->timing_manager->events.new_timer,
|
||||
&(struct wlr_commit_timing_manager_v1_new_timer_event){.timer = timer});
|
||||
}
|
||||
|
||||
static void timing_manager_handle_destroy(struct wl_client *wl_client,
|
||||
struct wl_resource *resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static const struct wp_commit_timing_manager_v1_interface timing_manager_impl = {
|
||||
.get_timer = timing_manager_handle_get_timer,
|
||||
.destroy = timing_manager_handle_destroy,
|
||||
};
|
||||
|
||||
static void timing_manager_bind(struct wl_client *client, void *data, uint32_t version,
|
||||
uint32_t id) {
|
||||
struct wl_resource *resource =
|
||||
wl_resource_create(client, &wp_commit_timing_manager_v1_interface, version, id);
|
||||
if (resource == NULL) {
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_commit_timing_manager_v1 *manager = data;
|
||||
wl_resource_set_implementation(resource, &timing_manager_impl, manager, NULL);
|
||||
}
|
||||
|
||||
static void handle_display_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_commit_timing_manager_v1 *manager =
|
||||
wl_container_of(listener, manager, display_destroy);
|
||||
wl_signal_emit_mutable(&manager->events.destroy, manager);
|
||||
wl_list_remove(&manager->display_destroy.link);
|
||||
wl_global_destroy(manager->global);
|
||||
free(manager);
|
||||
}
|
||||
|
||||
struct wlr_commit_timing_manager_v1 *wlr_commit_timing_manager_v1_create(struct wl_display *display,
|
||||
uint32_t version) {
|
||||
assert(version <= TIMING_MANAGER_VERSION);
|
||||
|
||||
struct wlr_commit_timing_manager_v1 *manager = calloc(1, sizeof(*manager));
|
||||
if (!manager) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
manager->global = wl_global_create(display, &wp_commit_timing_manager_v1_interface,
|
||||
version, manager, timing_manager_bind);
|
||||
if (!manager->global) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
wl_signal_init(&manager->events.new_timer);
|
||||
wl_signal_init(&manager->events.destroy);
|
||||
|
||||
manager->display_destroy.notify = handle_display_destroy;
|
||||
wl_display_add_destroy_listener(display, &manager->display_destroy);
|
||||
|
||||
return manager;
|
||||
|
||||
err_out:
|
||||
free(manager);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void wlr_commit_timer_v1_set_output(struct wlr_commit_timer_v1 *timer,
|
||||
struct wlr_output *output) {
|
||||
timer_reset(timer);
|
||||
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
timer->output = output;
|
||||
// we make a copy of the refresh rate so that we can check for whenever it changes
|
||||
timer->state.refresh = output->refresh;
|
||||
|
||||
timer->output_present.notify = timer_handle_output_present;
|
||||
wl_signal_add(&output->events.present, &timer->output_present);
|
||||
timer->output_destroy.notify = timer_handle_output_destroy;
|
||||
wl_signal_add(&output->events.destroy, &timer->output_destroy);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue