diff --git a/include/util/time.h b/include/util/time.h index 3f76aa47f..1fcb569cf 100644 --- a/include/util/time.h +++ b/include/util/time.h @@ -4,6 +4,8 @@ #include #include +static const long NSEC_PER_SEC = 1000000000; + /** * Get the current time, in milliseconds. */ @@ -30,4 +32,6 @@ void timespec_from_nsec(struct timespec *r, int64_t nsec); void timespec_sub(struct timespec *r, const struct timespec *a, const struct timespec *b); +int32_t mhz_to_nsec(int32_t mhz); + #endif diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 004be0f1d..c29b9e225 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -34,6 +34,8 @@ struct wlr_cursor { struct wlr_cursor_state *state; double x, y; + int max_latency; + /** * The interpretation of these signals is the responsibility of the * compositor, but some helpers are provided for your benefit. If you @@ -166,6 +168,8 @@ void wlr_cursor_set_xcursor(struct wlr_cursor *cur, void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y); +void wlr_cursor_set_max_latency(struct wlr_cursor *cur, int max_latency); + /** * Attaches this input device to this cursor. The input device must be one of: * diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 9de8dffba..31e2b9da9 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -43,9 +43,15 @@ struct wlr_output_cursor { struct wlr_fbox src_box; enum wl_output_transform transform; int32_t hotspot_x, hotspot_y; + // only when using a software cursor without a surface struct wlr_texture *texture; bool own_texture; struct wl_list link; + + struct timespec last_presentation; + bool deferred; + double deferred_x, deferred_y; + int max_latency; }; enum wlr_output_adaptive_sync_status { @@ -561,6 +567,13 @@ bool wlr_output_cursor_set_buffer(struct wlr_output_cursor *cursor, struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y); bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, double x, double y); +/** + * Call any_expired() before you check needs_frame. + * call all_deferred() whenever a new frame is needed. + * */ +void wlr_output_cursor_move_expired(struct wlr_output_cursor *cursor, struct timespec *now); +void wlr_output_cursor_move_any_expired(struct wlr_output *output, struct timespec *now); +void wlr_output_cursor_move_all_deferred(struct wlr_output *output, struct timespec *now); void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor); /** diff --git a/types/output/cursor.c b/types/output/cursor.c index ad37a048a..170a9b003 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -12,6 +13,9 @@ #include "types/wlr_buffer.h" #include "types/wlr_output.h" +#include +#include + static bool output_set_hardware_cursor(struct wlr_output *output, struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { if (!output->impl->set_cursor) { @@ -461,11 +465,10 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, return true; } -bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, - double x, double y) { - // Scale coordinates for the output - x *= cursor->output->scale; - y *= cursor->output->scale; +static bool output_cursor_move(struct wlr_output_cursor *cursor, + double x, double y, struct timespec *now) { + cursor->last_presentation = *now; + cursor->deferred = false; if (cursor->x == x && cursor->y == y) { return true; @@ -494,6 +497,71 @@ bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, return cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y); } +static bool output_cursor_move_should_defer(struct wlr_output_cursor *cursor, + struct timespec *now) { + if (!cursor->max_latency + || !cursor->output->refresh // avoid divide by zero + || cursor->output->adaptive_sync_status != WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) + return false; + + struct timespec delta; + int32_t vrr_min = NSEC_PER_SEC / 30; // edid? enforce 30fps minimum for now. + timespec_sub(&delta, now, &cursor->last_presentation); + if (delta.tv_sec + || delta.tv_nsec >= cursor->max_latency + || delta.tv_nsec >= vrr_min) + return false; + + return true; +} + +bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, + double x, double y) { + // Scale coordinates for the output + x *= cursor->output->scale; + y *= cursor->output->scale; + + if (cursor->x == x && cursor->y == y) { + return true; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + if (output_cursor_move_should_defer(cursor, &now)) { + cursor->deferred_x = x; + cursor->deferred_y = y; + cursor->deferred = true; + return true; + } + + return output_cursor_move(cursor, x, y, &now); +} + +void wlr_output_cursor_move_expired(struct wlr_output_cursor *cursor, struct timespec *now) { + if (cursor->deferred && !output_cursor_move_should_defer(cursor, now)) + output_cursor_move(cursor, cursor->deferred_x, cursor->deferred_y, now); +} + +void wlr_output_cursor_move_any_expired(struct wlr_output *output, struct timespec *now) { + struct wlr_output_cursor *cursor; + wl_list_for_each(cursor, &output->cursors, link) { + wlr_output_cursor_move_expired(cursor, now); + } +} + +void wlr_output_cursor_move_all_deferred(struct wlr_output *output, struct timespec *now) { + struct wlr_output_cursor *cursor; + wl_list_for_each(cursor, &output->cursors, link) { + if (cursor->deferred) + output_cursor_move(cursor, cursor->deferred_x, cursor->deferred_y, now); + else { + // Should be on wlr_output? + cursor->last_presentation = *now; + }; + } +} + struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) { struct wlr_output_cursor *cursor = calloc(1, sizeof(*cursor)); if (cursor == NULL) { diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index c651565ee..8960c64a3 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -670,6 +670,11 @@ void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface, cursor_update_outputs(cur); } +void wlr_cursor_set_max_latency(struct wlr_cursor *cur, int max_latency) { + cur->max_latency = max_latency; + wlr_log(WLR_DEBUG, "setting max_latency %i", max_latency); +} + static void handle_pointer_motion(struct wl_listener *listener, void *data) { struct wlr_pointer_motion_event *event = data; struct wlr_cursor_device *device = diff --git a/util/time.c b/util/time.c index 78faac560..6a5fa0102 100644 --- a/util/time.c +++ b/util/time.c @@ -4,8 +4,6 @@ #include "util/time.h" -static const long NSEC_PER_SEC = 1000000000; - int64_t timespec_to_msec(const struct timespec *a) { return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; } @@ -34,3 +32,7 @@ void timespec_sub(struct timespec *r, const struct timespec *a, r->tv_nsec += NSEC_PER_SEC; } } + +int32_t mhz_to_nsec(int32_t mhz) { + return 1000000000000LL / mhz; +}