summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md363
1 files changed, 58 insertions, 305 deletions
diff --git a/README.md b/README.md
index 0cb2eea..8300b74 100644
--- a/README.md
+++ b/README.md
@@ -1,321 +1,74 @@
-# YASL (Yet Another Surface Library)
-
-YASL is a minimal, modern, and high-performance desktop window surface library written in C.
-
-It provides window creation, frame pacing, high-DPI scaling, and input routing without bundling audio, physics, or game engine components.
-
-## Features
-
-- **Lean Core**: No audio, no font rendering, and no widget toolkit overhead.
-- **Wayland Protocol Coverage**:
- - `xdg-shell` toplevels and hierarchical popups (`xdg_popup` + `xdg_positioner`).
- - Fractional scaling via `wp_fractional_scale_v1` and `wp_viewporter`.
- - Presentation feedback and refresh rate timing via `wp_presentation` and `wp_fifo_v1`.
- - Cursor shapes via `wp_cursor_shape_device_v1`.
- - Graphics tablets and stylus input via `zwp_tablet_v2` (pressure, tilt, eraser).
- - Text Input Method (IME) composition via `zwp_text_input_v3`.
- - Primary selection and clipboard transfers via `wl_data_device` and `zwp_primary_selection_v1`.
- - Interactive client-side window move and resize triggers (`xdg_toplevel_move`, `xdg_toplevel_resize`).
-- **macOS Cocoa & Metal**:
- - Native `NSWindow` and `CAMetalLayer` presentation.
- - Vsync pacing via `CADisplayLink`.
-- **Zero Framework Leaks**:
- - Self-contained UTF-8 validation.
- - Self-contained event queue.
- - No terminal-specific telemetry or hardcoded application names.
-
-## Public API
-
-Include `<yasl/yasl.h>` for surface creation, drawing, input, and display dispatch.
-`yasl_surface_geometry()` returns device-pixel size and scale together.
-`yasl_display_dispatch(display, timeout_ms)` polls with zero, waits indefinitely
-with a negative timeout, or waits up to a positive number of milliseconds.
-Pending input or redraw work returns immediately; call `yasl_display_flush()`
-after processing input and scheduling redraws.
-
-Call `yasl_surface_set_cursor(surface, YASL_CURSOR_HIDDEN)` to hide the system
-mouse and tablet cursor over a surface while continuing to receive pointer
-input. The choice persists across motion and re-entry; select another cursor,
-such as `YASL_CURSOR_ARROW`, to show it again. `YASL_CURSOR_NONE` is a compatible
-alias for `YASL_CURSOR_HIDDEN`.
-
-Include `<yasl/loop.h>` to use the optional event loop, file watches, or timers.
-Timers use one callback model and nanoseconds throughout; use `YASL_MS_TO_NS(5)`
-for a five-millisecond interval. Add application timers to
-`yasl_display_loop(display)`. Add surface timers with `yasl_surface_add_timer()`
-when they should be canceled automatically on surface destruction.
-
-Include `<yasl/native.h>` for platform handles and application-managed native
-rendering. These extensions are separate from the portable CPU drawing API.
-`<yasl/timing.h>` contains the public timing result types.
-
-## Backend selection
-
-YASL selects the native window backend at runtime.
-Leave `yasl_display_spec.backend` at zero to use this mode.
-`YASL_BACKEND_AUTO` has the same effect.
-You can also pass NULL as the display spec.
-
-On Linux, YASL tries Wayland first in a Wayland session.
-It uses `WAYLAND_DISPLAY`, `WAYLAND_SOCKET`, and `XDG_SESSION_TYPE` to detect that session.
-In other sessions, it tries X11 first.
-If the first backend cannot open, it tries the other built backend.
-On macOS, it uses Cocoa.
-
-Set `backend` to request one backend:
-
-```c
-struct yasl_display_spec spec = {
- .app_id = "org.example.app",
- .backend = YASL_BACKEND_X11,
-};
-struct yasl_display *display = yasl_display_open(loop, &spec);
-```
-
-Use `YASL_BACKEND_WAYLAND` to request Wayland.
-Use `YASL_BACKEND_COCOA` to request Cocoa.
-An explicit request never falls back to another backend.
-Open returns NULL if the requested backend cannot open.
-It sets `errno` to `ENOSYS` for a backend that was not built.
-It sets `errno` to `EINVAL` for an invalid backend value.
-
-Call `yasl_display_backend(display)` to read the selected backend.
-All surfaces on a display use that backend.
-Open separate displays to use Wayland and X11 in one process.
-The public API needs no window system headers.
-Shared libraries export only the functions declared in the installed YASL headers.
-Input queues, frame clocks, backend entry points, and protocol helpers are private implementation details.
-Use `yasl_display_caps()` to check optional features.
-
-X11 supports CPU pixel buffers, keyboard and pointer input, and clipboard transfer.
-Tablet pressure, tilt, and erasers use XInput 2 when the server exposes it.
-It supports the primary selection, window placement, and drag operations.
-It does not yet support YASL popups or IME preedit text.
-Those features return their normal unsupported result.
-
-The surface spec needs no native graphics device for CPU drawing.
-On Cocoa, YASL creates its own Metal device for this mode.
-An app that renders with Metal includes `<yasl/native.h>` and calls
-`yasl_surface_create_native(display, &spec, device)`. During its draw callback,
-`yasl_surface_get_native_frame()` returns the current Metal drawable. The app
-submits and presents it, then calls `yasl_surface_present()` to record submission.
-
-Type-safe native handles are declared in `<yasl/native/wayland.h>`,
-`<yasl/native/x11.h>`, and `<yasl/native/cocoa.h>`:
-- Wayland: `wl_display *` and `wl_surface *` via `yasl_wayland_display_get_wl_display()` and `yasl_wayland_surface_get_wl_surface()`.
-- X11: `Display *` and `uintptr_t` window ID via `yasl_x11_display_get_display()` and `yasl_x11_surface_get_window()`.
-- Cocoa: `NSApplication *`, `NSWindow *`, and `NSView *` via `yasl_cocoa_display_get_application()`, `yasl_cocoa_surface_get_window()`, and `yasl_cocoa_surface_get_view()`.
-Generic untyped borrowed handles (`void *`) remain accessible via `<yasl/native.h>`. All handles expire on object destruction.
-
-## Ownership and backend contracts
-
-- Calls and callbacks use the thread that opened the display (the main thread
- on Cocoa). Supplied event loops are borrowed. Destroy popups before parents,
- surfaces before displays, and displays before supplied loops. Surface
- destruction inside surface callbacks is supported; display or loop destruction
- during callbacks/dispatch is not.
-- Callback tables, drag listeners, input strings, titles, clipboard text, and
- drag pixels are copied. Userdata is borrowed. Cocoa retains a supplied Metal
- device. Native handles are borrowed, not transferred to the caller.
-- Text, paste, and file-drop event payloads remain valid until the next
- **successful** input poll on that surface or surface destruction. Dispatch,
- flush, selection replacement, and failed polls do not invalidate them.
- Copy a payload if it must live longer.
-- Input queues grow rather than silently dropping events at a fixed limit.
- If allocation fails, pending input is discarded and `YASL_INPUT_RESET` is
- delivered before later events. X11 also sends a reset when input devices are
- removed or disabled. Apps must clear held keys/buttons and input composition
- state on this event.
-- Clipboard requests retain their original target. Destroying that target
- cancels delivery, rather than redirecting the paste to a focused window.
- Unsupported primary selections are no-ops, never aliases for the clipboard.
-- CPU pixels are tightly packed premultiplied `0xAARRGGBB`. Creation sizes are
- logical pixels; framebuffer sizes, pointer coordinates, caret and damage are
- device pixels. Every draw target needs complete valid pixel contents; do not
- assume a recycled framebuffer contains the previous frame.
-- Present only the current draw callback's target, at most once. Targets expire
- when the callback returns; preserve the opaque `generation` when copying a
- target. Damage is required, clipped to the target, and validated uniformly. Backends may upload the whole frame despite damage hints.
-- `yasl_surface_create_native()` requires `YASL_CAP_NATIVE_GRAPHICS` (currently
- Cocoa) and a non-NULL graphics device; other backends return `ENOTSUP`. Native
- drawable queries are valid only for the current draw target and expire on
- successful present, callback return, or surface destruction.
-- `YASL_CAP_FRAME_PACING` distinguishes compositor/display-driven draws from
- X11's unpaced flush-driven draws. `yasl_timing.source` distinguishes submission
- estimates, frame callbacks, and confirmed presentation feedback. Refresh and
- next-presentation times are estimates, not universal vsync guarantees.
-
-These are pre-release API changes. Applications must handle the new reset input
-kind and should inspect timing provenance instead of assuming all timestamps
-confirm scanout. See `include/yasl/yasl.h` for the complete contracts.
-
-## Building
-
-Use a C23 compiler and Meson 1.6 or later.
-Build with Meson and Ninja:
-
-```sh
-meson setup build
-ninja -C build
-ninja -C build test
-```
-
-Linux builds include each backend whose dependencies are installed.
-Wayland needs `wayland-client`, `wayland-cursor`, and `xkbcommon`.
-X11 needs `x11`, `xi` (libXi), and `xkbcommon`.
-Use `-Dwayland=enabled` or `-Dx11=enabled` to require a backend.
-Use `-Dwayland=disabled` or `-Dx11=disabled` to omit a backend.
-At least one backend must be built.
-
-To run the Linux window tests, install Weston and Xwayland.
-Then run:
-
-```sh
-meson setup build-test -Dintegration_tests=true
-meson test -C build-test --print-errorlogs
-```
-
-The tests start isolated display servers.
-They test explicit requests, automatic selection, and fallback.
-They also test both backends in one process, callback-table copying, input
-payload ownership, queue growth, concurrent clipboard targets, and consistent
-frame/damage validation. Wayland IME ownership tests need no display server.
-For memory and undefined-behavior checks, configure a separate build with
-`-Db_sanitize=address,undefined`.
-On macOS, the same option runs native Cocoa window tests.
-These tests need a desktop session.
-
-## Tablet drawing example
-
-Build and run the same C application on Wayland, X11, or Cocoa:
+# YASL — Yet Another Surface Library
+
+YASL is a C desktop surface library for applications that own their renderer.
+It creates windows and supplies CPU pixel targets, scale-aware geometry, queued
+input, clipboard text, popups, surface drags, and optional frame pacing.
+It runs on Wayland, X11, and macOS Cocoa.
+
+A software-rendered editor, terminal, drawing tool, or custom interface can open
+a window and paint pixels without creating an OpenGL context or adopting a
+widget toolkit. YASL also exposes tablet pressure/tilt and IME composition.
+It does not provide fonts, widgets, audio, or a graphics engine. Native handles
+and Cocoa/Metal rendering are separate extensions.
+
+**Status:** pre-release 0.1; source and ABI compatibility are not yet guaranteed.
+Physical keys have portable names, committed text arrives as UTF-8, and scroll
+offsets use fractional X/Y line units. Read the [application guide](docs/guide.md)
+for platform capabilities and limits.
+
+## Start here
+
+- [Getting started](docs/getting-started.md): build, install, link, and run a window.
+- [Application guide](docs/guide.md): geometry, drawing, input, ownership, and backend limits.
+- [Public reference](include/yasl/yasl.h), [loop](include/yasl/loop.h),
+ [timing](include/yasl/timing.h), and [native extensions](include/yasl/native.h).
+ Run `doxygen Doxyfile` for a searchable HTML reference in `build-docs/html`.
+- [API policy](docs/api-policy.md), [migration notes](docs/migrating.md), and
+ [contributing](CONTRIBUTING.md).
+- [Tests, tablet example, and benchmarks](docs/testing.md).
+
+## Build a window
+
+Use a C23 compiler, Meson 1.6 or newer, and Ninja. Linux needs the development
+packages for Wayland (`wayland-client`, `wayland-cursor`, `xkbcommon`) or X11
+(`x11`, `xi`, `xkbcommon`, `xext`; optional `xrandr`). Cocoa requires macOS 14 or
+newer and the system SDK, with a Metal-capable device for window drawing.
```sh
meson setup build
meson compile -C build
-./build/yasl-tablet
-./build/yasl-tablet --backend wayland
-./build/yasl-tablet --backend x11
-# On macOS:
-./build/yasl-tablet --backend cocoa
-```
-
-Draw with the pen tip: pressure controls brush width. Flip to the eraser to
-remove ink. The window shows the selected backend, tool, contact state, raw
-pressure (0..65535), percentage, and tilt in degrees, with a live pressure bar,
-brush outline, and tilt vector. The system cursor is hidden so the brush overlay
-serves as the drawing cursor. Mouse drawing uses a fixed width. Press C or
-Space to clear and Escape to quit. Resizing and display scale changes preserve
-the drawing. `--smoke-test` presents one frame and exits for automated checks.
-
-[`examples/tablet.c`](examples/tablet.c) includes only `<yasl/yasl.h>` and the C
-standard library. It uses CPU framebuffers and `yasl_surface_input_poll()` on
-every backend. Tablet events arrive as `YASL_INPUT_MOUSE`, distinguished by
-`mouse.device == YASL_PTR_DEV_PEN` or `YASL_PTR_DEV_ERASER`; `mouse.pen` contains
-pressure and signed tilt. Button 0 is tip contact on every backend. Track tip
-press/release events: zero pressure alone does not mean release. Hover and side
-buttons do not paint, and focus loss, leave, or `YASL_INPUT_RESET` cancels a stroke.
-Stationary pressure changes arrive as motion events and update the brush too.
-
-`YASL_CAP_TABLET` indicates backend/protocol support, not whether a tablet is
-plugged in or which axes it has. Missing axes are zero. Wayland requires the
-compositor's `zwp_tablet_manager_v2`; X11 requires XInput 2 and driver-provided
-`Abs Pressure`/`Abs Tilt X`/`Abs Tilt Y` valuators. Cocoa handles both tablet mouse
-subtypes and native tablet point/proximity events. X11 identifies erasers by
-the Wacom tool property or the driver's device name. Hardware and driver
-support determine which readings are available. The example displays a message
-when the server has no tablet protocol and still allows mouse drawing.
-
-The test suite checks synthetic Wayland/XInput samples and the drawing behavior
-without hardware, including pressure-only changes, sparse axes, eraser input,
-hover, release, reset, and scale changes. Linux integration tests launch the
-example on isolated Wayland and X11 servers; macOS integration tests also check
-Cocoa tablet event translation. Physical tablet testing is still needed to
-validate a particular device/driver combination.
-
-For an end-to-end X11 pressure test without hardware, install Xorg's `dummy`
-video driver and `inputtest` input driver, then run:
-
-```sh
-meson configure build -Dx11_tablet_tests=true
-meson test -C build tablet-x11-native --print-errorlogs
+./build/yasl-hello
+meson test -C build --print-errorlogs
```
-This starts an isolated Xorg server with virtual pen and eraser devices, sends
-real driver samples, checks YASL's shared events through the example, and verifies
-its presented window pixels. It checks stationary pressure changes, release
-without valuators, and eraser identification through the driver's tool property.
-The test disables automatic physical input/GPU attachment and cleans up its
-server. Set `YASL_XORG_MODULE_PATH` to a comma-separated module search path if
-the test drivers are outside Xorg's default directories.
-
-## Benchmarks
-
-YASL includes a dedicated performance microbenchmark target (`yasl-benchmark`) evaluating CPU efficiency, memory footprint, cache line pressure, data structure operations, and window lifecycle operations across core hot paths (surface lookups, multi-output refresh rate scans, input queue expansions, ring buffer throughput, timer arm/disarm cycles, cold/warm window creation, single and bulk window teardowns, titlebar property updates, and multi-resolution dynamic resizing):
+Linux builds automatically include backends with available dependencies. Require
+one with `-Dwayland=enabled` or `-Dx11=enabled`; omit one with `disabled`. Runtime
+selection chooses the native session and allows fallback, or you can request an
+explicit backend with `yasl_display_spec.backend`.
-```sh
-# Run via meson test runner
-meson test -C build --benchmark --verbose
-
-# Or invoke the executable directly
-ninja -C build tests/yasl-benchmark
-./build/tests/yasl-benchmark
-```
-
-## Quick Example
+Include `<yasl/yasl.h>` for the core. The complete [hello.c](examples/hello.c)
+handles creation failures, drawing, dispatch, input draining, and cleanup.
+Its drawing callback fills every pixel before presenting:
```c
-#include <yasl/yasl.h>
-
-static bool running = true;
-
-static void on_draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
-{
- /* Present frame target */
- struct yasl_damage_list dmg = { .n = -1 };
- yasl_surface_present(s, &frame->target, &dmg);
-}
-
-static void on_close(struct yasl_surface *s, void *ud)
-{
- (void)s; (void)ud;
- running = false;
-}
-
-int main(void)
+static void draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud)
{
- struct yasl_display_spec dspec = { .app_id = "org.example.app" };
- struct yasl_display *d = yasl_display_open(NULL, &dspec);
-
- struct yasl_surface_spec sspec = {
- .title = "YASL Application",
- .size = { .x = 800, .y = 600 },
- };
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
- struct yasl_surface_cbs cbs = {
- .draw = on_draw,
- .close = on_close,
- };
- yasl_surface_set_callbacks(s, &cbs, NULL);
- yasl_surface_schedule_redraw(s);
-
- while (running) {
- yasl_display_dispatch(d, -1);
- struct yasl_input_event ev;
- while (yasl_surface_input_poll(s, &ev)) {
- if (ev.kind == YASL_INPUT_KEY && ev.key.action == YASL_KEY_PRESS) {
- if (ev.key.key == 0xff1b) /* Escape */
- running = false;
- }
- }
- yasl_display_flush(d);
+ (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. */
}
-
- yasl_surface_destroy(s);
- yasl_display_close(d);
- return 0;
}
```
+Toplevel creation sizes are logical pixels; drawing and input use device pixels.
+Targets are borrowed for one draw callback. Keep durable content in application
+memory, dispatch events, drain input queues, then flush scheduled drawing. These
+rules are explained with examples in the application guide.
+
## License
-MIT License.
+[MIT](LICENSE).