diff options
Diffstat (limited to 'docs/api-redesign.md')
| -rw-r--r-- | docs/api-redesign.md | 147 |
1 files changed, 147 insertions, 0 deletions
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. |
