diff options
Diffstat (limited to 'src/common/surface.c')
| -rw-r--r-- | src/common/surface.c | 122 |
1 files changed, 97 insertions, 25 deletions
diff --git a/src/common/surface.c b/src/common/surface.c index aeca3d8..5c29a10 100644 --- a/src/common/surface.c +++ b/src/common/surface.c @@ -13,9 +13,12 @@ static struct yasl_private_display *display; static pthread_t owner; static pthread_mutex_t lifecycle_mutex = PTHREAD_MUTEX_INITIALIZER; static atomic_bool wake_requested; -static bool pumping; static unsigned refresh_depth; static char *delivered_text; /* Text of the last returned event; freed by the next wait. */ +/* The options of the open display; on_event set means callback mode. */ +static struct yasl_options active; +static char *active_app_id; +static bool queue_failure_reported; bool yasl_public_fail(int error) { @@ -62,20 +65,74 @@ bool yasl_public_connected(yasl_surface *s) /* Runs a private call that reports failure through errno. */ #define STATUS(call) (errno = 0, yasl_public_status((call) || yasl_public_fail(errno))) -int yasl_init(enum yasl_backend backend, const char *app_id) +static bool refresh_ready(yasl_surface *s); +static int take_event(yasl_event *out); +/* An owed REFRESH is an event the queue does not hold. */ +static bool public_pending(struct yasl_private_display *d) +{ + for (yasl_surface *s = d->surfaces; s; s = s->event_next) + if (s->refresh_owed && refresh_ready(s)) + return true; + return false; +} +/* Callback mode: runs in each loop step, after the display is read and before + * descriptor callbacks. Bounded, so a handler that keeps making events cannot + * starve the loop's other sources; the rest go in the next step, which does + * not wait while they are pending. */ +static void deliver_events(struct yasl_private_display *d) { - if (display) - return yasl_public_owner() ? yasl_public_error(EALREADY) : -errno; - if ((unsigned)backend > YASL_BACKEND_COCOA) + if (yasl_private_events_error(d) && !queue_failure_reported) { + queue_failure_reported = true; + active.on_event(active.userdata, &(yasl_event){ + .type = YASL_EVENT_DISPLAY_LOST, .action = YASL_LOST_FINAL, .time_ns = yasl_time_ns()}); + } + for (int i = 0; i < 256; i++) { + yasl_event event; + if (take_event(&event) <= 0) + break; + active.on_event(active.userdata, &event); + free(delivered_text); + delivered_text = NULL; + } +} +static bool same_options(const struct yasl_options *o) +{ + const char *app_id = o->app_id ? o->app_id : ""; + return o->backend == active.backend && o->loop == active.loop && + o->on_event == active.on_event && o->userdata == active.userdata && + !strcmp(app_id, active_app_id ? active_app_id : ""); +} +int yasl_init(const struct yasl_options *options) +{ + struct yasl_options o = options ? *options : (struct yasl_options){0}; + if (display) { + if (!yasl_public_owner()) + return -errno; + return yasl_public_error(same_options(&o) ? EALREADY : EBUSY); + } + if ((unsigned)o.backend > YASL_BACKEND_COCOA || !o.loop != !o.on_event) return yasl_public_error(EINVAL); + if (o.loop && !yasl_private_loop_owned(o.loop)) + return yasl_public_error(EPERM); + char *app_id = o.app_id ? strdup(o.app_id) : NULL; + if (o.app_id && !app_id) + return yasl_public_error(ENOMEM); struct yasl_private_display_spec spec = { - .app_id = app_id, - .backend = (enum yasl_private_backend)backend, + .app_id = o.app_id, + .backend = (enum yasl_private_backend)o.backend, }; errno = 0; - struct yasl_private_display *d = yasl_private_display_open(NULL, &spec); - if (!d) + struct yasl_private_display *d = yasl_private_display_open(o.loop, &spec); + if (!d) { + free(app_id); return yasl_public_error(errno); + } + d->pending = public_pending; + d->deliver = o.on_event ? deliver_events : NULL; + active = o; + active.app_id = NULL; + active_app_id = app_id; + queue_failure_reported = false; pthread_mutex_lock(&lifecycle_mutex); owner = pthread_self(); display = d; @@ -84,7 +141,7 @@ int yasl_init(enum yasl_backend backend, const char *app_id) } bool yasl_public_initialize(void) { - return display ? yasl_public_owner() : yasl_init(YASL_BACKEND_AUTO, NULL) == 0; + return display ? yasl_public_owner() : yasl_init(NULL) == 0; } static void close_display(void) { @@ -98,7 +155,7 @@ int yasl_shutdown(void) { if (display && !yasl_public_owner()) return -errno; - if (pumping || refresh_depth) + if (refresh_depth || (display && yasl_private_loop_stepping(display->loop))) return yasl_public_error(EBUSY); if (display) { for (yasl_surface *s = display->surfaces; s; s = s->event_next) @@ -110,6 +167,9 @@ int yasl_shutdown(void) } free(delivered_text); delivered_text = NULL; + free(active_app_id); + active_app_id = NULL; + active = (struct yasl_options){0}; atomic_store(&wake_requested, false); #if YASL_HAS_VULKAN extern void yasl_private_vulkan_reset(void); @@ -260,7 +320,7 @@ yasl_surface *yasl_surface_create(const struct yasl_surface_options *o) { if (!o || (unsigned)o->type > YASL_SURFACE_NATIVE || !size_valid(o->width, o->height)) return yasl_public_fail(EINVAL), NULL; - if (pumping || refresh_depth) + if (refresh_depth) return yasl_public_fail(EBUSY), NULL; unsigned flags = (o->type == YASL_SURFACE_CPU ? YASL_PRIVATE_SURFACE_CPU : 0u) | (o->undecorated ? YASL_PRIVATE_SURFACE_UNDECORATED : 0u) | @@ -387,6 +447,12 @@ int yasl_surface_request_size(yasl_surface *s, int w, int h) return yasl_public_error(EINVAL); return STATUS(yasl_private_surface_set_size(s, (struct yasl_private_vector2i){w, h})); } +int yasl_surface_request_frame(yasl_surface *s) +{ + if (!yasl_public_connected(s)) + return -errno; + return STATUS(yasl_private_surface_request_shown_frame(s)); +} int yasl_surface_set_cursor(yasl_surface *s, enum yasl_cursor cursor) { if (!yasl_public_surface(s, false)) @@ -409,7 +475,8 @@ void yasl_private_surface_notify_refresh(yasl_surface *s) { if (!s || !s->display) return; - if (s->refresh && s->display->modal && pumping && !refresh_depth) { + /* modal holds only inside the system's own loop, within the wait. */ + if (s->refresh && s->display->modal && !refresh_depth) { ++refresh_depth; s->refresh(s); --refresh_depth; @@ -703,6 +770,7 @@ static bool convert_event(const struct yasl_private_event *p, yasl_event *e) return true; case YASL_PRIVATE_EVENT_DISPLAY_LOST: e->type = YASL_EVENT_DISPLAY_LOST; + e->action = display->reconnects ? YASL_LOST_RECONNECTING : YASL_LOST_FINAL; return true; case YASL_PRIVATE_EVENT_DISPLAY_RESTORED: e->type = YASL_EVENT_DISPLAY_RESTORED; @@ -869,11 +937,14 @@ static int pump(yasl_event *out, int timeout) int n = take_event(out); if (n) return n; - yasl_private_display_flush(display); - if ((n = take_event(out))) - return n; + int error = yasl_private_events_error(display); + if (error) + return yasl_public_error(error); if (atomic_exchange(&wake_requested, false)) return 0; + /* A loss that does not reconnect ends the waits once its event is out. */ + if (atomic_load(&display->offline) && !display->reconnects) + return yasl_public_error(ENOTCONN); int wait = timeout; if (timeout > 0) { uint64_t now = yasl_time_ns(); @@ -881,11 +952,11 @@ static int pump(yasl_event *out, int timeout) return 0; wait = (int)((deadline - now + 999999u) / 1000000u); } - if (yasl_private_display_dispatch(display, wait) < 0) - return yasl_public_error(errno); - yasl_private_display_flush(display); - if ((n = take_event(out)) || timeout == 0) - return n; + int stepped = yasl_loop_step(display->loop, wait); + if (stepped < 0) + return yasl_public_error(-stepped); + if (timeout == 0) + return take_event(out); } } int yasl_wait_event(yasl_event *out, int timeout) @@ -895,13 +966,11 @@ int yasl_wait_event(yasl_event *out, int timeout) *out = (yasl_event){.type = YASL_EVENT_NONE}; if (!yasl_public_owner()) return -errno; - if (pumping || refresh_depth) + if (active.on_event || refresh_depth || yasl_private_loop_stepping(display->loop)) return yasl_public_error(EBUSY); free(delivered_text); delivered_text = NULL; - pumping = true; int n = pump(out, timeout); - pumping = false; if (n < 0) *out = (yasl_event){.type = YASL_EVENT_NONE}; return n; @@ -1014,5 +1083,8 @@ int yasl_gl_present(yasl_surface *s) return -errno; if (!display->ops->gl_swap_buffers) return yasl_public_error(ENOTSUP); - return STATUS(display->ops->gl_swap_buffers(display, s)); + int status = STATUS(display->ops->gl_swap_buffers(display, s)); + if (status == 0) + atomic_store(&s->presented_ns, yasl_private_loop_now_ns()); + return status; } |
