diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 23:41:31 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 23:41:31 +0300 |
| commit | 76073fe5fad8799e5850805f2f090af2cb62c741 (patch) | |
| tree | 9658019babe4760a3634bdab4da0731a10e42e68 /src/common/private_loop.h | |
| parent | dd1775a9927922c14f8679ccbc4d3ca8d5917e20 (diff) | |
Simplify the public event and CPU API
Applications now need far less ceremony: hello.c is a 20-line loop that
handles REFRESH, CLOSE and QUIT and draws into the surface's own pixels.
- Fold GEOMETRY, STATE and FOCUS into one WINDOW event carrying size,
scale and state; unchanged values send nothing.
- Guarantee REFRESH once a window has a size and after every resize,
never at size zero.
- Merge DROP_HOVER into DROP with over/leave/data actions and give drag
and drop actions enums.
- Keep event strings valid until the next wait and drop
yasl_event_release; empty or failed waits report NONE.
- Replace copy-in presentation with a lent pixel buffer:
yasl_cpu_pixels and yasl_cpu_present.
- Remove yasl_poll_event, yasl_cpu_read_pixels, unused event fields and
comments that restated names.
- Wayland: resize floating windows in request_size instead of reporting
it unsupported, and stop marking new GPU surfaces as Vulkan so
yasl_vulkan_create_surface no longer fails with NATIVE_WINDOW_IN_USE.
Also includes the in-progress public API refactor that was uncommitted
in the tree (removal of the app/loop layer, new surface, GL and Vulkan
entry points).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/common/private_loop.h')
| -rw-r--r-- | src/common/private_loop.h | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/common/private_loop.h b/src/common/private_loop.h new file mode 100644 index 0000000..432bbc3 --- /dev/null +++ b/src/common/private_loop.h @@ -0,0 +1,214 @@ +/** + * @file + * Optional event loop, file watches, and timers. + * + * Use the loop's owning thread for all calls and callbacks, except + * yasl_private_loop_now_ns(), yasl_private_loop_kick(), and yasl_private_loop_post(), which any thread + * may call. A display borrows a supplied loop. For window applications, + * drive yasl_private_display_dispatch(), drain input, then yasl_private_display_flush(); the + * standalone loop_run() function does not replace this cycle. Timer intervals are + * nanoseconds; timeout_ms arguments are milliseconds. Do not recursively step/run + * or destroy a loop from a callback. + */ +#ifndef YASL_PRIVATE_LOOP_H +#define YASL_PRIVATE_LOOP_H + +#include "common/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Optional event loop. Use the display's loop when adding application timers. + * A step waits at most timeout_ms: zero polls, a negative value waits indefinitely. + * Calls and callbacks use the loop's owning thread. */ +/** + * Create an owned standalone file-descriptor and timer loop. + * @return A loop to destroy with yasl_private_loop_destroy(), or NULL on allocation failure. + * Use on one owning thread. Passing it to yasl_private_display_open() only lends it to + * the display; the caller retains ownership. + */ +struct yasl_private_loop *yasl_private_loop_create(void); +/** + * Release a loop, its watches, its timers, and its signal watch. + * @param l Loop, or NULL for no action. + * Close displays borrowing this loop first. Must not run during a loop callback + * or an active step/run. Watched file descriptors are not closed. Posted + * functions that have not run are dropped; no thread may still post or kick. + */ +void yasl_private_loop_destroy(struct yasl_private_loop *l); +/** + * Step a standalone loop until yasl_private_loop_quit() is called. + * @param l Loop. + * @return Zero after quit, or -1 on failure. + * For a window application, use a dispatch, input-poll, flush cycle instead; + * loop_run alone does not perform that full cycle on every backend. + */ +int yasl_private_loop_run(struct yasl_private_loop *l); +/** + * Wait for file-descriptor readiness or a timer deadline and run callbacks. + * @param l Loop. + * @param timeout_ms Maximum wait: zero polls, negative waits indefinitely. + * @return Number of callbacks handled, or -1 on failure. + * Timer deadlines shorten the wait. Callbacks can add/delete sources, including + * themselves. Do not recursively step/run this loop. + */ +int yasl_private_loop_step(struct yasl_private_loop *l, int timeout_ms); +/** + * Ask the currently running yasl_private_loop_run() to return after this step. + * @param l Loop, or NULL for no action. + * Call on the owning thread, usually from a callback. + */ +void yasl_private_loop_quit(struct yasl_private_loop *l); +/** + * Wake a loop that is waiting, without running any callback. + * @param l Loop, or NULL for no action. + * May be called from any thread and from a signal handler. The loop must stay + * alive until the call returns. A waiting yasl_private_loop_step(), yasl_private_loop_run(), or + * yasl_private_display_dispatch() on this loop returns early; a loop that is not + * waiting returns early from its next wait instead. Kicks coalesce. This does + * not schedule a redraw or enqueue an event; use yasl_private_loop_post() to run code + * on the owning thread. + */ +void yasl_private_loop_kick(struct yasl_private_loop *l); +/** + * Run a function on the loop's owning thread. + * @param l Loop. + * @param fn Required function, called once with ud during a later step. + * @param ud Borrowed userdata until fn runs. + * @return true if queued; false for NULL arguments, a loop without a wake + * pipe, or allocation failure. + * May be called from any thread, but not from a signal handler. Wakes the loop + * like yasl_private_loop_kick(). Functions run in posting order, during a + * yasl_private_loop_step() or display dispatch on the owning thread. They may post + * again; a function posted while posts are running waits for a later step. + * Stop posting before yasl_private_loop_destroy(), which drops functions that have + * not run. + */ +bool yasl_private_loop_post(struct yasl_private_loop *l, void (*fn)(void *ud), void *ud); +/** + * Deliver POSIX signals to a callback on the loop's owning thread. + * @param l Loop. + * @param signals Signal numbers to watch; the array is read during the call. + * @param n Number of entries, at least one. + * @param cb Required callback, given ud and one signal number per delivery. + * @param ud Borrowed userdata until the watch is removed. + * @return true if every signal is watched; false with errno set and no + * dispositions changed. EINVAL means a NULL or invalid argument, including + * SIGKILL and SIGSTOP. EBUSY means another loop watches one of the signals. + * Replaces this loop's previous watch, if any. Installs one process-wide + * handler per signal with SA_RESTART and an empty handler mask, and saves the + * previous action. The handler only records the signal and wakes the loop, + * so it may run on any thread. YASL does not change any thread's signal mask: + * a signal blocked in every thread stays pending and is not delivered. Caught + * signals reset to their default action across exec, so spawned programs + * do not inherit the handler. Repeated deliveries of one signal before the + * callback runs coalesce into one callback. SIGCHLD reports stopped and + * continued children as well as exits. Callbacks run during yasl_private_loop_step() + * or display dispatch, and may remove the watch. + */ +bool yasl_private_loop_watch_signals(struct yasl_private_loop *l, const int *signals, int n, + void (*cb)(void *ud, int signo), void *ud); +/** + * Stop watching signals and restore their previous actions. + * @param l Loop, or NULL for no action. + * Does nothing if the loop watches no signals. Signals that arrived but have + * not reached the callback are dropped. yasl_private_loop_destroy() also unwatches. + */ +void yasl_private_loop_unwatch_signals(struct yasl_private_loop *l); +/** + * Read the monotonic clock used by YASL timers and timing results. + * @return Nanoseconds from CLOCK_MONOTONIC, unrelated to wall-clock time. + * May be called from any thread. + */ +uint64_t yasl_private_loop_now_ns(void); +/** + * Get the loop used for a display's file watches and application timers. + * @param d Display, or NULL. + * @return Borrowed loop, or NULL. Do not destroy a display-owned loop yourself. + */ +struct yasl_private_loop *yasl_private_display_loop(struct yasl_private_display *d); +/** + * Register a borrowed file descriptor for poll-style readiness. + * @param l Loop. + * @param fd Open file descriptor; it remains owned by the caller. + * @param events Bitmask from <poll.h>, for example POLLIN or POLLOUT. + * @param fn Callback receiving poll revents (including errors/hangup); return value is ignored. + * @param ud Borrowed userdata until watch removal or loop destruction. + * @return Positive watch ID, or -1 on failure. + * Delete the watch before closing fd. The callback must consume readiness or + * change/remove its watch to avoid repeated immediate wakeups. + */ +int yasl_private_loop_add_fd(struct yasl_private_loop *l, int fd, short events, + int (*fn)(int fd, short revents, void *ud), void *ud); +/** + * Remove a file watch without closing its descriptor. + * @param l Loop. + * @param id Watch ID returned by yasl_private_loop_add_fd(). + * @return Zero if removed, or -1 for an invalid/missing watch. + */ +int yasl_private_loop_del_fd(struct yasl_private_loop *l, int id); +/** + * Replace a watch's poll event mask. + * @param l Loop. + * @param id Watch ID. + * @param events New <poll.h> mask; zero disables requested readiness (errors may still arrive). + * @return Zero if updated, or -1 for an invalid/missing watch. + */ +int yasl_private_loop_set_fd_events(struct yasl_private_loop *l, int id, short events); + +/* Timers use nanoseconds. Return the next interval to repeat/reschedule, or zero + * to remove the timer. A zero ID indicates failure. Loop timers live until they + * stop, are deleted, or the loop is destroyed. Surface timers are also canceled + * when their surface is destroyed. */ +/** Timer identity scoped to its loop. Zero is invalid; do not reuse a retired ID. */ +typedef uint32_t yasl_private_timer_id; +/** Timer callback. interval_ns is the last scheduled delay; return the next delay or zero to stop. */ +typedef uint64_t (*yasl_private_timer_cb)(void *ud, yasl_private_timer_id id, uint64_t interval_ns); + +/** Convert a nonnegative integer millisecond value to nanoseconds; avoid overflow. */ +#define YASL_PRIVATE_MS_TO_NS(ms) ((uint64_t)(ms) * 1000000ull) + +/** + * Schedule a timer on a loop. + * @param l Loop. + * @param interval_ns Nonzero delay in monotonic nanoseconds before first firing. + * @param cb Required callback; returns a new nonzero interval to repeat, or zero to stop. + * @param ud Borrowed userdata until the timer stops, is deleted, or the loop is destroyed. + * @return Nonzero timer ID, or zero for invalid arguments or allocation failure. + * Repeating timers coalesce missed deadlines; do not use them to count elapsed + * wall time. The callback receives the last scheduled interval, not elapsed time. + */ +yasl_private_timer_id yasl_private_loop_add_timer(struct yasl_private_loop *l, uint64_t interval_ns, + yasl_private_timer_cb cb, void *ud); +/** + * Cancel a loop timer, including from its own callback. + * @param l Loop. + * @param id Timer ID. + * @return true if an armed timer was removed, false if invalid or already retired. + */ +bool yasl_private_loop_del_timer(struct yasl_private_loop *l, yasl_private_timer_id id); +/** + * Schedule a timer canceled automatically on surface destruction. + * @param s Surface whose display loop will run the timer. + * @param interval_ns Nonzero initial delay in nanoseconds. + * @param cb Required timer callback, with the same rules as yasl_private_loop_add_timer(). + * @param ud Borrowed userdata until the timer ends or the surface is destroyed. + * @return Nonzero timer ID, or zero on failure. + * The surface does not own userdata. Cancel with yasl_private_surface_del_timer(). + */ +yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns, + yasl_private_timer_cb cb, void *ud); +/** + * Cancel a timer registered through yasl_private_surface_add_timer(). + * @param s Owning surface. + * @param id Timer ID. + * @return true if still armed and removed; false if unknown or already retired. + */ +bool yasl_private_surface_del_timer(struct yasl_surface *s, yasl_private_timer_id id); + +#ifdef __cplusplus +} +#endif + +#endif /* YASL_PRIVATE_LOOP_H */ |
