diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 21:23:03 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-13 21:23:03 +0300 |
| commit | dd1775a9927922c14f8679ccbc4d3ca8d5917e20 (patch) | |
| tree | 3acc442bb4a3470364b74aa82ff67cb84d555c45 /docs/guide.md | |
| parent | ecb81aeea4c0c880b3a60a0dc8418c4bf2a7cf85 (diff) | |
public api refactor
Diffstat (limited to 'docs/guide.md')
| -rw-r--r-- | docs/guide.md | 149 |
1 files changed, 91 insertions, 58 deletions
diff --git a/docs/guide.md b/docs/guide.md index 76279c4..1b64880 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -44,13 +44,21 @@ All examples include `<yasl/app.h>` and call `yasl_run()` with a which the runner creates before `init` with its first frame requested; `yasl_run()` returns an error if creation fails. Create further surfaces in the optional `init`, handle `YASL_EVENT_*` notifications in `event`, and render into -`frame->cpu.pixels` in `frame`. Present with `yasl_frame_present_cpu(frame, NULL, +`frame->target.pixels` in `frame`. Present with `yasl_frame_present(frame, NULL, 0)` and return `YASL_APP_QUIT` from an event handler to exit. Without an `event` -callback, closing any window stops the runner. The runner handles dispatch, input polling, resize redraws, +callback, closing any window stops the runner. The runner handles dispatch, queued event delivery, resize redraws, and remaining surface/display cleanup. Display quit stops the runner; connection loss and dispatch failures return an error after cleanup. The runner delivers -input exactly as `yasl_surface_poll_event()` returns it, plus -`YASL_EVENT_WINDOW_RESIZE`, `YASL_EVENT_WINDOW_CLOSE`, and `YASL_EVENT_USER`. +the same display-wide FIFO consumed by `yasl_display_poll_event()`, including +window state, drag hover, composition, clipboard completion, and display lifecycle +events. `event.sequence` increases in enqueue order across all windows; it is +YASL capture order, not a claim about ordering between native producers. +Return `YASL_APP_ERROR` on application failure, or call +`yasl_app_exit(display, YASL_APP_ERROR)` from a frame, timer, or fd callback. +Cleanup still runs, and `yasl_run()` returns failure. During `init`, +`yasl_app_window(display)` exposes the optional initial window. Application data +can be supplied in `window.userdata` or attached with +`yasl_surface_set_userdata()`; it is independent of callback userdata. Unified pointer button and motion events retain `device` and `pen` axes, including erasers and pressure-only motion. Cancel held input or active strokes @@ -62,7 +70,7 @@ For applications managing their own event loop, use the core API below. Open the display with application-wide options and notifications in `yasl_display_spec`. Create each toplevel with `yasl_window_spec`, including its -title, logical size, optional `yasl_window_cbs` table in `cbs`, and userdata in +title, `size_logical`, optional `yasl_window_cbs` table in `cbs`, and userdata in `ud`. One display can own multiple windows with separate callback tables. The window copies its callbacks during creation. Its callbacks cannot run before @@ -75,6 +83,9 @@ installs popup callbacks; the setter does not invoke the callbacks. When using `yasl_run()`, its runner installs callbacks for windows on its display. Use `yasl_app_desc.event` and `.frame` rather than `yasl_window_spec.cbs` and `.ud`. +Replacing callbacks on a runner-owned surface fails with `EBUSY`. +With a manual loop, install just the draw callback and drain the display FIFO +after dispatch; lifecycle and input share the same event handler logic. Read window state with `yasl_surface_state()`. Its `YASL_STATE_*` flags can coexist, and the `state` callback receives the same current bitmask. Wayland @@ -114,7 +125,7 @@ saturates out-of-range finite results, and returns zero for a zero scale. ## Drawing and pacing -`yasl_surface_schedule_redraw()` coalesces requests. `yasl_display_flush()` +`yasl_surface_request_frame()` coalesces requests. `yasl_display_flush()` services them when the backend can draw. During a native resize, dispatch can also draw synchronously. Avoid assuming callbacks occur only in flush. @@ -132,27 +143,27 @@ static void draw(struct yasl_surface *surface, struct yasl_frame *frame, void *u size_t count = (size_t)frame->target.size.x * frame->target.size.y; for (size_t i = 0; i < count; i++) frame->target.pixels[i] = 0xff306090; - struct yasl_damage_list damage = {.n = -1}; - if (!yasl_surface_present(surface, &frame->target, &damage)) { + if (!yasl_frame_present(frame, NULL, 0)) { /* Arrange application error handling after returning. */ } } ``` -Present that exact target at most once during draw. Do not change its pointer, -size, or generation, free it, or use it after callback return or surface -destruction. A successful present consumes it. Invalid input returns false -without consuming it. `damage.n = -1` means full target; `0` means no damaged -rectangles; positive `n` uses the first `n` entries in `rects`. Extents must be -nonnegative, and the core clips them to the target. `bounds` is a reserved hint -and can be zero. Damage may reduce uploads, but every target must still contain -complete valid content; backends may upload all pixels. - -To animate, schedule again from draw. With `YASL_CAP_FRAME_PACING`, the backend -uses display/compositor signals to limit draws; tearing can bypass this. X11 -is unpaced: scheduling continuously will use CPU continuously. Use an application -timer to limit redraws there. Suspended/hidden windows need not draw at each -requested interval. +Present the exact active frame at most once during its callback. Do not mutate +the target identity or retain frame/pixel pointers after return. NULL rectangles +with count zero mean full damage; a non-NULL array with zero count means no damage. +All supplied extents must be nonnegative. Rectangles are clipped, and more than +`YASL_DAMAGE_MAX` rectangles fall back to full damage after all are validated. +Damage may reduce uploads, but every target must contain complete valid pixels. +Native rendering uses the same frame, with a drawable queried in the extension. + +`yasl_surface_set_pacing()` works in either loop style. ON_DEMAND coalesces +explicit requests and sleeps when idle. CONTINUOUS uses native display signals +where available and a refresh-based timer otherwise, with about 60 Hz as a +fallback estimate. It reports allocation/argument failures instead of silently +ignoring surfaces outside the runner. Suspension can suppress actual drawing. +Legacy `yasl_surface_present()` and `yasl_surface_schedule_redraw()` remain +migration adapters; new code uses the frame presentation and request functions. `yasl_surface_read_pixels()` copies the last presented CPU frame, for example for a screenshot or a debugging tool. Call it outside draw callbacks. A frame @@ -170,19 +181,20 @@ clock; it does not establish actual scanout time. ## Input, text, and payload lifetime -Dispatch receives native input. `yasl_surface_poll_event()` only drains it and -never waits. Poll every live surface queue until empty. Wayland routes popup -pointer and keyboard events through the root parent's queue; inspect -`event.surface` to identify the recipient. Do not assume it equals the object -whose queue you polled. - -Read the union member selected by `event.type`; `time_ns` is a monotonic -timestamp on the `yasl_loop_now_ns()` clock. Polling returns only input types, -`YASL_EVENT_KEY` through `YASL_EVENT_INPUT_RESET`. Text, paste, and file-drop -payloads share `event.text`: UTF-8 byte strings, NUL-terminated; `len` excludes -the NUL. They remain valid until the -next **successful** poll on the queue or queue destruction. Dispatch, flush, -and unsuccessful polls do not invalidate them. Copy data to keep it longer. +Dispatch receives native input; `yasl_display_poll_event()` drains the display +FIFO without waiting. Drain it once for the whole display, including multiple +windows and popup input. `event.surface` identifies the live recipient and +`surface_id` is its stable identity. Destruction cancels undelivered recipient +events. `yasl_surface_poll_event()` remains an input-only migration adapter and +discards pending non-input notifications, which legacy callbacks already +delivered. Do not mix the two polling styles. + +Read the union member selected by `event.type`. `time_ns` is monotonic and +`sequence` orders capture across the display. TEXT and DROP use `.text`, PASTE +uses `.paste`, and COMPOSITION uses `.composition`. Each string is copied when +queued, NUL-terminated, and borrowed until the next successful display/surface +poll or display close. Dispatch, unsuccessful polls, and recipient destruction +do not invalidate the last polled string. Copy it to retain it longer. Use `YASL_EVENT_TEXT` for document text insertion, including committed IME text. It contains UTF-8 and can contain several characters. Key actions are for @@ -265,19 +277,22 @@ device interruptions; ignoring it can leave application input stuck. ## Clipboard, file drops, and IME -`yasl_display_copy()` copies a NUL-terminated UTF-8 string during the call. -`yasl_display_paste()` requests delivery to the original target as -`YASL_EVENT_PASTE`; destroying the target cancels delivery. There is no transfer -ID or error callback, so unavailable or failed transfers may produce no event. -Wayland selection offers can require a recent input serial. X11 currently does -not implement incremental (`INCR`) selection transfers and may reject large -transfers. X11 converts legacy `STRING` data between Latin-1 and UTF-8; it only -offers that legacy format when the copied text fits Latin-1. Malformed UTF-8 -clipboard transfers are discarded. Unsupported primary selection does nothing -and never aliases the clipboard. Cocoa uses a YASL named pasteboard to emulate primary selection; -this is not a desktop-wide macOS select-to-copy convention. - -External file drag hover uses the surface `drop` callback; actual file URLs +`yasl_clipboard_set(display, selection, text)` copies a NUL-terminated UTF-8 +offer and reports failure through its return and errno. Wayland may require a +recent input serial. `yasl_clipboard_request(surface, selection)` returns a +nonzero request ID, or zero with errno when rejected. Each accepted request +produces one `YASL_EVENT_PASTE` carrying `.paste.request_id`, `.status`, `.data`, +and `.len`, unless its target/display is destroyed. OK includes empty text; +UNAVAILABLE means no text offer, and FAILED means transfer/decoding failure. +External Linux transfers have a 30-second timeout. X11 does not implement INCR +and reports unsupported large transfers as FAILED. Legacy STRING uses Latin-1 +conversion. Malformed UTF-8 completes as failure. + +Unsupported primary selection reports ENOTSUP and never aliases the clipboard. +Cocoa uses a YASL named pasteboard for primary selection. Legacy copy/paste calls +remain adapters; legacy paste completions carry request ID zero. + +External file drag hover uses `YASL_EVENT_DROP_HOVER`; actual file URLs arrive as `YASL_EVENT_DROP`. `text.data` is a `text/uri-list` string separated by CRLF, not a list of native paths. `yasl_drop_next_path()` walks it and decodes each local `file:` URL into a path. It skips comments, other schemes, remote @@ -296,12 +311,12 @@ while ((len = yasl_drop_next_path(&cursor, path, sizeof(path))) != 0) { Decoded paths are raw bytes and may contain any character the file system allows, including newlines. Quote them before pasting into a shell. -Use `yasl_surface_ime_preedit()` to draw the uncommitted composition. The borrowed -pointer expires on the next dispatch or surface destruction; copy to keep it. -Set the candidate-window caret with `yasl_surface_set_ime_caret()` in device -pixels. X11 has no inline preedit. Cocoa supplies preedit text but currently no -cursor range. On Wayland, `yasl_surface_ime_preedit_cursor()` returns UTF-8 byte -offsets when a range is available. A false result leaves outputs unchanged. +Handle `YASL_EVENT_COMPOSITION` for immutable UTF-8 preedit snapshots, byte +selection offsets, and composition ending. Missing selection information uses +-1 offsets. Insert only committed `YASL_EVENT_TEXT` into the document. Set the +candidate-window caret with `yasl_surface_set_ime_caret()` in device pixels. +X11 uses XIM preedit callbacks when offered; fallback input methods may provide +no inline composition. The old preedit getters remain migration helpers. ## Popups and surface drags @@ -315,8 +330,8 @@ serial to grab a popup, and older protocols may reject repositioning. Destroy children before parents. The close callback requests application destruction; it does not automatically free the popup. -The surface `drag` callback handles a YASL surface drag; `drop` handles external -data hover. `yasl_display_drag_start()` is intended for window/tab tear-off +`YASL_EVENT_DRAG` handles a YASL surface drag; `YASL_EVENT_DROP_HOVER` handles +external data hover. Legacy callbacks remain migration adapters. `yasl_display_drag_start()` is intended for window/tab tear-off workflows, not arbitrary MIME data sources. Keep transferred application state in your own registry. Start from a recent pointer press, and check the boolean result even when `YASL_CAP_DRAG` is present. Supply an optional copied image and @@ -367,7 +382,7 @@ Every backend's dispatch wakes when a loop descriptor becomes ready. Cocoa waits for native events and mirrors the loop's descriptors in a kqueue while it waits. If kqueue rejects a descriptor, Cocoa bounds the wait to 10 ms instead. -Two calls work from any thread. `yasl_loop_kick()` only wakes the loop, and it +Loop wake and post calls work from any thread. `yasl_loop_kick()` only wakes the loop, and it is also safe in a signal handler. `yasl_loop_post()` queues a function to run on the owning thread during a later step or dispatch. Use it to hand results from worker threads to the loop: @@ -387,6 +402,24 @@ and restores the previous action on unwatch. YASL leaves thread signal masks alone: a watched signal must stay unblocked in at least one thread. Only one loop can watch a given signal at a time. +## Worker messages and window requests + +Capture `yasl_surface_id(surface)` on the display thread and pass that value to +workers. `yasl_display_post(display, &message)` accepts an identity instead of a +borrowed surface pointer. Zero targets the display. On success, optional +`message.release(payload)` runs exactly once on the display thread after delivery +or cancellation, including shutdown. It may post messages but must not poll, +dispatch, or destroy YASL objects. Failed posting leaves ownership with the caller. +Stop and join producers before closing the display. The same API works with a +manual loop and with the runner; `yasl_post_event()` is a legacy adapter. + +Toplevel requests `yasl_surface_set_size(surface, logical_size)`, +`yasl_surface_set_fullscreen`, `yasl_surface_set_maximized`, and +`yasl_surface_minimize` return acceptance with errno on failure. Confirm actual +geometry/state through queued events. Popup targets are invalid. Wayland cannot +request an arbitrary live size and returns ENOTSUP for that operation; fullscreen, +maximize, and minimize use xdg-shell requests. X11 and Cocoa implement all four. + ## Desktop services Include `<yasl/desktop.h>` for services that sit next to windowing. Display and @@ -436,7 +469,7 @@ dispatch native connections, or change surface roles and buffer ownership. Application-managed native drawing currently supports Cocoa/Metal. Use `yasl_surface_create_native()` with a non-NULL Metal device, which YASL retains. During draw, query `yasl_surface_get_native_frame()` for a borrowed drawable, -encode and present with your renderer, then call `yasl_surface_present()` to +encode and present with your renderer, then call `yasl_frame_present()` to record submission. The drawable expires on successful present, callback return, or surface destruction. Do not infer GPU completion from the boolean result. Other backends return NULL with `errno = ENOTSUP` for native creation; having a |
