summaryrefslogtreecommitdiff
path: root/src/common/surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/surface.c')
-rw-r--r--src/common/surface.c122
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;
}