1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
# Getting started
YASL gives a C application desktop windows with CPU pixel targets, scale-aware
geometry, queued input, clipboard text, and optional frame pacing. It fits
software-rendered tools such as editors, terminals, drawing applications, and
custom interfaces that already have their own renderer. A window needs neither
an OpenGL context nor a graphics device supplied by the application.
The core headers expose no window system types. Native handles and Metal drawing
are separate opt-in extensions. YASL currently supports Linux (Wayland and X11)
and macOS (Cocoa); it does not provide widgets, font shaping, audio, an OpenGL
context API, or a Vulkan surface API. It is pre-release software; see the
[API policy](api-policy.md) before distributing a library that depends on its ABI.
## Build and run
Use a C23 compiler, Meson 1.6 or newer, and Ninja. GCC and Clang can build the library. On Linux install either backend's development dependencies:
| Backend | pkg-config dependencies |
| --- | --- |
| Wayland | `wayland-client`, `wayland-cursor`, `xkbcommon` |
| X11 | `x11`, `xi`, `xkbcommon`, `xext`; optional `xrandr` |
| Cocoa | macOS 14+ SDK with AppKit, Metal, and QuartzCore; Metal-capable device |
```sh
meson setup build
meson compile -C build
./build/yasl-hello
meson test -C build --print-errorlogs
```
Each Linux backend defaults to `auto`, meaning build it when its dependencies
are available. Require one with `-Dwayland=enabled` or `-Dx11=enabled`; omit one
with `-Dwayland=disabled` or `-Dx11=disabled`. At least one backend is required.
Cocoa is selected on macOS and requires no extra third-party dependencies.
Its minimum deployment target is macOS 14. Set `MACOSX_DEPLOYMENT_TARGET=14.0`
(or newer) before configuring if your toolchain defaults to an older target.
CPU pixel targets are presented through the system's Metal device; applications
do not need to create or manage that device themselves.
The complete beginner example is [hello.c](../examples/hello.c). It checks
construction failures, fills every pixel, presents inside `draw`, drains input,
responds to quit/close/lost, and destroys resources in order. The
[interactive window](../examples/simple_window.c) adds pointer input; the
[tablet example](../examples/tablet.c) demonstrates retained application content,
pressure, erasers, resizing, and scale changes.
## Link an application
Install the library and public headers into your chosen prefix:
```sh
meson setup build --prefix=/usr/local
meson compile -C build
meson install -C build
cc -std=c23 examples/hello.c -o hello $(pkg-config --cflags --libs yasl)
./hello
```
The prefix must be writable, and your compiler, pkg-config, and runtime loader
must search it. For a custom prefix, set `PKG_CONFIG_PATH` to its pkg-config
directory and configure the loader for its library directory. Static linking
uses `pkg-config --static --libs yasl` to include backend dependencies.
A Meson consumer uses the installed dependency:
```meson
project('my-app', 'c', default_options: ['c_std=c23'])
yasl = dependency('yasl', version: '>=0.1.0')
executable('my-app', 'main.c', dependencies: yasl)
```
YASL also declares `meson.override_dependency('yasl', ...)`, so the same consumer
can use a vendored Meson subproject through a fallback dependency. Public headers
can be included from C++11 or newer; the library itself builds as C23.
## The application cycle
1. Open a display with `yasl_display_open(NULL, &spec)`. NULL creates a loop owned
by the display. Supply your own loop only when you need shared loop ownership.
2. Create a toplevel with a positive **logical-pixel** size and install callbacks.
Zero-initialize every option and callback struct. Callback tables are copied;
userdata must remain alive. Registration can call back synchronously.
3. Schedule the initial redraw. `draw` receives a borrowed target. Fill every
pixel with premultiplied `0xAARRGGBB`, then present that exact target, once.
4. Dispatch, drain input for every live surface queue, update application state,
and flush. Flush runs scheduled drawing and sends native requests. Dispatch
may also invoke callbacks, including drawing during native live resize.
5. Destroy popups before parents, surfaces before displays, and displays before
any supplied loop. NULL destroy/close calls do nothing; freed pointers are
invalid for every operation.
```c
while (running) {
if (yasl_display_dispatch(display, -1) < 0) {
perror("yasl_display_dispatch");
break;
}
struct yasl_input_event event;
while (yasl_surface_input_poll(surface, &event)) {
/* Read the union member selected by event.kind. */
}
yasl_display_flush(display);
}
```
Zero timeout polls, positive timeout bounds the wait in milliseconds, and a
negative timeout waits for work. Undrained input makes dispatch return without
waiting. Use the return value only to detect failure, not to count input events.
If callbacks destroy surfaces, update your application registry immediately and
only poll surfaces that are still alive. Arrange display/loop cleanup after the
cycle ends; destroying them inside a callback is unsupported.
See the [application guide](guide.md) for drawing, input, popups, timers, and
platform limits, or the comments in the [public header](../include/yasl/yasl.h)
for each function's contract. To build the searchable HTML reference, install
Doxygen and run `doxygen Doxyfile` from the repository root. Open
`build-docs/html/index.html`; Doxygen is not needed to build the library.
|