diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-15 20:38:14 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-15 20:38:14 +0300 |
| commit | c2832a63547e6328afde25bb3b8145267eadef47 (patch) | |
| tree | bf03ace79f01df10ddaffd70bea74678ab4ca219 /include | |
| parent | 3d012d707d0242780cd386868ab511846dc9b0af (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 'include')
| -rw-r--r-- | include/yasl/yasl.h | 88 |
1 files changed, 75 insertions, 13 deletions
diff --git a/include/yasl/yasl.h b/include/yasl/yasl.h index 15bbe16..dab1091 100644 --- a/include/yasl/yasl.h +++ b/include/yasl/yasl.h @@ -27,6 +27,7 @@ extern "C" { #endif typedef struct yasl_surface yasl_surface; typedef struct yasl_event yasl_event; +typedef struct yasl_loop yasl_loop; /* Lifecycle */ enum yasl_backend { YASL_BACKEND_AUTO, YASL_BACKEND_WAYLAND, YASL_BACKEND_X11, YASL_BACKEND_COCOA }; @@ -36,11 +37,25 @@ enum yasl_feature { YASL_FEATURE_TEXT_INPUT = 64, YASL_FEATURE_LOCKED_CURSOR = 128, YASL_FEATURE_OPENGL = 256, YASL_FEATURE_VULKAN = 512 }; -/** Optional: the first surface initializes with AUTO and no app ID. app_id is a - * reverse-DNS name for window grouping and notifications. EALREADY once - * initialized; a failed initialization may be retried. */ -YASL_API int yasl_init(enum yasl_backend backend, const char *app_id); -/** Destroys every surface. EBUSY while a Vulkan surface exists. */ +/** Zero is the default: AUTO, no app ID, and yasl_wait_event delivers events. + * app_id is a reverse-DNS name for window grouping and notifications. Set loop + * and on_event together to run the display on an application loop: each + * yasl_loop_step then delivers events to on_event, and yasl_wait_event fails + * with EBUSY. */ +struct yasl_options { + enum yasl_backend backend; + const char *app_id; + yasl_loop *loop; + void (*on_event)(void *userdata, const yasl_event *event); + void *userdata; +}; +/** Optional: the first surface initializes with defaults; NULL options too. + * EINVAL when only one of loop and on_event is set; EPERM off the loop's + * thread. EALREADY once initialized with the same options, EBUSY with others; + * a failed initialization may be retried. */ +YASL_API int yasl_init(const struct yasl_options *options); +/** Destroys every surface; on_event is not called again. EBUSY inside a loop + * step or while a Vulkan surface exists. */ YASL_API int yasl_shutdown(void); /** AUTO and 0 before initialization. */ YASL_API enum yasl_backend yasl_get_backend(void); @@ -99,6 +114,10 @@ YASL_API int yasl_surface_set_title(yasl_surface *surface, const char *title); enum yasl_request { YASL_REQUEST_RESTORE, YASL_REQUEST_FULLSCREEN, YASL_REQUEST_MAXIMIZE, YASL_REQUEST_MINIMIZE }; YASL_API int yasl_surface_request(yasl_surface *surface, enum yasl_request request); YASL_API int yasl_surface_request_size(yasl_surface *surface, int width, int height); +/** Asks for a REFRESH when the window can show a new frame: at once when none + * is in flight, otherwise once the system shows the current one. Requests + * coalesce. Use it to pace animation; draw at once for input. */ +YASL_API int yasl_surface_request_frame(yasl_surface *surface); enum yasl_cursor { YASL_CURSOR_ARROW, YASL_CURSOR_TEXT, YASL_CURSOR_LINK, YASL_CURSOR_CROSSHAIR, YASL_CURSOR_MOVE, @@ -196,10 +215,11 @@ enum yasl_event_type { YASL_EVENT_DROP, /**< External data: x, y; action is a yasl_drop_action; * text on RELEASE is a text/uri-list. */ YASL_EVENT_DRAG, /**< A yasl_drag_start drag: x, y; action is a yasl_drop_action. */ - /** The display server went away. Before the next wait, destroy Vulkan - * surfaces and detach GL contexts on every thread; popups close. Wayland - * reconnects and sends DISPLAY_RESTORED: recreate them (GL contexts are new). - * Elsewhere loss is final and waits fail with ENOTCONN. */ + /** The display server went away; action is a yasl_lost. Before the next + * wait, or before returning to the loop, destroy Vulkan surfaces and detach + * GL contexts on every thread; popups close. On RECONNECTING, DISPLAY_RESTORED + * follows: recreate them (GL contexts are new). After FINAL, yasl_wait_event + * fails with ENOTCONN once no event is left. */ YASL_EVENT_DISPLAY_LOST, YASL_EVENT_DISPLAY_RESTORED }; @@ -218,6 +238,7 @@ enum yasl_scroll_source { YASL_SCROLL_UNKNOWN, YASL_SCROLL_WHEEL, YASL_SCROLL_FI enum yasl_scroll_phase { YASL_SCROLL_PHASE_NONE, YASL_SCROLL_BEGIN, YASL_SCROLL_UPDATE, YASL_SCROLL_END }; enum yasl_transfer { YASL_TRANSFER_OK, YASL_TRANSFER_UNAVAILABLE, YASL_TRANSFER_FAILED }; enum yasl_drop_action { YASL_DROP_OVER, YASL_DROP_LEAVE, YASL_DROP_RELEASE }; +enum yasl_lost { YASL_LOST_RECONNECTING, YASL_LOST_FINAL }; /** Physical key, layout labels (codepoint unshifted, shifted; 0 if none) and the * native code (evdev, X11 keycode or macOS virtual key). Type with TEXT, not this. */ @@ -256,7 +277,8 @@ struct yasl_event { uint64_t time_ns; unsigned action, mods; /**< mods: YASL_MOD_* for KEY, POINTER and SCROLL. */ double x, y; - const char *text; /**< NUL-terminated UTF-8 until the next wait, or NULL. */ + const char *text; /**< NUL-terminated UTF-8 until the next wait or until + * on_event returns, or NULL. */ size_t length; union { struct yasl_surface_info window; @@ -268,18 +290,58 @@ struct yasl_event { }; }; /** 1 with an event, 0 once timeout_ms passes or after yasl_wake. 0 polls and -1 - * waits forever. On failure out->type is NONE. */ + * waits forever. On failure out->type is NONE. EBUSY with on_event. */ YASL_API int yasl_wait_event(yasl_event *out, int timeout_ms); /** Makes a waiting, or the next, yasl_wait_event return 0 once no event is - * pending. Thread-safe. */ + * pending. With on_event, wakes the loop. Thread-safe. */ YASL_API void yasl_wake(void); /** Monotonic nanoseconds, the clock of event times. Thread-safe. */ YASL_API uint64_t yasl_time_ns(void); +/* Loop: one wait for the whole application. Give it to yasl_init to run the + * display on it; it also runs descriptors, timers and signals without one. + * Call it and get its callbacks on the thread that created it. */ +/** NULL with errno on failure. */ +YASL_API yasl_loop *yasl_loop_create(void); +/** EBUSY while a step runs or the display runs on it. Leaves descriptors open. */ +YASL_API int yasl_loop_destroy(yasl_loop *loop); +/** Waits at most timeout_ms (0 polls, -1 waits) and runs what is ready: + * display events, then descriptors, then due timers. Returns how many + * descriptor and timer callbacks ran. EBUSY inside a step; EPERM off the + * loop's thread. */ +YASL_API int yasl_loop_step(yasl_loop *loop, int timeout_ms); +/** Steps until yasl_loop_quit. A quit made before the run ends it at once. */ +YASL_API int yasl_loop_run(yasl_loop *loop); +YASL_API void yasl_loop_quit(yasl_loop *loop); +/** Calls callback with the poll(2) revents, errors and hangup included; it + * must consume them or change the watch. Returns an ID above zero. */ +YASL_API int yasl_loop_add_fd(yasl_loop *loop, int fd, short events, + void (*callback)(void *userdata, int fd, short revents), + void *userdata); +/** ENOENT for an unknown ID. Leaves the descriptor open. */ +YASL_API int yasl_loop_del_fd(yasl_loop *loop, int id); +YASL_API int yasl_loop_set_fd_events(yasl_loop *loop, int id, short events); +/** Calls callback after delay_ns, 0 meaning the next step. It returns the next + * delay, or 0 to stop. Returns an ID that is never reused, or 0 with errno. */ +YASL_API uint64_t yasl_loop_add_timer(yasl_loop *loop, uint64_t delay_ns, + uint64_t (*callback)(void *userdata, uint64_t id, + uint64_t delay_ns), + void *userdata); +/** True when it cancels an armed timer, also from its own callback. */ +YASL_API bool yasl_loop_del_timer(yasl_loop *loop, uint64_t id); +/** Calls callback on the loop's thread for each signal; repeats before the + * call coalesce. EBUSY when this loop already watches signals or another loop + * watches one of them. Unwatching restores the previous actions. */ +YASL_API int yasl_loop_watch_signals(yasl_loop *loop, const int *signals, int count, + void (*callback)(void *userdata, int signo), + void *userdata); +YASL_API void yasl_loop_unwatch_signals(yasl_loop *loop); + /* Rendering */ /** CPU surfaces: the window's pixel_width * pixel_height premultiplied * 0xAARRGGBB pixels, rows tightly packed. They persist, and a resize keeps the - * overlap and zeroes the rest. Valid until the next wait. Present to show them; + * overlap and zeroes the rest. Valid until the next wait, the return of the + * callback that got them, or the next yasl_cpu_ call. Present to show them; * the system may also show them when it redraws the window. */ struct yasl_damage { int x, y, width, height; |
