summaryrefslogtreecommitdiff
path: root/src/platform/wayland/surface.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-15 20:38:14 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-15 20:38:14 +0300
commitc2832a63547e6328afde25bb3b8145267eadef47 (patch)
treebf03ace79f01df10ddaffd70bea74678ab4ca219 /src/platform/wayland/surface.c
parent3d012d707d0242780cd386868ab511846dc9b0af (diff)
Make the loop public and let the display drive it
An application that has its own descriptors and timers needed a second loop next to yasl's, and could not see yasl's key-repeat timer, clipboard pipes or reconnect timer. Now there is one wait: the application owns a yasl_loop, and yasl_init can run the display on it. - Public yasl_loop_*: descriptors, timers and signals. Descriptor callbacks return void. Timer IDs are 64 bits and never reused; a zero delay runs in the next step. A nested step or run fails with EBUSY, a step off the loop's thread with EPERM, and a quit made before a run ends it at once. An interrupted wait keeps its deadline. Creating a loop fails when it cannot make its wake pipe. - The display drives the loop through prepare, wait and check hooks instead of wrapping its step. Wayland finishes its read before any callback runs, so loop callbacks may create surfaces and do roundtrips. Readiness (queued events, an owed REFRESH) is derived in the step, so the wait never blocks over pending work. A draw that cannot proceed for want of a buffer is no longer counted as work. - yasl_init takes struct yasl_options. With loop and on_event, each step delivers events to on_event and yasl_wait_event fails with EBUSY. EALREADY only for the same options, EBUSY for others. - yasl_surface_request_frame: a REFRESH once the window can show a new frame, for pacing animation. - DISPLAY_LOST reports whether the display reconnects. Pull waits still fail with ENOTCONN after a final loss. - Events pushed from another thread wake the loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/platform/wayland/surface.c')
-rw-r--r--src/platform/wayland/surface.c147
1 files changed, 57 insertions, 90 deletions
diff --git a/src/platform/wayland/surface.c b/src/platform/wayland/surface.c
index cc0c715..bc37c10 100644
--- a/src/platform/wayland/surface.c
+++ b/src/platform/wayland/surface.c
@@ -49,7 +49,7 @@ static void display_lose(struct wayland_display *c)
report_display_error(c);
display_cancel_read(c);
if (c->loop && c->watch > 0)
- yasl_private_loop_del_fd(c->loop, c->watch);
+ yasl_loop_del_fd(c->loop, c->watch);
c->watch = 0;
c->depth++;
conn_drag_end(c);
@@ -62,7 +62,7 @@ static void display_lose(struct wayland_display *c)
yasl_private_display_notify_event(&c->base, YASL_PRIVATE_EVENT_DISPLAY_LOST);
c->reconnect_delay_ns = YASL_PRIVATE_MS_TO_NS(100);
if (c->loop && !c->reconnect_timer)
- c->reconnect_timer = yasl_private_loop_add_timer(c->loop, c->reconnect_delay_ns, reconnect_tick, c);
+ c->reconnect_timer = yasl_loop_add_timer(c->loop, c->reconnect_delay_ns, reconnect_tick, c);
}
void surface_fail(struct wayland_surface *w)
@@ -73,43 +73,18 @@ void surface_fail(struct wayland_surface *w)
yasl_private_surface_notify_close(&w->base);
}
-static int display_ready(int fd, short revents, void *ud)
+/* The display fd is read in wayland_display_check, before any callback. */
+static void display_ready(void *ud, int fd, short revents)
{
- (void)fd;
- struct wayland_display *c = ud;
- if (c->lost)
- return 0;
- c->depth++;
- if (c->reading) {
- c->reading = false;
- if (!(revents & POLLIN))
- wl_display_cancel_read(c->wl);
- else if (wl_display_read_events(c->wl) < 0)
- revents |= POLLERR;
- }
- if (revents & POLLOUT) {
- if (wl_display_flush(c->wl) >= 0) {
- if (c->loop && c->watch > 0)
- yasl_private_loop_set_fd_events(c->loop, c->watch, POLLIN);
- } else if (errno != EAGAIN) {
- display_lose(c);
- }
- }
- if (wl_display_dispatch_pending(c->wl) < 0 ||
- (revents & (POLLERR | POLLHUP | POLLNVAL)))
- display_lose(c);
- display_leave(c);
- return 0;
+ (void)ud; (void)fd; (void)revents;
}
-static bool display_arm(struct wayland_display *c)
+static void display_arm(struct wayland_display *c)
{
- bool had_pending = false;
while (!c->reading && wl_display_prepare_read(c->wl) != 0) {
- had_pending = true;
if (wl_display_dispatch_pending(c->wl) < 0) {
display_lose(c);
- return false;
+ return;
}
}
c->reading = true;
@@ -118,74 +93,65 @@ static bool display_arm(struct wayland_display *c)
if (errno != EAGAIN) {
display_cancel_read(c);
display_lose(c);
- return false;
+ return;
}
events |= POLLOUT;
}
- if (c->loop && c->watch > 0)
- yasl_private_loop_set_fd_events(c->loop, c->watch, events);
- return had_pending;
+ yasl_loop_set_fd_events(c->loop, c->watch, events);
}
-static inline bool display_has_immediate_work(const struct wayland_display *c)
+/* A draw that cannot proceed now, for want of a buffer or a configure, is not
+ * work: the release or configure that unblocks it schedules it again. */
+static bool display_has_immediate_work(const struct wayland_display *c)
{
- if (unlikely(!c || c->lost)) return false;
- for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) {
- bool ready_draw = w->want_draw & (w->frame_cb == NULL) & w->configured;
- bool has_input = (w->input.count > 0 || w->input.reset_pending);
- if (unlikely((ready_draw | has_input) & !w->closed))
+ for (const struct wayland_surface *w = c->surfaces; w; w = w->conn_next)
+ if (surface_draw_ready(w))
return true;
- }
return false;
}
-int wayland_display_dispatch_timeout(struct wayland_display *c, int timeout_ms)
+/* Before the wait: dispatch what libwayland already holds, draw due surfaces,
+ * and arm the read. While lost, only the reconnect timer runs. */
+bool wayland_display_prepare(struct wayland_display *c)
{
- if (!c)
- return -1;
-
+ if (c->lost)
+ return false;
c->depth++;
- if (c->lost) {
- /* Reconnect attempts run as a loop timer. */
- int n = c->loop ? yasl_private_loop_step(c->loop, timeout_ms) : 0;
- display_leave(c);
- return n;
- }
-
/* Losing the connection is reported as DISPLAY_LOST, not as a failed wait. */
- if (wl_display_dispatch_pending(c->wl) < 0) {
+ if (wl_display_dispatch_pending(c->wl) < 0)
display_lose(c);
- display_leave(c);
- return 0;
- }
-
- display_arm(c);
- if (c->lost) {
- display_leave(c);
- return 0;
- }
-
- if (timeout_ms != 0 && display_has_immediate_work(c)) {
- timeout_ms = 0;
- }
-
- int n = 0;
- if (c->loop) {
- n = yasl_private_loop_step(c->loop, timeout_ms);
- }
+ if (!c->lost)
+ wayland_display_flush(c);
+ if (!c->lost)
+ display_arm(c);
+ bool ready = !c->lost && display_has_immediate_work(c);
+ display_leave(c);
+ return ready;
+}
- if (c->reading) {
- display_cancel_read(c);
+/* After the wait, before any callback: finish the read prepare armed, so
+ * callbacks may do roundtrips, and dispatch what it brought. */
+void wayland_display_check(struct wayland_display *c)
+{
+ if (!c->reading)
+ return;
+ c->depth++;
+ short revents = yasl_private_loop_revents(c->loop, c->watch);
+ c->reading = false;
+ if (!(revents & POLLIN))
+ wl_display_cancel_read(c->wl);
+ else if (wl_display_read_events(c->wl) < 0)
+ revents |= POLLERR;
+ if (revents & POLLOUT) {
+ if (wl_display_flush(c->wl) >= 0)
+ yasl_loop_set_fd_events(c->loop, c->watch, POLLIN);
+ else if (errno != EAGAIN)
+ display_lose(c);
}
-
- if (c->wl && wl_display_dispatch_pending(c->wl) < 0) {
+ if (!c->lost && (wl_display_dispatch_pending(c->wl) < 0 ||
+ (revents & (POLLERR | POLLHUP | POLLNVAL))))
display_lose(c);
- display_leave(c);
- return 0;
- }
-
display_leave(c);
- return n;
}
void wayland_display_flush(struct wayland_display *c)
@@ -212,7 +178,7 @@ static void connection_release(struct wayland_display *c)
for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next)
surface_release_native(w);
conn_drag_end(c);
- if (c->loop && c->watch > 0) yasl_private_loop_del_fd(c->loop, c->watch);
+ if (c->loop && c->watch > 0) yasl_loop_del_fd(c->loop, c->watch);
c->watch = 0;
if (c->wl) display_cancel_read(c);
while (c->seats)
@@ -271,7 +237,7 @@ static bool connection_open(struct wayland_display *c)
return false;
surface_init_cursor(c);
if (c->loop) {
- c->watch = yasl_private_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c);
+ c->watch = yasl_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c);
if (c->watch <= 0) { c->watch = 0; return false; }
}
return display_input_init(c) == 0;
@@ -329,10 +295,10 @@ static uint64_t reconnect_tick(void *ud, yasl_private_timer_id id, uint64_t inte
static void display_free(struct wayland_display *c)
{
connection_release(c);
- if (c->loop && c->reconnect_timer) yasl_private_loop_del_timer(c->loop, c->reconnect_timer);
+ if (c->loop && c->reconnect_timer) yasl_loop_del_timer(c->loop, c->reconnect_timer);
if (c->xkb_ctx) xkb_context_unref(c->xkb_ctx);
if (c->owns_loop && c->loop) {
- yasl_private_loop_destroy(c->loop);
+ yasl_loop_destroy(c->loop);
c->loop = NULL;
c->base.loop = NULL;
}
@@ -340,14 +306,15 @@ static void display_free(struct wayland_display *c)
free(c);
}
-struct wayland_display *wayland_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec)
+struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struct yasl_private_display_spec *spec)
{
struct wayland_display *c = calloc(1, sizeof(*c));
if (!c) return NULL;
c->owner = pthread_self();
c->base.ops = &yasl_private_wayland_backend;
+ c->base.reconnects = true;
if (!loop) {
- c->loop = yasl_private_loop_create();
+ c->loop = yasl_loop_create();
c->owns_loop = true;
} else {
c->loop = loop;
@@ -605,8 +572,8 @@ void wayland_surface_schedule_redraw(struct wayland_surface *w)
return;
}
w->want_draw = true;
- if (w->conn && w->conn->loop)
- yasl_private_loop_kick(w->conn->loop);
+ if (w->conn)
+ yasl_private_loop_wake(w->conn->loop);
}
struct yasl_private_geometry wayland_surface_geometry(const struct wayland_surface *w)