summaryrefslogtreecommitdiff
path: root/include/yasl/yasl.h
blob: d68bcc9f29699253404cd640209369fa3389fcad (plain)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
/** @file YASL: desktop windows, input and events for application-owned rendering.
 *
 * Calls return 0 or a negative errno; constructors return NULL and set errno;
 * queries name their fallback. Errors: EINVAL bad argument, ENOTSUP not
 * supported here, EPERM wrong thread, ENODEV not initialized, ENOTCONN display
 * lost, EBUSY not allowed now, ESTALE needs a pointer press in progress.
 * Call from the thread that initialized YASL (the main thread on macOS) unless
 * a function says it is thread-safe. Positions and sizes are logical units;
 * multiply by the scale for pixels. Names with "pixel" are in pixels. */
#ifndef YASL_H
#define YASL_H
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "yasl/keycodes.h"
#if defined(__GNUC__)
#define YASL_API __attribute__((visibility("default")))
#else
#define YASL_API
#endif
#define YASL_VERSION_MAJOR 0
#define YASL_VERSION_MINOR 2
#define YASL_VERSION_PATCH 0
#ifdef __cplusplus
extern "C" {
#endif
typedef struct yasl_surface yasl_surface;
typedef struct yasl_event yasl_event;
typedef struct yasl_loop yasl_loop;

/* Lifecycle */
enum yasl_backend { YASL_BACKEND_AUTO, YASL_BACKEND_WAYLAND, YASL_BACKEND_X11, YASL_BACKEND_COCOA };
enum yasl_feature {
    YASL_FEATURE_POPUPS = 1, YASL_FEATURE_DRAG = 2, YASL_FEATURE_DRAG_CARRY = 4,
    YASL_FEATURE_PLACE_WINDOW = 8, YASL_FEATURE_PRIMARY_SELECTION = 16, YASL_FEATURE_TABLET = 32,
    YASL_FEATURE_TEXT_INPUT = 64, YASL_FEATURE_LOCKED_CURSOR = 128, YASL_FEATURE_OPENGL = 256,
    YASL_FEATURE_VULKAN = 512
};
/** Zero is the default: AUTO, no app ID, and yasl_wait_event delivers events.
 * app_id is a reverse-DNS name for window grouping and notifications. Set loop
 * and on_event together to run the display on an application loop: each
 * yasl_loop_step then delivers events to on_event, and yasl_wait_event fails
 * with EBUSY. */
struct yasl_options {
    enum yasl_backend backend;
    const char *app_id;
    yasl_loop *loop;
    void (*on_event)(void *userdata, const yasl_event *event);
    void *userdata;
};
/** Optional: the first surface initializes with defaults; NULL options too.
 * EINVAL when only one of loop and on_event is set; EPERM off the loop's
 * thread. EALREADY once initialized with the same options, EBUSY with others;
 * a failed initialization may be retried. */
YASL_API int yasl_init(const struct yasl_options *options);
/** Destroys every surface; on_event is not called again. EBUSY inside a loop
 * step or while a Vulkan surface exists. */
YASL_API int yasl_shutdown(void);
/** AUTO and 0 before initialization. */
YASL_API enum yasl_backend yasl_get_backend(void);
YASL_API unsigned yasl_get_features(void);

/* Surfaces */
enum yasl_surface_type {
    YASL_SURFACE_CPU,   /**< Draw into yasl_cpu_pixels. */
    YASL_SURFACE_GL,    /**< OpenGL through EGL; Linux only. */
    YASL_SURFACE_NATIVE /**< Draw with Vulkan, Metal or the native handles. */
};
/** Zero selects OpenGL 3.2 core (ES 3.0 with es), RGBA8, 24-bit depth, 8-bit stencil. */
struct yasl_gl_options {
    int major, minor, samples;
    bool es, compatibility, debug, srgb, no_depth_stencil;
    yasl_surface *share; /**< A GL surface whose objects the new context shares. */
};
struct yasl_surface_options {
    const char *title;
    int width, height; /**< Required. */
    enum yasl_surface_type type;
    bool undecorated, fixed_size, tearing;
    void *userdata;
    /** Called when the window needs drawing while the system blocks the event
     * loop, as during a macOS live resize; draw and present before returning.
     * REFRESH events cover every other case. Events stay queued meanwhile, so
     * apply the size from yasl_surface_get_info, already current here; one
     * WINDOW event reports the final size once the system's loop returns.
     * Waiting, creating and destroying fail with EBUSY. */
    void (*refresh)(yasl_surface *surface);
    struct yasl_gl_options gl;
};
/** Strings are copied. */
YASL_API yasl_surface *yasl_surface_create(const struct yasl_surface_options *options);
/** Also destroys its popups. NULL is a no-op. EBUSY while one has a Vulkan surface. */
YASL_API int yasl_surface_destroy(yasl_surface *surface);

enum yasl_state {
    YASL_STATE_FULLSCREEN = 1,
    YASL_STATE_MAXIMIZED = 2,
    YASL_STATE_ACTIVATED = 4,
    YASL_STATE_SUSPENDED = 8, /**< Hidden from view; skip drawing. */
    YASL_STATE_CSD = 16,      /**< No system title bar: draw your own. */
    YASL_STATE_TILED = 32
};
struct yasl_surface_info {
    int width, height, pixel_width, pixel_height;
    double scale;
    unsigned state; /**< YASL_STATE_* */
};
/** Zero sizes and scale 1 for NULL. */
YASL_API struct yasl_surface_info yasl_surface_get_info(const yasl_surface *surface);
YASL_API void *yasl_surface_get_userdata(const yasl_surface *surface);
YASL_API int yasl_surface_set_userdata(yasl_surface *surface, void *userdata);
YASL_API int yasl_surface_set_title(yasl_surface *surface, const char *title);

/** Window requests; the outcome arrives as a WINDOW event. RESTORE leaves
 * fullscreen and maximized. */
enum yasl_request { YASL_REQUEST_RESTORE, YASL_REQUEST_FULLSCREEN, YASL_REQUEST_MAXIMIZE, YASL_REQUEST_MINIMIZE };
YASL_API int yasl_surface_request(yasl_surface *surface, enum yasl_request request);
YASL_API int yasl_surface_request_size(yasl_surface *surface, int width, int height);
/** Asks for a REFRESH after the next frame is shown. A frame presented after
 * the request, in the same callback, is that frame, so an animation can ask
 * before it draws. With no frame in flight, the REFRESH comes one refresh
 * interval after the last present or REFRESH. Requests coalesce. Use it to
 * pace animation; draw at once for input. */
YASL_API int yasl_surface_request_frame(yasl_surface *surface);

enum yasl_cursor {
    YASL_CURSOR_ARROW, YASL_CURSOR_TEXT, YASL_CURSOR_LINK, YASL_CURSOR_CROSSHAIR, YASL_CURSOR_MOVE,
    YASL_CURSOR_RESIZE_NWSE, YASL_CURSOR_RESIZE_NESW, YASL_CURSOR_RESIZE_EW, YASL_CURSOR_RESIZE_NS,
    YASL_CURSOR_HIDDEN,
    /** Hidden and held in place while focused; POINTER events then report
     * motion only in dx/dy (FEATURE_LOCKED_CURSOR). */
    YASL_CURSOR_LOCKED
};
YASL_API int yasl_surface_set_cursor(yasl_surface *surface, enum yasl_cursor cursor);

enum yasl_text_purpose { YASL_TEXT_NORMAL, YASL_TEXT_PASSWORD, YASL_TEXT_NUMBER, YASL_TEXT_TERMINAL };
/** The focused text field, for input methods. text is UTF-8 around the caret,
 * selection offsets are bytes into it, and revision comes back in COMPOSITION
 * and TEXT_EDIT events. The caret rectangle places the candidate window. */
struct yasl_text_input {
    enum yasl_text_purpose purpose;
    const char *text;
    size_t length, selection_begin, selection_end;
    uint64_t revision;
    double caret_x, caret_y, caret_width, caret_height;
};
/** Copied. NULL turns input methods off. */
YASL_API int yasl_surface_set_text_input(yasl_surface *surface, const struct yasl_text_input *input);

enum yasl_edge { YASL_EDGE_TOP = 1, YASL_EDGE_BOTTOM = 2, YASL_EDGE_LEFT = 4, YASL_EDGE_RIGHT = 8 };
/** While a pointer or pen button is held: let the user move the window (edges 0)
 * or resize it from one edge or a corner (two adjacent edges). */
YASL_API int yasl_surface_begin_move_resize(yasl_surface *surface, unsigned edges);
YASL_API int yasl_surface_show_window_menu(yasl_surface *surface, double x, double y);
YASL_API int yasl_surface_bell(yasl_surface *surface);

/* Popups */
enum yasl_popup_adjust {
    YASL_ADJUST_SLIDE_X = 1, YASL_ADJUST_SLIDE_Y = 2, YASL_ADJUST_FLIP_X = 4, YASL_ADJUST_FLIP_Y = 8,
    YASL_ADJUST_RESIZE_X = 16, YASL_ADJUST_RESIZE_Y = 32
};
/** Placed against an anchor rectangle in the parent: anchor picks a point on it
 * and gravity the direction the popup grows, both as YASL_EDGE_* bits (0 is the
 * center). adjust keeps it on screen; 0 flips, then slides. */
struct yasl_popup_options {
    int width, height;
    double x, y, anchor_width, anchor_height;
    unsigned anchor, gravity, adjust;
    void *userdata;
};
/** A CPU surface. Create it while handling the input that opens it; it takes
 * the input grab and gets CLOSE when dismissed. */
YASL_API yasl_surface *yasl_popup_create(yasl_surface *parent, const struct yasl_popup_options *options);
YASL_API int yasl_popup_reposition(yasl_surface *popup, const struct yasl_popup_options *options);

/* Clipboard, drag and drop */
enum yasl_selection { YASL_CLIPBOARD, YASL_PRIMARY };
/** Offers copied UTF-8 text; call while handling input on one of your windows. */
YASL_API int yasl_clipboard_set(enum yasl_selection selection, const char *text);
/** The text arrives as a CLIPBOARD event on target, in request order. */
YASL_API int yasl_clipboard_request(yasl_surface *target, enum yasl_selection selection);
/** Premultiplied 0xAARRGGBB pixels, rows tightly packed; copied. */
struct yasl_drag_image {
    const uint32_t *pixels;
    int pixel_width, pixel_height, hotspot_x, hotspot_y;
};
/** While a pointer button is held on origin: drag it over YASL surfaces, which
 * get DRAG events. image may be NULL. */
YASL_API int yasl_drag_start(yasl_surface *origin, const struct yasl_drag_image *image);
YASL_API int yasl_drag_cancel(void);
/** During a drag: carry this toplevel with (x, y) under the pointer (FEATURE_DRAG_CARRY). */
YASL_API int yasl_drag_carry(yasl_surface *surface, double x, double y);
/** Move the toplevel so (x, y) is under the pointer (FEATURE_PLACE_WINDOW). */
YASL_API int yasl_surface_place_under_pointer(yasl_surface *surface, double x, double y);
/** Next local path in a DROP event's text/uri-list. Returns its length, or 0 at
 * the end. If capacity exceeds the length, writes it with a NUL and advances
 * cursor; otherwise changes nothing, so retry larger. Thread-safe. */
YASL_API size_t yasl_drop_next_path(const char **cursor, char *out, size_t capacity);

/* Events */
enum yasl_event_type {
    YASL_EVENT_NONE,
    YASL_EVENT_CLOSE,       /**< The user asked to close the window; destroy it to comply. */
    YASL_EVENT_QUIT,        /**< The system asked the app to quit; exit to comply. */
    YASL_EVENT_REOPEN,      /**< macOS: the app was activated with no window open. */
    YASL_EVENT_WINDOW,      /**< window: size, scale or state changed. It reports the
                             * current geometry, so changes made since the last
                             * one fold into it. */
    YASL_EVENT_REFRESH,     /**< Draw now: once sized, after every resize, and on expose. */
    YASL_EVENT_KEY,         /**< key; action is a yasl_key_action. */
    YASL_EVENT_TEXT,        /**< text: typed text to insert. */
    YASL_EVENT_COMPOSITION, /**< text: input method preedit to show at the caret,
                             * ime.begin..end its cursor; empty ends it. */
    YASL_EVENT_TEXT_EDIT,   /**< text: replaces bytes ime.begin..end of the
                             * yasl_text_input text with revision ime.revision. */
    YASL_EVENT_POINTER,     /**< pointer, x, y; action is a yasl_pointer_action. */
    YASL_EVENT_SCROLL,      /**< scroll, x, y. */
    YASL_EVENT_PEN,         /**< pen, x, y; action is a yasl_pen_action. */
    YASL_EVENT_INPUT_RESET, /**< Forget held keys, buttons and strokes. */
    YASL_EVENT_CLIPBOARD,   /**< text; action is a yasl_transfer. */
    YASL_EVENT_DROP,        /**< External data: x, y; action is a yasl_drop_action;
                             * text on RELEASE is a text/uri-list. */
    YASL_EVENT_DRAG,        /**< A yasl_drag_start drag: x, y; action is a yasl_drop_action.
                             * Each drag ends once: RELEASE on the surface it drops on,
                             * or CANCEL on its origin when it ends anywhere else. */
    /** The display server went away; action is a yasl_lost. Before the next
     * wait, or before returning to the loop, destroy Vulkan surfaces and detach
     * GL contexts on every thread; popups close. On RECONNECTING, DISPLAY_RESTORED
     * follows: recreate them (GL contexts are new). After FINAL, yasl_wait_event
     * fails with ENOTCONN once no event is left. */
    YASL_EVENT_DISPLAY_LOST,
    YASL_EVENT_DISPLAY_RESTORED
};
enum yasl_key_action { YASL_KEY_PRESS, YASL_KEY_RELEASE, YASL_KEY_REPEAT };
enum yasl_mod { YASL_MOD_SHIFT = 1, YASL_MOD_ALT = 2, YASL_MOD_CTRL = 4, YASL_MOD_SUPER = 8, YASL_MOD_CAPS = 16 };
enum yasl_pointer_action { YASL_POINTER_MOVE, YASL_POINTER_DOWN, YASL_POINTER_UP, YASL_POINTER_LEAVE };
enum yasl_button { YASL_BUTTON_PRIMARY, YASL_BUTTON_MIDDLE, YASL_BUTTON_SECONDARY, YASL_BUTTON_BACK, YASL_BUTTON_FORWARD };
#define YASL_BUTTON_BIT(button) (1u << (button))
enum yasl_pen_action { YASL_PEN_ENTER, YASL_PEN_LEAVE, YASL_PEN_MOVE, YASL_PEN_DOWN, YASL_PEN_UP, YASL_PEN_BUTTON, YASL_PEN_CANCEL };
enum yasl_pen_tool { YASL_PEN_UNKNOWN, YASL_PEN_PEN, YASL_PEN_ERASER, YASL_PEN_BRUSH, YASL_PEN_AIRBRUSH, YASL_PEN_PENCIL };
enum yasl_pen_axis {
    YASL_PEN_PRESSURE = 1, YASL_PEN_TILT = 2, YASL_PEN_ROTATION = 4, YASL_PEN_DISTANCE = 8,
    YASL_PEN_TANGENTIAL = 16, YASL_PEN_WHEEL = 32
};
enum yasl_scroll_source { YASL_SCROLL_UNKNOWN, YASL_SCROLL_WHEEL, YASL_SCROLL_FINGER, YASL_SCROLL_CONTINUOUS };
enum yasl_scroll_phase { YASL_SCROLL_PHASE_NONE, YASL_SCROLL_BEGIN, YASL_SCROLL_UPDATE, YASL_SCROLL_END };
enum yasl_transfer { YASL_TRANSFER_OK, YASL_TRANSFER_UNAVAILABLE, YASL_TRANSFER_FAILED };
/** CANCEL ends a drag that dropped on no YASL surface, or was canceled; x and y are 0. */
enum yasl_drop_action { YASL_DROP_OVER, YASL_DROP_LEAVE, YASL_DROP_RELEASE, YASL_DROP_CANCEL };
enum yasl_lost { YASL_LOST_RECONNECTING, YASL_LOST_FINAL };

/** Physical key, layout labels (codepoint unshifted, shifted; 0 if none) and the
 * native code (evdev, X11 keycode or macOS virtual key). Type with TEXT, not this. */
struct yasl_key_event {
    enum yasl_key key;
    uint32_t codepoint, shifted, native_code;
};
/** button changed on DOWN/UP; buttons are held YASL_BUTTON_BIT()s. */
struct yasl_pointer_event {
    double dx, dy;
    unsigned button, buttons;
};
/** Positive right/down; a delta is 0 when the device does not report that unit. */
struct yasl_scroll_event {
    double lines_x, lines_y, pixels_x, pixels_y;
    enum yasl_scroll_source source;
    enum yasl_scroll_phase phase;
    bool momentum;
};
/** pressure and distance 0..1, tangential -1..1, tilt and rotation in degrees.
 * axes holds the valid YASL_PEN_* axes; button changed on BUTTON. */
struct yasl_pen_event {
    uint64_t device_id, tool_id;
    double pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel_degrees;
    enum yasl_pen_tool tool;
    unsigned axes, buttons, button;
    bool in_contact;
};
struct yasl_ime_event {
    size_t begin, end;
    uint64_t revision;
};
struct yasl_event {
    enum yasl_event_type type;
    yasl_surface *surface;
    uint64_t time_ns;
    unsigned action, mods; /**< mods: YASL_MOD_* for KEY, POINTER and SCROLL. */
    double x, y;
    const char *text; /**< NUL-terminated UTF-8 until the next wait or until
                       * on_event returns, or NULL. */
    size_t length;
    union {
        struct yasl_surface_info window;
        struct yasl_key_event key;
        struct yasl_pointer_event pointer;
        struct yasl_scroll_event scroll;
        struct yasl_pen_event pen;
        struct yasl_ime_event ime;
    };
};
/** 1 with an event, 0 once timeout_ms passes or after yasl_wake. 0 polls and -1
 * waits forever. On failure out->type is NONE. EBUSY with on_event. */
YASL_API int yasl_wait_event(yasl_event *out, int timeout_ms);
/** Makes a waiting, or the next, yasl_wait_event return 0 once no event is
 * pending. With on_event, wakes the loop. Thread-safe. */
YASL_API void yasl_wake(void);
/** Monotonic nanoseconds, the clock of event times. Thread-safe. */
YASL_API uint64_t yasl_time_ns(void);

/* Loop: one wait for the whole application. Give it to yasl_init to run the
 * display on it; it also runs descriptors, timers and signals without one.
 * Call it and get its callbacks on the thread that created it. */
/** NULL with errno on failure. */
YASL_API yasl_loop *yasl_loop_create(void);
/** EBUSY while a step runs or the display runs on it. Leaves descriptors open. */
YASL_API int yasl_loop_destroy(yasl_loop *loop);
/** Waits at most timeout_ms (0 polls, -1 waits) and runs what is ready:
 * display events, then descriptors, then due timers, then signals. Returns
 * how many descriptor and timer callbacks ran. EBUSY inside a step; EPERM off
 * the loop's thread. */
YASL_API int yasl_loop_step(yasl_loop *loop, int timeout_ms);
/** Steps until yasl_loop_quit. A quit made before the run ends it at once. */
YASL_API int yasl_loop_run(yasl_loop *loop);
YASL_API void yasl_loop_quit(yasl_loop *loop);
/** Calls callback with the poll(2) revents, errors and hangup included; it
 * must consume them or change the watch. Returns an ID above zero. */
YASL_API int yasl_loop_add_fd(yasl_loop *loop, int fd, short events,
                              void (*callback)(void *userdata, int fd, short revents),
                              void *userdata);
/** ENOENT for an unknown ID. Leaves the descriptor open. */
YASL_API int yasl_loop_del_fd(yasl_loop *loop, int id);
YASL_API int yasl_loop_set_fd_events(yasl_loop *loop, int id, short events);
/** Calls callback after delay_ns, 0 meaning the next step. It returns the next
 * delay, or 0 to stop. Returns an ID that is never reused, or 0 with errno. */
YASL_API uint64_t yasl_loop_add_timer(yasl_loop *loop, uint64_t delay_ns,
                                      uint64_t (*callback)(void *userdata, uint64_t id,
                                                           uint64_t delay_ns),
                                      void *userdata);
/** True when it cancels an armed timer, also from its own callback. */
YASL_API bool yasl_loop_del_timer(yasl_loop *loop, uint64_t id);
/** Calls callback on the loop's thread for each signal; repeats before the
 * call coalesce. EBUSY when this loop already watches signals or another loop
 * watches one of them. Unwatching restores the previous actions. */
YASL_API int yasl_loop_watch_signals(yasl_loop *loop, const int *signals, int count,
                                     void (*callback)(void *userdata, int signo),
                                     void *userdata);
YASL_API void yasl_loop_unwatch_signals(yasl_loop *loop);

/* Rendering */
/** CPU surfaces: the window's pixel_width * pixel_height premultiplied
 * 0xAARRGGBB pixels, rows tightly packed. They persist, and a resize keeps the
 * overlap and zeroes the rest. Valid until the next wait, the return of the
 * callback that got them, or the next yasl_cpu_ call. Present to show them;
 * the system may also show them when it redraws the window. */
struct yasl_damage {
    int x, y, width, height;
};
YASL_API uint32_t *yasl_cpu_pixels(yasl_surface *surface, int *pixel_width, int *pixel_height);
YASL_API int yasl_cpu_present(yasl_surface *surface);
YASL_API int yasl_cpu_present_damage(yasl_surface *surface, const struct yasl_damage *damage, int count);

typedef void (*yasl_gl_proc)(void);
/** These four may run on a rendering thread; synchronize them with destruction.
 * NULL detaches the calling thread's context, also after DISPLAY_LOST. */
YASL_API int yasl_gl_make_current(yasl_surface *surface);
YASL_API yasl_gl_proc yasl_gl_get_proc_address(const char *name);
YASL_API int yasl_gl_set_swap_interval(yasl_surface *surface, int interval);
YASL_API int yasl_gl_present(yasl_surface *surface);

struct wl_display;
struct wl_surface;
struct _XDisplay;
/** Borrowed handles of the active backend; the rest are zero. They expire with
 * the surface or on DISPLAY_LOST. Never destroy, dispatch or close them. */
struct yasl_native {
    struct wl_display *wl_display;
    struct wl_surface *wl_surface;
    struct _XDisplay *x11_display;
    unsigned long x11_window;
    void *ns_window, *ns_view; /**< NSWindow *, NSView * */
};
YASL_API int yasl_surface_get_native(const yasl_surface *surface, struct yasl_native *out);
/** NATIVE surfaces on macOS: the view's CAMetalLayer, reserving the surface for
 * Metal. YASL sizes it; choose the device and format, and present yourself. */
YASL_API void *yasl_metal_get_layer(yasl_surface *surface);

/* Desktop */
enum yasl_appearance { YASL_APPEARANCE_DARK = 1, YASL_APPEARANCE_LIGHT = 2, YASL_APPEARANCE_HIGH_CONTRAST = 4 };
/** Opens uri with its default application. Thread-safe. */
YASL_API int yasl_open_uri(const char *uri);
YASL_API int yasl_notify(const char *title, const char *body);
/** These two may block briefly on the session bus. 0 is no preference. */
YASL_API unsigned yasl_get_appearance(void);
/** Milliseconds; 400 when unknown. */
YASL_API int yasl_get_double_click_ms(void);
/** LOCALE is a POSIX name such as en_US; RESOURCE_DIR is macOS only. */
enum yasl_system_string { YASL_EXECUTABLE_PATH, YASL_RESOURCE_DIR, YASL_LOCALE };
/** Returns the length, or 0 with errno. Writes it with a NUL only if capacity
 * exceeds the length. Thread-safe. */
YASL_API size_t yasl_get_system_string(enum yasl_system_string which, char *out, size_t capacity);
#ifdef __cplusplus
}
#endif
#endif