summaryrefslogtreecommitdiff
path: root/src/common/backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/backend.c')
-rw-r--r--src/common/backend.c136
1 files changed, 98 insertions, 38 deletions
diff --git a/src/common/backend.c b/src/common/backend.c
index ad9e218..9910e08 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -22,8 +22,38 @@ static const struct yasl_private_backend_ops *backend_ops(enum yasl_private_back
}
}
+/* The display drives the loop it runs on; see struct yasl_private_loop_driver. */
+static bool driver_prepare(void *ud)
+{
+ struct yasl_private_display *d = ud;
+ bool ready = d->ops->display_prepare(d);
+ /* Readiness the backends cannot see: events already queued, an error to
+ * report, or an owed REFRESH. */
+ return ready || yasl_private_events_pending(d) || yasl_private_events_error(d) ||
+ (d->pending && d->pending(d));
+}
+
+static int driver_wait(void *ud, int timeout_ms)
+{
+ struct yasl_private_display *d = ud;
+ return d->ops->display_wait(d, timeout_ms);
+}
+
+static void driver_check(void *ud)
+{
+ struct yasl_private_display *d = ud;
+ d->ops->display_check(d);
+}
+
+static void driver_deliver(void *ud)
+{
+ struct yasl_private_display *d = ud;
+ if (d->deliver)
+ d->deliver(d);
+}
+
static struct yasl_private_display *open_backend(enum yasl_private_backend backend,
- struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec)
+ struct yasl_loop *loop, const struct yasl_private_display_spec *spec)
{
const struct yasl_private_backend_ops *ops = backend_ops(backend);
if (!ops) {
@@ -33,18 +63,26 @@ static struct yasl_private_display *open_backend(enum yasl_private_backend backe
errno = 0;
struct yasl_private_display *d = ops->display_open(loop, spec);
if (!d && !errno) errno = ENODEV;
- if (d) {
- if (!yasl_private_events_open(d)) {
- int error = errno;
- ops->display_close(d);
- errno = error;
- return NULL;
- }
+ if (!d)
+ return NULL;
+ struct yasl_private_loop_driver driver = {
+ .prepare = driver_prepare,
+ .wait = ops->display_wait ? driver_wait : NULL,
+ .check = driver_check,
+ .deliver = driver_deliver,
+ .ud = d,
+ };
+ if (!yasl_private_events_open(d) || !yasl_private_loop_set_driver(d->loop, &driver)) {
+ int error = errno;
+ yasl_private_events_close(d);
+ ops->display_close(d);
+ errno = error;
+ return NULL;
}
return d;
}
-struct yasl_private_display *yasl_private_display_open(struct yasl_private_loop *loop,
+struct yasl_private_display *yasl_private_display_open(struct yasl_loop *loop,
const struct yasl_private_display_spec *spec)
{
enum yasl_private_backend backend = spec ? spec->backend : YASL_PRIVATE_BACKEND_AUTO;
@@ -78,6 +116,7 @@ void yasl_private_display_close(struct yasl_private_display *d)
{
if (!d || !d->ops->display_close)
return;
+ yasl_private_loop_set_driver(d->loop, NULL);
yasl_private_events_close(d);
d->ops->display_close(d);
}
@@ -89,27 +128,6 @@ uint32_t yasl_private_display_caps(const struct yasl_private_display *d)
return d->ops->display_caps(d);
}
-int yasl_private_display_dispatch(struct yasl_private_display *d, int timeout_ms)
-{
- if (!d) {
- errno = EINVAL;
- return -1;
- }
- if (!d->ops->display_dispatch_timeout) {
- errno = ENOTSUP;
- return -1;
- }
- int queue_error = yasl_private_events_error(d);
- if (queue_error) { errno = queue_error; return -1; }
- if (yasl_private_events_pending(d)) timeout_ms = 0;
- errno = 0;
- int result = d->ops->display_dispatch_timeout(d, timeout_ms);
- queue_error = yasl_private_events_error(d);
- if (queue_error) { errno = queue_error; return -1; }
- if (result < 0 && !errno) errno = EIO;
- return result;
-}
-
void yasl_private_display_flush(struct yasl_private_display *d)
{
if (!d || !d->ops->display_flush)
@@ -317,7 +335,10 @@ bool yasl_private_surface_present(struct yasl_surface *s, const struct yasl_priv
};
}
}
- return s->display->ops->surface_present(s, fb, &clipped);
+ if (!s->display->ops->surface_present(s, fb, &clipped))
+ return false;
+ atomic_store(&s->presented_ns, yasl_private_loop_now_ns());
+ return true;
}
bool yasl_private_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
@@ -473,10 +494,10 @@ bool yasl_private_surface_place_under_pointer(struct yasl_surface *s, struct yas
void yasl_private_surface_clear_timers(struct yasl_surface *s)
{
if (!s || !s->timers) return;
- struct yasl_private_loop *loop = s->display ? s->display->loop : NULL;
+ struct yasl_loop *loop = s->display ? s->display->loop : NULL;
for (int i = 0; i < s->timer_count; i++) {
if (loop && s->timers[i]) {
- yasl_private_loop_del_timer(loop, s->timers[i]);
+ yasl_loop_del_timer(loop, s->timers[i]);
}
}
free(s->timers);
@@ -491,7 +512,7 @@ yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uin
if (!s || !s->display || !s->display->loop || !cb || interval_ns == 0)
return 0;
- yasl_private_timer_id id = yasl_private_loop_add_timer(s->display->loop, interval_ns, cb, ud);
+ yasl_private_timer_id id = yasl_loop_add_timer(s->display->loop, interval_ns, cb, ud);
if (!id) return 0;
/* A one-shot timer retires itself in the loop. Drop retired ids before
@@ -507,7 +528,7 @@ yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uin
int new_cap = s->timer_capacity ? s->timer_capacity * 2 : 4;
yasl_private_timer_id *nt = realloc(s->timers, (size_t)new_cap * sizeof(*nt));
if (!nt) {
- yasl_private_loop_del_timer(s->display->loop, id);
+ yasl_loop_del_timer(s->display->loop, id);
return 0;
}
s->timers = nt;
@@ -523,13 +544,13 @@ bool yasl_private_surface_del_timer(struct yasl_surface *s, yasl_private_timer_i
for (int i = 0; i < s->timer_count; i++) {
if (s->timers[i] == id) {
s->timers[i] = s->timers[--s->timer_count];
- return s->display && yasl_private_loop_del_timer(s->display->loop, id);
+ return s->display && yasl_loop_del_timer(s->display->loop, id);
}
}
return false;
}
-struct yasl_private_loop *yasl_private_display_loop(struct yasl_private_display *d)
+struct yasl_loop *yasl_private_display_loop(struct yasl_private_display *d)
{
return d ? d->loop : NULL;
}
@@ -560,7 +581,46 @@ void yasl_private_surface_request_frame(struct yasl_surface *s)
{
if (!s) return;
yasl_private_surface_schedule_redraw(s);
- if (s->display->loop) yasl_private_loop_kick(s->display->loop);
+ yasl_private_loop_wake(s->display->loop);
+}
+
+void yasl_private_surface_notify_frame_shown(struct yasl_surface *s)
+{
+ if (!s || !s->frame_requested)
+ return;
+ s->frame_requested = false;
+ s->refresh_owed = true;
+}
+
+static uint64_t frame_interval_done(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
+{
+ (void)id; (void)interval_ns;
+ yasl_private_surface_notify_frame_shown(ud);
+ return 0;
+}
+
+bool yasl_private_surface_request_shown_frame(struct yasl_surface *s)
+{
+ s->frame_requested = true;
+ const struct yasl_private_backend_ops *ops = s->display->ops;
+ if (ops->surface_frame_in_flight) {
+ if (!ops->surface_frame_in_flight(s))
+ yasl_private_surface_notify_frame_shown(s);
+ return true;
+ }
+ /* No frame signal: one frame per refresh interval since the last present. */
+ if (yasl_private_loop_timer_armed(s->display->loop, s->frame_timer))
+ return true;
+ double refresh = yasl_private_surface_timing(s).refresh_ns;
+ uint64_t interval = refresh >= 1000000.0 && refresh <= 1000000000.0 ? (uint64_t)refresh : 16666667;
+ uint64_t presented = atomic_load(&s->presented_ns);
+ uint64_t since = yasl_private_loop_now_ns() - presented;
+ if (!presented || since >= interval) {
+ yasl_private_surface_notify_frame_shown(s);
+ return true;
+ }
+ s->frame_timer = yasl_private_surface_add_timer(s, interval - since, frame_interval_done, s);
+ return s->frame_timer != 0;
}
static uint64_t pacing_tick(void *ud, yasl_private_timer_id id, uint64_t interval)