summaryrefslogtreecommitdiff
path: root/docs/guide.md
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 22:12:02 +0300
commit94c5e361fbb229b0cd6853e2102a7d95f1f358b3 (patch)
tree3c0f11af9dcb84dac8067a31a6979acd6dd56fbf /docs/guide.md
parent08dc9047098d6d96791024ac7d8f5837633673ef (diff)
better public interface and doxygen
Diffstat (limited to 'docs/guide.md')
-rw-r--r--docs/guide.md298
1 files changed, 298 insertions, 0 deletions
diff --git a/docs/guide.md b/docs/guide.md
new file mode 100644
index 0000000..43a7a1e
--- /dev/null
+++ b/docs/guide.md
@@ -0,0 +1,298 @@
+# Application guide
+
+The contracts in [yasl.h](../include/yasl/yasl.h), [loop.h](../include/yasl/loop.h),
+[timing.h](../include/yasl/timing.h), and [native.h](../include/yasl/native.h) are
+the reference for individual calls. All examples here use the display's opening
+thread, which must be the main thread on macOS. Userdata stays owned by the
+application. Copy borrowed data before retaining it beyond its documented life.
+
+## Backend selection and capabilities
+
+Zero-initialize `yasl_display_spec` or pass NULL to select automatically. On
+Linux, `WAYLAND_DISPLAY`, `WAYLAND_SOCKET`, or `XDG_SESSION_TYPE=wayland` makes
+YASL try Wayland first; otherwise it tries X11 first. It falls back to the other
+built backend if opening fails. On macOS it uses Cocoa. Explicit
+`YASL_BACKEND_WAYLAND`, `YASL_BACKEND_X11`, or `YASL_BACKEND_COCOA` requests never
+fall back. Query the result with `yasl_display_backend()`.
+
+Use `yasl_display_caps()` for optional behavior. A bit means backend/server
+support, not that a device is attached or that the current input state allows
+an operation. Always check the operation's return value too.
+
+| Capability | Meaning | Wayland | X11 | Cocoa |
+| --- | --- | --- | --- | --- |
+| `YASL_CAP_POPUPS` | Child popup surfaces | Yes, input grab required | Yes | Yes |
+| `YASL_CAP_DRAG` | Surface-drag source and target events | Data device v3 | Yes | Yes |
+| `YASL_CAP_DRAG_CARRY` | Carry a toplevel with a surface drag | Optional toplevel-drag protocol | Yes | Yes |
+| `YASL_CAP_PLACE_WINDOW` | Place a window under the pointer | No | Yes | Yes |
+| `YASL_CAP_PRIMARY_SELECTION` | Separate primary text selection | Optional primary-selection protocol | Yes | Named pasteboard emulation |
+| `YASL_CAP_CSD` | Backend advertises client decoration support | Yes | No | Yes |
+| `YASL_CAP_NATIVE_GRAPHICS` | Application-managed native drawables | No | No | Metal |
+| `YASL_CAP_FRAME_PACING` | Display/compositor-driven drawing | Yes | No | Yes |
+| `YASL_CAP_TABLET` | Tablet event translation | Optional tablet-v2 protocol | XInput 2 | Yes |
+
+This table describes the implementation, not universal operating-system
+promises. Wayland protocols and XInput availability depend on the running
+server. A capability does not guarantee every related action: Cocoa currently
+returns false for interactive resize and window-menu requests even though
+it advertises CSD. Check each result and provide an application fallback.
+
+## Geometry and scaling
+
+| Value | Units |
+| --- | --- |
+| Toplevel `yasl_surface_spec.size` | Logical pixels |
+| `yasl_geometry.size`, framebuffer size | Device pixels |
+| Pointer position, caret, window-menu position | Device pixels, relative to the surface |
+| Damage rectangles | Device pixels, relative to the draw target |
+| All popup fields, including `size` | Device pixels; anchor is relative to the parent |
+| Drag image size, hotspot, carry/place offsets | Device pixels |
+| `scale_120` | 120 = 1x, 180 = 1.5x, 240 = 2x |
+
+The origin is the top left, x increases right, and y increases down. Use
+`yasl_surface_geometry()` for the current size and scale, and update cached
+layout on `configure`. The native system may choose a different size from the
+request. X11 currently reports unit scale; automatic output DPI scaling is not
+implemented there.
+
+```c
+struct yasl_geometry geometry = yasl_surface_geometry(surface);
+int padding_px = yasl_scale_to_device(8.0, geometry.scale_120);
+int logical_width = yasl_scale_to_logical(geometry.size.x, geometry.scale_120);
+```
+
+Scale conversion rounds to the nearest integer, with ties away from zero,
+saturates out-of-range finite results, and returns zero for a zero scale.
+`yasl_scale_to_device()` also returns zero for non-finite logical coordinates.
+
+## Drawing and pacing
+
+`yasl_surface_schedule_redraw()` 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.
+
+The CPU target is tightly packed premultiplied `0xAARRGGBB`. Row stride is
+`target.size.x * sizeof(uint32_t)`. For example, opaque red is `0xffff0000`,
+50% transparent red is approximately `0x80800000`, and transparent black is
+`0x00000000`. The numeric format describes a 32-bit value, not a portable byte
+sequence. Keep your application's durable content separately: a new target may
+contain no previous frame contents.
+
+```c
+static void draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud)
+{
+ (void)ud;
+ 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)) {
+ /* 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.
+
+`yasl_surface_timing()` reports estimates and their provenance. All timestamps
+are monotonic nanoseconds. `YASL_TIMING_SUBMISSION` is CPU submission time,
+`YASL_TIMING_FRAME_CALLBACK` is a compositor pacing signal, and only
+`YASL_TIMING_PRESENTATION` confirms compositor presentation. `UNKNOWN` means no
+sample. `refresh_ns` and `next_present_ns` remain estimates. Use
+`yasl_surface_frame_time_ns(surface, yasl_loop_now_ns())` as a stable animation
+clock; it does not establish actual scanout time.
+
+## Input, text, and payload lifetime
+
+Dispatch receives native input. `yasl_surface_input_poll()` 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.kind`. The `reserved` field is internal
+and must not be interpreted. Text, paste, and file-drop payloads are 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.
+
+Use `YASL_INPUT_TEXT` for document text insertion, including committed IME text.
+It contains UTF-8 and can contain several characters. Key actions are for
+commands and held-key state; releases and repeats have separate tags. A text
+commit need not correspond to one key event, and a key need not produce text.
+
+`key.key` is a portable physical identity from
+[keycodes.h](../include/yasl/keycodes.h), included by `<yasl/yasl.h>`. Names follow
+US keyboard positions: `YASL_KEY_A` identifies the same position on a French
+layout even though it types Q. Modifiers do not change this identity. Keypad
+keys and left/right modifiers have distinct names. Unknown native keys report
+`YASL_KEY_UNKNOWN`; `native_code` preserves their backend-specific identity
+(Wayland evdev code, X11 server keycode, or Cocoa virtual keycode).
+
+`codepoint` and `shifted` are optional Unicode scalar labels for the active
+layout without modifiers and with Shift. Zero means no single printable label,
+including function keys, unavailable translations, and mappings that produce
+multiple characters. Dead keys may have a spacing label or zero. These fields
+are useful for displaying shortcuts or
+choosing layout-sensitive commands; they are never document text. For example:
+
+```c
+if (event.kind == YASL_INPUT_KEY && event.key.action == YASL_KEY_PRESS &&
+ event.key.key == YASL_KEY_ESCAPE) {
+ running = false;
+}
+/* Insert event.text.text[0..event.text.len) only for YASL_INPUT_TEXT. */
+```
+
+Wayland uses the supplied XKB layout and locale compose rules for ordinary text
+and dead keys, with text-input-v3 for IME commits. Compose rules use `LC_ALL`,
+`LC_CTYPE`, then `LANG`, falling back to `C.UTF-8`. X11 uses the X input method
+when available; Cocoa uses AppKit text services. X11 input methods use the
+application's `LC_CTYPE` locale. Initialize it before opening a display when
+using locale-specific XIM, for example with `setlocale(LC_CTYPE, "")`; YASL does
+not change the process locale. IME availability and system configuration still
+determine which compositions work. Physical key events
+remain available for commands; input methods may consume keys while composing.
+
+Pointer events use `YASL_INPUT_MOUSE`, with `device` distinguishing mouse, touch,
+pen, and eraser. Press/release button 0 is primary/tip, 1 middle, and 2 secondary.
+Track those transitions; motion/leave button values are informational. Pen
+pressure is normalized to 0..65535, signed tilt is in degrees, and missing axes
+are zero. Pressure-only changes can produce motion. Zero pressure alone does
+not mean release. This interface does not expose simultaneous touch contacts.
+
+For `YASL_PTR_SCROLL`, read `mouse.scroll.x` and `.y`: fractional line offsets,
+positive right and down, after system scroll preferences. Both axes may be
+nonzero in one event. Preserve fractions and choose your application's own
+pixels per line; one wheel detent normally gives one line. `button` is unused
+for scroll. Wayland high-resolution wheel values take precedence over duplicate
+discrete/raw descriptions. Smooth Wayland offsets and precise Cocoa offsets
+use ten native units per line. X11 currently reports whole wheel steps, so it
+does not provide the same smooth-scroll precision as the other backends.
+
+Cancel held keys, buttons, active strokes, and composition on `YASL_INPUT_RESET`,
+focus loss, or pointer leave as appropriate to your application. RESET has no
+union payload. It is emitted after input-queue allocation failure and certain
+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_INPUT_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
+arrive as `YASL_INPUT_DROP_FILES`. `files` is a `text/uri-list` string separated by
+CRLF, not a list of native paths. Parse URL encoding, skip blank/comment lines,
+and decide which schemes/hosts your application accepts before opening files.
+
+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.
+
+## Popups and surface drags
+
+Create popups from a live parent, normally in response to input. All fields in
+`yasl_popup_spec` use device pixels. `size` includes transparent margins;
+`content_offset` and `content_size` describe the actual content rectangle used
+for positioning. A zero content-size axis extends to the corresponding size
+edge. `beside = true` prefers the anchor's right edge; false prefers below.
+Placement may flip or slide to stay on screen. Wayland requires a recent input
+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
+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
+completion listener. One active drag is supported per display. A successful
+start calls a non-NULL `end` exactly once, including explicit cancel or display
+close; a failed start never does. `DROPPED` does not prove that another
+application accepted your application object.
+
+Where `YASL_CAP_DRAG_CARRY` is present, `yasl_surface_drag_carry()` can attach a
+toplevel to the active drag. Otherwise an image can represent it. With
+`YASL_CAP_PLACE_WINDOW`, `yasl_surface_place_under_pointer()` requests placement
+relative to the pointer. These calls may fail due to current input/drag state.
+Completion can run synchronously during cancel/close; keep listener userdata
+alive until then and do not destroy the display recursively.
+
+## Timers and file descriptors
+
+Include `<yasl/loop.h>`. Use `yasl_display_loop(display)` for application timers
+so they participate in display dispatch. Intervals are nanoseconds; the
+`YASL_MS_TO_NS(ms)` macro converts a nonnegative integer millisecond value.
+
+```c
+static uint64_t tick(void *ud, yasl_timer_id id, uint64_t interval_ns)
+{
+ (void)id;
+ yasl_surface_schedule_redraw(ud);
+ return interval_ns; /* Return zero for a one-shot timer. */
+}
+/* Check zero for failure. This timer is canceled with the surface. */
+yasl_timer_id timer = yasl_surface_add_timer(surface, YASL_MS_TO_NS(16), tick, surface);
+```
+
+A timer callback receives the scheduled interval, not actual elapsed time. A
+nonzero return repeats/reschedules; zero retires it. Missed periods coalesce.
+Timers can add/delete timers, including themselves. Loop timers end when retired,
+deleted, or the loop is destroyed; surface timers also end on surface destruction.
+Userdata remains application-owned. Delete surface timers with
+`yasl_surface_del_timer()` and ordinary timers with `yasl_loop_del_timer()`.
+
+File watches use `<poll.h>` masks. `yasl_loop_add_fd()` returns a positive watch
+ID or -1. Its callback receives `revents`, including error/hangup flags; its
+integer return is ignored. Consume readiness or disable/delete the watch.
+The loop never closes application file descriptors: delete the watch before
+closing its descriptor. Ready callbacks may add or remove watches. Do not call
+`yasl_loop_step()` or `yasl_loop_run()` recursively.
+
+Cocoa dispatch waits for native events and checks the loop nonblockingly
+afterward. An application FD alone does not currently wake an indefinite Cocoa
+dispatch; use a bounded timeout when integrating such sources. `loop_kick()`
+signals the loop pipe but is not a portable cross-thread event-posting API.
+
+## Native interoperability
+
+Portable CPU drawing needs no native headers. Opt into `<yasl/native.h>` and
+`<yasl/native/wayland.h>`, `<yasl/native/x11.h>`, or `<yasl/native/cocoa.h>` when
+integrating a platform API. Typed headers and functions are installed only for
+built backends. Accessors return borrowed handles and zero/NULL for the wrong
+backend. Do not pass freed objects, destroy borrowed handles, independently
+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
+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
+native window handle does not imply this rendering path is supported.