From dd1775a9927922c14f8679ccbc4d3ca8d5917e20 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Tue, 13 Oct 2026 21:23:03 +0300 Subject: public api refactor --- docs/api-policy.md | 2 +- docs/api-redesign.md | 147 +++++++++++++++++++++++++++++++++++++++++++++++ docs/getting-started.md | 21 +++---- docs/guide.md | 149 +++++++++++++++++++++++++++++------------------- docs/migrating.md | 45 ++++++++++++++- 5 files changed, 292 insertions(+), 72 deletions(-) create mode 100644 docs/api-redesign.md (limited to 'docs') diff --git a/docs/api-policy.md b/docs/api-policy.md index dd22ca7..3e510f3 100644 --- a/docs/api-policy.md +++ b/docs/api-policy.md @@ -4,7 +4,7 @@ YASL 0.1 is pre-release. The headers under `include/yasl/` are the public API; i headers under `src/` are private even when their symbols appear in a static archive. The shared library exports only declarations from public headers. Native backend accessors are exported only for built backends. Installation -copies only the shared and static libraries; use matching source-tree headers. +copies both libraries, public headers, enabled native headers, and pkg-config metadata. There is currently no stable ABI promise and no versioned shared-library SONAME. Rebuild consumers against the exact headers/library pair they ship. Source and diff --git a/docs/api-redesign.md b/docs/api-redesign.md new file mode 100644 index 0000000..a111a92 --- /dev/null +++ b/docs/api-redesign.md @@ -0,0 +1,147 @@ +# Public API redesign + +This is the implementation contract for the pre-release API revision. The +portable layer owns event ordering, queued payloads, surface identities, and +frame validation. Backends translate native behavior into that contract. + +## One event stream + +`yasl_display_poll_event(display, &event)` consumes one display-wide FIFO. Input, +window resize/close/state, drag hover/drop, composition, clipboard results, and +display quit/reopen/loss use the same `yasl_event`. Events have an increasing +`sequence` assigned when YASL queues them, and a monotonic `time_ns`. Sequence +orders capture by YASL, not unknowable ordering between OS threads or devices. +The runner consumes this same queue; it does not drain separate queues by window. + +Event strings are copied on enqueue and borrowed until the next successful +display poll or display close, even if the recipient is destroyed. Destroying a +surface removes its undelivered events. A callback may destroy its recipient; +it must stop using that surface. Draws remain synchronous to satisfy native +live-resize rules; the runner drains queued events before invoking a frame. + +Legacy surface polling consumes input from the same queue and discards pending +non-input notifications, which were already delivered through legacy callbacks. +Applications must choose one polling style; discarding prevents idle spin and +unbounded retention in old manual loops. Legacy window +callbacks remain for migration, but applications should choose queue delivery +and a draw callback. Backend notifications must queue before calling a legacy +callback, and must not access a surface after a callback that can destroy it. + +## One frame contract + +Both manual drawing and `yasl_app_desc.frame` receive `struct yasl_frame`. +Its `target` describes CPU pixels (NULL for native rendering), device-pixel size, +and row stride. The frame also carries frame index, scale, and an estimated +animation timestamp. Native drawables are queried through ``; +there is no untyped GPU layer or public internal pointer in the portable frame. + +`yasl_frame_present(frame, rects, count)` submits the exact active frame once. +NULL with zero count means full damage; a non-NULL array with zero count means +no damage. NULL with nonzero count and negative extents fail with EINVAL. +More than the backend rectangle limit falls back to full damage after validating +every rectangle; damage must never be silently lost. Targets expire at callback +return. Pixels must always describe a complete frame. + +`yasl_surface_request_frame` is the normal coalescing redraw operation. +`yasl_surface_set_pacing` returns success/failure and works with either loop +style. Continuous drawing uses native pacing where available and a refresh-based +timer otherwise. On-demand surfaces require explicit invalidation. + +## Identities, posting, and shutdown + +`yasl_surface_id(surface)` returns a nonzero identity unique within its display. +`yasl_display_post(display, &message)` is thread-safe; messages target an identity +(zero means display-wide), never a borrowed surface pointer. The display must +remain alive during posting, and producer threads must stop before display close. +After a successful post the optional release callback runs exactly once on the +display thread, after consumption or cancellation (including surface destruction +and shutdown). A failed post leaves payload ownership with the caller. Posting +to a retired identity fails. The display queue owns only its copy of the message; +the release callback defines payload cleanup. Release may post, but must not +poll, dispatch, or destroy YASL objects. Legacy `yasl_post_event` adapts to +this mechanism, with the caller responsible for its borrowed target lifetime. + +Application callbacks may return CONTINUE, QUIT, or ERROR. ERROR makes +`yasl_run` return failure after cleanup. `yasl_app_exit(display, result)` permits +timers, fd callbacks, and frames to request the same orderly termination. +The optional initial window is available through `yasl_app_window(display)`. +Surface userdata is independent of legacy callback userdata and accessible +through `yasl_surface_set_userdata` / `yasl_surface_userdata`. + +## Window requests and units + +New code spells toplevel creation size `size_logical`; `size` remains an alias +for migration. Framebuffer geometry, pointer coordinates, popup placement, +damage, IME carets, and drag-image coordinates are device pixels as documented +on their structures. Conversion helpers remain explicit. + +`yasl_surface_set_size(surface, logical_size)`, +`yasl_surface_set_fullscreen(surface, enabled)`, +`yasl_surface_set_maximized(surface, enabled)`, and +`yasl_surface_minimize(surface)` return whether the native request was issued. +They set errno on failure: EINVAL for invalid input/non-toplevel surfaces, +ENOTSUP for unsupported requests, or an OS error. Success is acceptance of a +request, not a promise that the window manager honored it. Resulting size/state +is reported through events. No portable absolute window-position setter exists. + +## Clipboard and composition + +`yasl_clipboard_set(display, selection, text)` returns whether a copied UTF-8 +offer was issued, with errno on failure. `yasl_clipboard_request(surface, +selection)` returns a nonzero request ID, or zero with errno on failure. +The backend operation `display_request_paste(d, selection, surface, id)` returns +true only after accepting responsibility for one completion. It completes via +`yasl_surface_complete_paste(surface, id, status, text, len)`; status is +YASL_TRANSFER_OK, UNAVAILABLE, or FAILED. Results use YASL_EVENT_PASTE and its +`.paste` payload (data/len/request_id/status); legacy `.text` aliases data/len. +Surface destruction cancels delivery and native transfers. A backend must +complete errors as well as success, including empty text. Request IDs are +monotonic per display. Legacy paste uses ID zero. External Linux transfers time out after 30 seconds. + +YASL_EVENT_COMPOSITION carries copied UTF-8 preedit text, byte selection offsets +(both -1 if unavailable), and an active flag. Each native change queues a +snapshot, including composition ending; committed text remains YASL_EVENT_TEXT. +The query functions remain migration helpers. + +## Backend boundary + +Common helpers declared in `common/backend.h`: + +* `yasl_surface_notify_configure(s, geometry)` +* `yasl_surface_notify_close(s)` +* `yasl_surface_notify_state(s, state)` +* `yasl_surface_notify_drag(s, event, position_px)` +* `yasl_surface_notify_drop(s, event, position_px)` +* `yasl_surface_notify_composition(s, text, begin, end)` (NULL/empty ends it) +* `yasl_surface_draw(s, frame)` (fills frame metadata and invokes draw) +* `yasl_display_notify_event(d, YASL_EVENT_DISPLAY_QUIT/REOPEN/LOST)` +* `yasl_surface_complete_paste(s, request_id, status, text, length)` + +Backends keep using their existing input_queue; a new `surface_input_queue` +operation exposes that queue to common code for attaching its event publisher. +Backends must replace direct lifecycle/draw/drag callbacks with these helpers, +without weakening their native destruction guards. They implement optional +surface_set_size/fullscreen/maximized/minimize and display_copy_text / +display_request_paste operations. Common wrappers validate arguments and +report unsupported operations uniformly. + +Linux backends are compiled and tested independently and together. Cocoa changes +can be inspected here but require macOS compilation/runtime verification. + +## Validation + +The combined Linux configuration passes all 26 Meson tests, including isolated +Weston/Xwayland integration, runner error/cleanup paths, event ordering, +clipboard outcomes, composition, and destruction during callbacks. Separate +Wayland-only and X11-only builds pass their unit suites and the final public +export, C++11, and event-contract checks. + +AddressSanitizer and UndefinedBehaviorSanitizer pass the common contract tests +and the isolated backend integration suite. Leak detection was disabled because +LeakSanitizer is unavailable in this execution environment. Doxygen builds with +warnings treated as errors. A staged install links both shared and static +consumers using only the installed headers and pkg-config metadata. + +Cocoa code and native regression tests were updated and reviewed, but were not +compiled or executed: macOS validation remains required. Existing X11 INCR and +Wayland arbitrary-size limitations are reported explicitly rather than hidden. diff --git a/docs/getting-started.md b/docs/getting-started.md index 5e569fe..d1f284d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -50,20 +50,20 @@ pressure, erasers, resizing, and scale changes. ## Link an application -Install the shared and static libraries into your chosen prefix. Headers, -examples, documentation, and pkg-config metadata remain in the source tree: +Install the libraries, public headers, and pkg-config metadata into your prefix: ```sh meson setup build --prefix=/usr/local --libdir=lib meson compile -C build meson install -C build -cc -std=c23 -Iinclude examples/hello.c -o hello -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lyasl +cc -std=c23 examples/hello.c -o hello $(pkg-config --cflags --libs yasl) ./hello ``` -The prefix must be writable. Use matching headers from `include/`, and adjust -the library search path and runtime path for your chosen prefix and `libdir`. -Static consumers must also link the enabled backends' dependencies. +The prefix must be writable. For a custom prefix, add its pkg-config directory +to `PKG_CONFIG_PATH` and its library directory to the runtime loader path. +Use `pkg-config --static --libs yasl` for static consumers so backend dependencies +are included. Installed native accessor headers match the built backends. A Meson application can instead vendor the source under `subprojects/yasl`: @@ -75,7 +75,7 @@ executable('my-app', 'main.c', dependencies: yasl) The subproject dependency includes headers and backend link dependencies. Public headers can be included from C++11 or newer; the library itself builds -as C23. Only the library artifacts are installed by `meson install`. +as C23. The libraries, public headers, and pkg-config metadata are installed together. ## The application cycle @@ -84,9 +84,10 @@ as C23. Only the library artifacts are installed by `meson install`. 2. In `init`, create a toplevel with a positive **logical-pixel** size and call `yasl_surface_request_frame(surface)` for the first redraw. 3. In `event`, handle the union member selected by `event->type`. Return - `YASL_APP_QUIT` for a close request; otherwise return `YASL_APP_CONTINUE`. -4. In `frame`, fill every pixel in `frame->cpu.pixels` with premultiplied - `0xAARRGGBB` and present once with `yasl_frame_present_cpu(frame, NULL, 0)`. + `YASL_APP_QUIT` for a close request, `YASL_APP_ERROR` for failure, or + `YASL_APP_CONTINUE` to keep running. +4. In `frame`, fill every pixel in `frame->target.pixels` with premultiplied + `0xAARRGGBB` and present once with `yasl_frame_present(frame, NULL, 0)`. 5. Let the runner close remaining surfaces and the display. Release your own resources in the optional `quit` callback or after `yasl_run()` returns. 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 `` 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 `` 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 diff --git a/docs/migrating.md b/docs/migrating.md index 86804c8..912ea4d 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -4,6 +4,45 @@ YASL 0.1 has no stable source or binary compatibility promise. Rebuild all consumers against matching public headers and the library after updating. See the [application guide](guide.md) for the full window and input workflow. +## Display queue, frames, and application failures + +The runner now consumes the display-wide FIFO directly. Manual loops should +replace per-surface polling with `yasl_display_poll_event(display, &event)`. +Surface polling remains an input-only adapter that discards pending non-input +notifications delivered through legacy callbacks. Do not mix polling styles. Strings from either poll +now survive recipient destruction and expire at the next successful poll on +that display, or display close. Queue allocation failure is reported by dispatch +as an error instead of silently losing a lifecycle notification. + +Replace `struct yasl_frame_context` with `struct yasl_frame`, `frame->cpu.pixels` +with `frame->target.pixels`, and `frame->size` with `frame->target.size`. +`target.stride_bytes` replaces `cpu.stride_bytes`. Native drawing uses +`yasl_surface_get_native_frame(surface, &frame->target, &native)`. +`yasl_frame_present(frame, NULL, 0)` replaces the CPU presentation wrapper. +A non-NULL damage array with count zero now explicitly means no damaged pixels. +Lists larger than the backend limit become full damage instead of truncating. + +`YASL_APP_ERROR` and `yasl_app_exit(display, result)` report application failures +through `yasl_run`. `yasl_app_window(display)` gives init access to desc.window. +Use `yasl_surface_set_userdata` / `yasl_surface_userdata` for per-window state, +or `window.userdata` during creation. `yasl_surface_set_callbacks` now returns a +boolean and rejects runner-owned surfaces with EBUSY. + +Spell toplevel creation sizes `size_logical`; `size` remains an alias. +New window request functions report acceptance with errno. Pacing now works +outside the runner too, and returns a boolean instead of silently ignoring calls. + +Replace clipboard copy/paste with `yasl_clipboard_set` / `yasl_clipboard_request`. +Check the returned request ID and `event.paste.status` on completion. Empty text +is successful; missing data and transfer errors now have explicit outcomes. +Preedit snapshots arrive through `YASL_EVENT_COMPOSITION`; state and hover use +`YASL_EVENT_WINDOW_STATE`, `YASL_EVENT_DRAG`, and `YASL_EVENT_DROP_HOVER`. + +For worker messages, capture a surface ID on the display thread and use +`yasl_display_post` with an optional release callback. Do not also free the +payload in the event handler when release owns cleanup. Stop producer threads +before display shutdown; retired IDs reject posting with ENOENT. + ## Application callbacks All examples now use `` and `yasl_run()`. Describe a single window @@ -11,7 +50,7 @@ in `yasl_app_desc.window`, or create windows in `.init`; move input handling to `.event` and drawing to `.frame`. Replace manual dispatch/poll/flush loops with the runner and request redraws with `yasl_surface_request_frame()`. Present the frame context with -`yasl_frame_present_cpu()`; the runner destroys remaining surfaces on exit. +`yasl_frame_present()`; the runner destroys remaining surfaces on exit. Window state remains a `YASL_STATE_*` bitmask; `YASL_STATE_TILED` is reported for tiled Wayland toplevels. @@ -19,8 +58,8 @@ for tiled Wayland toplevels. ## Events The core API and the runner now share one `struct yasl_event`, declared in -``. `yasl_surface_poll_event()` returns the same events that -`yasl_app_desc.event` receives, so input handling code works under both. +``. `yasl_display_poll_event()` returns the same events that +`yasl_app_desc.event` receives. Legacy surface polling returns only input types. | Previous use | Replacement | | --- | --- | -- cgit v1.2.3