summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/api-policy.md2
-rw-r--r--docs/api-redesign.md147
-rw-r--r--docs/getting-started.md21
-rw-r--r--docs/guide.md149
-rw-r--r--docs/migrating.md45
5 files changed, 292 insertions, 72 deletions
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 `<yasl/native.h>`;
+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 `<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
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 `<yasl/app.h>` 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/yasl.h>`. `yasl_surface_poll_event()` returns the same events that
-`yasl_app_desc.event` receives, so input handling code works under both.
+`<yasl/yasl.h>`. `yasl_display_poll_event()` returns the same events that
+`yasl_app_desc.event` receives. Legacy surface polling returns only input types.
| Previous use | Replacement |
| --- | --- |