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
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
|
/**
* @file
* Portable desktop surfaces, CPU drawing, input, and display dispatch.
*
* All calls and callbacks use the display's opening thread (the main thread on
* Cocoa), unless explicitly documented otherwise. Userdata is borrowed; callback
* tables and creation specs are copied. Do not recursively dispatch/flush or
* destroy a display/loop while dispatch or a callback is active. Destroy child
* popups before parents, surfaces before displays, displays before borrowed loops.
* Surface destruction from its own callback is supported; stop using it immediately.
*
* Zero-initialize option and callback structs before setting fields. Pointers to
* destroyed objects are invalid, even for getters and destroy functions. NULL
* handling is documented per function; it does not make freed pointers safe.
* Only functions explicitly documenting errno guarantee a diagnostic there.
* Other boolean results report success/availability; void setters are best effort.
*
* Toplevel creation sizes use logical pixels. Geometry, framebuffers, pointer
* positions, damage, IME carets, popup specs, and drag offsets use device pixels.
* Coordinates have their origin at the upper left; x grows right and y grows down.
* Scale is an integer ratio with YASL_SCALE_UNIT (120) representing 1x.
* See the application guide for the dispatch/poll/flush cycle and backend limits.
*/
#ifndef YASL_H
#define YASL_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include <float.h>
#include <math.h>
#include "yasl/timing.h"
#include "yasl/keycodes.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Public shared-library symbol visibility annotation. */
#if defined(_WIN32) || defined(__CYGWIN__)
#if defined(YASL_BUILD_SHARED)
#define YASL_API __declspec(dllexport)
#elif defined(YASL_SHARED)
#define YASL_API __declspec(dllimport)
#else
#define YASL_API
#endif
#elif defined(__GNUC__) && __GNUC__ >= 4
#define YASL_API __attribute__((visibility("default")))
#else
#define YASL_API
#endif
struct yasl_display;
struct yasl_surface;
struct yasl_loop;
/* Geometry */
/** A two-dimensional integer size, position, or offset; units depend on the caller. */
struct yasl_vector2i {
/** Horizontal coordinate or width. */
int x;
/** Vertical coordinate or height. */
int y;
};
/** Two-dimensional fractional offset; units depend on the containing structure. */
struct yasl_vector2d {
/** Horizontal component. */
double x;
/** Vertical component. */
double y;
};
/** Integer rectangle; origin is upper left and extents must be nonnegative. */
struct yasl_rect2i {
/** Left coordinate. */
int x;
/** Top coordinate. */
int y;
/** Nonnegative horizontal extent. */
int width;
/** Nonnegative vertical extent. */
int height;
};
/** Scale numerator representing 1x. */
#define YASL_SCALE_UNIT 120
/** Backend minimum logical dimension used when constraining toplevel sizes. */
#define YASL_MIN_LOGICAL_PX 64
/* Nearest integer, ties away from zero. Zero scale returns zero; finite
* out-of-range results saturate. Non-finite logical coordinates return zero. */
/** Convert a logical coordinate to device pixels, rounding ties away from zero.
* @param logical Logical coordinate; non-finite values return zero.
* @param scale_120 Scale numerator; zero returns zero.
* @return Rounded device coordinate, saturated to the range of int.
*/
static inline int yasl_scale_to_device(double logical, uint32_t scale_120)
{
if (!scale_120 || !(logical >= -DBL_MAX && logical <= DBL_MAX)) return 0;
double px = (scale_120 == YASL_SCALE_UNIT) ? logical : (logical * ((double)scale_120 / YASL_SCALE_UNIT));
if (px >= INT_MAX) return INT_MAX;
if (px <= INT_MIN) return INT_MIN;
return (int)(px + copysign(0.5, px));
}
/** Convert a device coordinate to logical pixels, rounding ties away from zero.
* @param device Device coordinate.
* @param scale_120 Scale numerator; zero returns zero.
* @return Rounded logical coordinate, saturated to the range of int.
*/
static inline int yasl_scale_to_logical(int device, uint32_t scale_120)
{
if (!scale_120) return 0;
if (scale_120 == YASL_SCALE_UNIT) return device;
int64_t px = (int64_t)device * YASL_SCALE_UNIT;
int64_t half = (int64_t)(scale_120 / 2);
int64_t bias = (px < 0) ? -half : half;
px = (px + bias) / scale_120;
if (px > INT_MAX) return INT_MAX;
if (px < INT_MIN) return INT_MIN;
return (int)px;
}
/* Capabilities */
/** Optional backend/server features, returned as a bitmask by yasl_display_caps(). */
enum yasl_display_cap : uint32_t {
YASL_CAP_POPUPS = 1 << 0, /**< Popup creation and placement. */
YASL_CAP_DRAG = 1 << 1, /**< Surface-drag source and target support. */
YASL_CAP_DRAG_CARRY = 1 << 2, /**< Carry a toplevel during a drag. */
YASL_CAP_PLACE_WINDOW = 1 << 3, /**< Place a window relative to the pointer. */
YASL_CAP_PRIMARY_SELECTION = 1 << 4, /**< Separate primary selection. */
YASL_CAP_CSD = 1 << 5, /**< Backend advertises client decoration support; check each action result. */
YASL_CAP_NATIVE_GRAPHICS = 1 << 6, /**< Application-managed native rendering. */
YASL_CAP_FRAME_PACING = 1 << 7, /**< Compositor/display-driven draws. */
/* Backend/protocol supports tablet axes, independent of attached hardware. */
YASL_CAP_TABLET = 1 << 8, /**< Tablet protocol support, independent of attached hardware. */
};
/** Independent selections. An unsupported selection never aliases the clipboard. */
enum yasl_selection : uint8_t {
YASL_CLIPBOARD, /**< Explicit copy/paste selection. */
YASL_PRIMARY, /**< Independent primary selection, if supported. */
};
/** Reported surface state flags; absent flags may also mean unsupported state. */
enum yasl_surface_state : uint32_t {
YASL_STATE_FULLSCREEN = 1 << 0, /**< Fullscreen. */
YASL_STATE_MAXIMIZED = 1 << 1, /**< Maximized. */
YASL_STATE_ACTIVATED = 1 << 2, /**< Active window. */
YASL_STATE_SUSPENDED = 1 << 3, /**< Compositor reports suspension. */
YASL_STATE_CSD = 1 << 4, /**< Backend reports client-side decorations. */
YASL_STATE_TILED = 1 << 5, /**< At least one edge is tiled; currently reported by Wayland. */
};
/** System cursor shapes. HIDDEN preserves pointer input; NONE is its compatibility alias. */
enum yasl_cursor {
YASL_CURSOR_ARROW, /**< Default arrow. */
YASL_CURSOR_TEXT, /**< Text insertion. */
YASL_CURSOR_LINK, /**< Link/pointer hand. */
YASL_CURSOR_CROSSHAIR, /**< Crosshair. */
YASL_CURSOR_MOVE, /**< Move in any direction. */
YASL_CURSOR_RESIZE_NWSE, /**< Northwest/southeast resize. */
YASL_CURSOR_RESIZE_NESW, /**< Northeast/southwest resize. */
YASL_CURSOR_RESIZE_EW, /**< Horizontal resize. */
YASL_CURSOR_RESIZE_NS, /**< Vertical resize. */
YASL_CURSOR_HIDDEN, /**< Hidden system cursor; pointer input continues. */
YASL_CURSOR_NONE = YASL_CURSOR_HIDDEN, /**< Compatibility alias for HIDDEN. */
};
/** Valid interactive resize edges; combinations are restricted to the named values. */
enum yasl_resize_edge : uint32_t {
YASL_EDGE_NONE = 0, /**< No resize edge; rejected by interactive resize. */
YASL_EDGE_TOP = 1, /**< Top edge. */
YASL_EDGE_BOTTOM = 2, /**< Bottom edge. */
YASL_EDGE_LEFT = 4, /**< Left edge. */
YASL_EDGE_TOP_LEFT = 5, /**< Top-left corner. */
YASL_EDGE_BOTTOM_LEFT = 6, /**< Bottom-left corner. */
YASL_EDGE_RIGHT = 8, /**< Right edge. */
YASL_EDGE_TOP_RIGHT = 9, /**< Top-right corner. */
YASL_EDGE_BOTTOM_RIGHT = 10, /**< Bottom-right corner. */
};
/* Damage & Framebuffer
* CPU pixels are tightly packed premultiplied ARGB8888 (0xAARRGGBB).
* Size, pointer positions, IME caret and damage use device pixels; surface
* toplevel creation sizes use logical pixels. Damage is a hint: backends may upload
* the whole frame. Every pixel must contain valid content on each draw.
*/
/** Maximum number of damage rectangles in one presentation. */
#define YASL_DAMAGE_MAX 64
/** Damage hints for one presentation; every target still needs complete valid pixels. */
struct yasl_damage_list {
/** -1: full target; 0: no damaged rectangles; otherwise number of rects. */
int n;
/** Reserved hint; callers may leave zero. Core presentation uses rects. */
struct yasl_rect2i bounds;
/** First n rectangles, in device pixels; ignored for n == -1. */
struct yasl_rect2i rects[YASL_DAMAGE_MAX];
};
/** Borrowed draw target, valid only for the current draw callback. Never free pixels. */
struct yasl_framebuffer {
/** Opaque draw identity. Preserve it and all target fields when copying. */
uint64_t generation;
/** Writable premultiplied ARGB8888, row stride size.x * sizeof(uint32_t); may be NULL for native rendering. */
uint32_t *pixels;
/** Target extent in device pixels. */
struct yasl_vector2i size;
};
/** One draw invocation. Do not retain its pointer after the callback returns. */
struct yasl_frame {
/** Surface being drawn. */
struct yasl_surface *surface;
/** Target to initialize and present before returning. */
struct yasl_framebuffer target;
};
/* Drag and Drop */
/** Target notifications for YASL surface drags, separate from external file drops. */
enum yasl_drag_event {
YASL_DRAG_OVER, /**< Surface drag entered or moved over the target. */
YASL_DRAG_LEAVE, /**< Surface drag left the target. */
YASL_DRAG_DROP, /**< Surface drag dropped on the target. */
};
/** External text/file drag hover notifications; completed data arrives through input. */
enum yasl_drop_event {
YASL_DROP_OVER, /**< External data drag entered or moved over the target. */
YASL_DROP_LEAVE, /**< External data drag left the target. */
};
/** Source-side drag completion; DROPPED does not prove application-level acceptance. */
enum yasl_drag_end {
YASL_DRAG_DROPPED, /**< Drag ended in a drop; not an application acceptance receipt. */
YASL_DRAG_CANCELLED, /**< Drag canceled, including display close. */
};
/** Copied drag icon in tightly packed premultiplied 0xAARRGGBB device pixels. */
struct yasl_drag_image {
/** Copied pixel array; required when an image is supplied. */
const uint32_t *pixels;
/** Positive width and height in device pixels. */
struct yasl_vector2i size;
/** Pointer position within the image, in device pixels. */
struct yasl_vector2i hotspot;
};
/** Copied drag completion listener; userdata remains owned by the application. */
struct yasl_drag_listener {
/** Called exactly once after a successful start, possibly synchronously on cancel/close. */
void (*end)(void *ud, enum yasl_drag_end how);
};
/** Configured device-pixel content size and the corresponding display scale. */
struct yasl_geometry {
/** Current content extent in device pixels. */
struct yasl_vector2i size;
/** Positive scale numerator; 120 means 1x and 180 means 1.5x. */
uint32_t scale_120;
};
/* Input Model */
/** Keyboard press, release, or autorepeat; releases must not insert text. */
enum yasl_key_action {
YASL_KEY_PRESS, /**< Key pressed. */
YASL_KEY_RELEASE, /**< Key released. */
YASL_KEY_REPEAT, /**< Key autorepeat. */
};
/** Modifier bitmask. SUPER is Command on macOS. */
enum yasl_mod : uint32_t {
YASL_MOD_SHIFT = 1 << 0, /**< Shift modifier. */
YASL_MOD_ALT = 1 << 1, /**< Alt/Option modifier. */
YASL_MOD_CTRL = 1 << 2, /**< Control modifier. */
YASL_MOD_SUPER = 1 << 3, /**< Super/Command modifier. */
YASL_MOD_CAPS = 1 << 4, /**< Caps Lock modifier. */
};
/** Pointer source class; capability support does not imply an attached device. */
enum yasl_pointer_device {
YASL_PTR_DEV_MOUSE, /**< Mouse or trackpad pointer. */
YASL_PTR_DEV_TOUCH, /**< Touch translated into pointer input. */
YASL_PTR_DEV_PEN, /**< Tablet stylus. */
YASL_PTR_DEV_ERASER, /**< Tablet eraser tool. */
};
/** Pointer button; meaningful only for POINTER_DOWN and POINTER_UP. */
enum yasl_pointer_button {
YASL_BUTTON_PRIMARY = 0, /**< Left button, pen tip, or touch contact. */
YASL_BUTTON_MIDDLE = 1, /**< Middle button / wheel click. */
YASL_BUTTON_SECONDARY = 2, /**< Right button or pen barrel button. */
YASL_BUTTON_BACK = 3, /**< Side "back" button. */
YASL_BUTTON_FORWARD = 4, /**< Side "forward" button. */
};
/** Bit for a yasl_pointer_button in yasl_pointer_event.buttons. */
#define YASL_BUTTON_BIT(button) (1u << (button))
/** Pointer capture mode for a surface. */
enum yasl_pointer_mode {
YASL_POINTER_NORMAL = 0, /**< Absolute positions; cursor per yasl_surface_set_cursor(). */
/** Cursor hidden and locked in place while the surface has focus. POINTER_MOTION
* reports unbounded relative motion in delta; pos stays at the lock position.
* The lock is released on focus loss and re-acquired on focus gain. */
YASL_POINTER_RELATIVE,
};
/** Normalized tablet axes; zero for unavailable axes and non-tablet devices. */
struct yasl_pointer_pen {
/** 0..65535 normalized to device range. Zero alone does not end contact. */
uint16_t pressure;
/** Degrees in -90..90; positive right. */
int8_t tilt_x;
/** Degrees in -90..90; positive down. */
int8_t tilt_y;
};
/** Event tag. Each tag lists the union member it selects. */
enum yasl_event_type {
YASL_EVENT_NONE = 0,
/* Input: produced by backends, returned by yasl_surface_poll_event(). */
YASL_EVENT_KEY, /**< .key */
YASL_EVENT_TEXT, /**< .text — committed UTF-8; insert this, not key codepoints. */
YASL_EVENT_POINTER_DOWN, /**< .pointer — button/tip pressed; button valid. */
YASL_EVENT_POINTER_UP, /**< .pointer — button/tip released; button valid. */
YASL_EVENT_POINTER_MOTION, /**< .pointer — position, relative motion, or pen axes changed. */
YASL_EVENT_POINTER_SCROLL, /**< .pointer — scroll valid. */
YASL_EVENT_POINTER_LEAVE, /**< .pointer — last known pos/device; cancel hover state. */
YASL_EVENT_FOCUS, /**< .focus — keyboard focus changed; cancel held state on loss. */
YASL_EVENT_PASTE, /**< .text — UTF-8 from yasl_display_paste(). */
YASL_EVENT_DROP, /**< .text — dropped text/uri-list: CRLF-separated file URLs, not paths. */
YASL_EVENT_INPUT_RESET, /**< No payload. Input stream interrupted; clear held keys, buttons, strokes, IME. */
/* Window and application: delivered only through yasl_app_desc.event().
* Low-level users receive the same information via yasl_window_cbs. */
YASL_EVENT_WINDOW_RESIZE, /**< .resize — size and/or scale changed; reallocate and redraw. */
YASL_EVENT_WINDOW_CLOSE, /**< No payload. User asked to close the window. */
YASL_EVENT_USER, /**< .user — posted with yasl_post_event(). */
};
/** Physical keyboard transition with optional layout labels.
* key is independent of modifiers/layout and stable between press and release.
* codepoint and shifted are optional single Unicode scalars for the current
* layout at levels zero/one, not text input. Zero means unavailable, a control
* key, or a layout mapping that produces multiple characters. Insert committed
* YASL_EVENT_TEXT into documents instead. */
struct yasl_key_event {
/** Portable physical key; YASL_KEY_UNKNOWN when unmapped. */
enum yasl_key key;
/** Press, release, or autorepeat. */
enum yasl_key_action action;
/** yasl_mod bitmask at this event. */
uint32_t mods;
/** Optional unshifted layout Unicode scalar; zero if unavailable. */
uint32_t codepoint;
/** Optional shifted layout Unicode scalar; zero if unavailable. */
uint32_t shifted;
/** Native code: Wayland evdev, X11 server keycode, Cocoa virtual keycode. */
uint32_t native_code;
};
/** Pointer sample shared by all POINTER_* events. Pen and eraser share it with
* mouse input. Track contact with DOWN/UP of the primary button, not pressure.
* This is not a multi-contact touch API. */
struct yasl_pointer_event {
/** Surface-relative device-pixel position. Frozen while YASL_POINTER_RELATIVE is locked. */
struct yasl_vector2i pos;
/** MOTION: device-pixel movement since the previous sample on this surface.
* Unbounded under YASL_POINTER_RELATIVE; zero after enter, leave, or reset. */
struct yasl_vector2d delta;
/** SCROLL: fractional line offsets after system preferences; positive right/down. */
struct yasl_vector2d scroll;
/** DOWN/UP: which button changed. */
enum yasl_pointer_button button;
/** YASL_BUTTON_BIT() mask of buttons held after this event, on every
* POINTER_* type. Tracked from this queue's DOWN and UP events for all
* devices; INPUT_RESET clears it. On Wayland LEAVE clears it too, since the
* compositor sends leave during a press only when it takes the pointer.
* X11 and Cocoa keep sending motion and the release after LEAVE. */
uint32_t buttons;
/** Mouse, touch, pen, or eraser. */
enum yasl_pointer_device device;
/** yasl_mod bitmask at this event. */
uint32_t mods;
/** Current tablet axes, including on pressure-only motion; missing axes are zero. */
struct yasl_pointer_pen pen;
};
/** Borrowed NUL-terminated UTF-8, shared by TEXT, PASTE and DROP. */
struct yasl_text_event {
/** Borrowed NUL-terminated bytes; see yasl_surface_poll_event() for lifetime. */
const char *data;
/** Length in bytes excluding the terminating NUL. */
size_t len;
};
/** Keyboard focus transition. */
struct yasl_focus_event {
/** true on focus gain, false on loss. */
bool focused;
};
/** New configured geometry for a surface. */
struct yasl_resize_event {
/** Content size in device pixels and display scale. */
struct yasl_geometry geometry;
/** true when scale_120 differs from the previous resize of this surface. */
bool scale_changed;
};
/** Application message posted with yasl_post_event(). */
struct yasl_user_event {
/** Application-defined message code. */
uint32_t code;
/** Pointer passed through unchanged; ownership stays with the application. */
void *payload;
/** Optional payload size, passed through unchanged. */
size_t size;
};
/** One tagged event; read only the union member selected by type. */
struct yasl_event {
/** Selects the valid union member. */
enum yasl_event_type type;
/** Recipient surface, or NULL for display-wide events. Can differ from the
* polled surface for routed popup input. */
struct yasl_surface *surface;
/** Monotonic timestamp in nanoseconds, same clock as yasl_loop_now_ns(). */
uint64_t time_ns;
union {
struct yasl_key_event key; /**< KEY */
struct yasl_pointer_event pointer; /**< POINTER_* */
struct yasl_text_event text; /**< TEXT, PASTE, DROP */
struct yasl_focus_event focus; /**< FOCUS */
struct yasl_resize_event resize; /**< WINDOW_RESIZE */
struct yasl_user_event user; /**< USER */
};
};
/* Ownership and threading
* All calls and callbacks run on the display's opening thread (main thread on
* Cocoa). Displays borrow supplied loops; destroy surfaces before displays,
* popups before parents, and displays before their supplied loop.
* Specs, strings, callback tables and drag listeners are copied during calls.
* Userdata is borrowed until callbacks are removed or the object/drag ends.
* Surface destruction is allowed from its callbacks; display/loop destruction
* during dispatch or callbacks is not supported.
*/
/* Callbacks */
/** Optional display notifications. NULL callback members are ignored. */
struct yasl_display_cbs {
/** Application quit request; arrange orderly shutdown after dispatch returns. */
void (*quit)(void *ud);
/** Application reopen/activation request, currently Cocoa. */
void (*reopen)(void *ud);
/** Display connection lost; arrange cleanup after dispatch returns. */
void (*lost)(void *ud);
};
/** Optional window/popup notifications, copied from yasl_window_spec.cbs or by
yasl_surface_set_callbacks().
Callbacks may destroy their surface. They must then return without further use
of the surface or draw target. NULL members are ignored. */
struct yasl_window_cbs {
/** Initialize the entire target and optionally present. Requested through schedule_redraw/flush. */
void (*draw)(struct yasl_surface *s, struct yasl_frame *frame, void *ud);
/** Configured size/scale changed; reallocate application content and request a redraw. */
void (*configure)(struct yasl_surface *s, struct yasl_geometry geometry, void *ud);
/** Window/popup close request; application is responsible for destruction. */
void (*close)(struct yasl_surface *s, void *ud);
/** Reported yasl_surface_state bitmask changed; matches yasl_surface_state(s). */
void (*state)(struct yasl_surface *s, uint32_t state, void *ud);
/** YASL surface-drag target hover, leave, or drop; pos is in device pixels. */
void (*drag)(struct yasl_surface *s, enum yasl_drag_event ev,
struct yasl_vector2i pos, void *ud);
/** External data-drag hover or leave; pos is in device pixels. */
void (*drop)(struct yasl_surface *s, enum yasl_drop_event ev,
struct yasl_vector2i pos, void *ud);
};
/* Display Lifecycle */
/** Runtime backend choice. AUTO selects a native backend and allows fallback. */
enum yasl_backend {
YASL_BACKEND_AUTO = 0, /**< Choose native backend with fallback. */
YASL_BACKEND_WAYLAND, /**< Require Wayland. */
YASL_BACKEND_X11, /**< Require X11. */
YASL_BACKEND_COCOA, /**< Require Cocoa. */
};
/** Display options. Zero-initialize for automatic backend selection. */
struct yasl_display_spec {
/** Copied application identifier, conventionally a reverse-domain UTF-8 string; NULL uses backend defaults. */
const char *app_id;
/** Copied optional display callbacks; NULL disables them. */
const struct yasl_display_cbs *cbs;
/** Borrowed display callback userdata. */
void *ud;
/** Zero/AUTO chooses native backend; explicit choices never fall back. */
enum yasl_backend backend;
};
/* Return NULL and set errno if the requested backend cannot open. */
/**
* Open a desktop connection and its event machinery.
* @param loop Borrowed loop, or NULL to create a display-owned loop.
* @param spec Copied options, or NULL for automatic backend selection and defaults.
* @return An owned display, or NULL with errno set. EINVAL means an invalid backend;
* ENOSYS means the requested backend was not built; ENODEV is the fallback when
* opening fails without a more specific platform error. Explicit choices never
* fall back. Close with yasl_display_close() after destroying its surfaces.
*/
YASL_API struct yasl_display *yasl_display_open(struct yasl_loop *loop,
const struct yasl_display_spec *spec);
/**
* Read the selected backend.
* @param d Display, or NULL.
* @return The actual backend, or YASL_BACKEND_AUTO for NULL.
*/
YASL_API enum yasl_backend yasl_display_backend(const struct yasl_display *d);
/**
* Close a display and release its owned loop, if any.
* @param d Display to close, or NULL for no action.
* Destroy all surfaces first. Cancels an active drag and may invoke its end
* listener. Must not be called during dispatch or from a callback.
*/
YASL_API void yasl_display_close(struct yasl_display *d);
/**
* Query currently available optional features.
* @param d Display, or NULL.
* @return A bitmask of yasl_display_cap values, or zero for NULL.
* Capabilities describe backend/server support, not attached input hardware or
* permission to perform an operation at any arbitrary moment.
*/
YASL_API uint32_t yasl_display_caps(const struct yasl_display *d);
/* Process events, waiting at most timeout_ms: zero polls, negative waits.
* Pending input or redraw work returns immediately. Returns -1 on failure. */
/**
* Receive events and run ready loop sources.
* @param d Display to dispatch.
* @param timeout_ms Maximum wait in milliseconds: zero polls, negative waits indefinitely.
* @return A nonnegative backend-specific work count on success, or -1 with errno
* set on failure (EINVAL for NULL, EIO if no specific backend error is available).
* Do not interpret the count as the number of queued input events. Pending input
* or runnable redraw work prevents waiting. Drain input and call
* yasl_display_flush() after updating application state. Do not dispatch recursively.
*/
YASL_API int yasl_display_dispatch(struct yasl_display *d, int timeout_ms);
/**
* Draw scheduled surfaces and send pending display requests.
* @param d Display, or NULL for no action.
* May synchronously invoke draw and other callbacks. A paced surface may wait
* for its next display/compositor signal. This is not a GPU completion wait.
*/
YASL_API void yasl_display_flush(struct yasl_display *d);
/**
* Read the latest modifier state known to the backend.
* @param d Display, or NULL.
* @return A bitmask of yasl_mod values, or zero for NULL.
* For an individual event use its own mods field instead.
*/
YASL_API int yasl_display_modifiers(const struct yasl_display *d);
/** Double-click interval used when the system reports none. */
#define YASL_DOUBLE_CLICK_MS_DEFAULT 400
/**
* Read the system's double-click interval.
* @param d Display, or NULL.
* @return Maximum milliseconds between the clicks of a double click, or
* YASL_DOUBLE_CLICK_MS_DEFAULT when d is NULL or the system reports none.
* X11 reads the XSETTINGS Net/DoubleClickTime value. Wayland, and X11 without
* XSETTINGS, read the KDE or GNOME setting through the XDG Settings portal,
* which needs D-Bus and can wait briefly. Cocoa reads the system interval.
* The user can change it at any time; read it again when a window gains focus
* rather than on every click.
*/
YASL_API int yasl_display_double_click_ms(const struct yasl_display *d);
/* Unsupported/invalid selections are no-ops, never clipboard aliases. */
/**
* Offer a copy of a NUL-terminated UTF-8 string as a selection.
* @param d Display, or NULL for no action.
* @param sel YASL_CLIPBOARD or YASL_PRIMARY.
* @param text Text copied during this call; NULL does nothing.
* Unsupported selections do nothing. The void return does not confirm that the
* system accepted the offer; Wayland may require a recent input serial.
*/
YASL_API void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text);
/**
* Request text delivery to a particular surface.
* @param d Display that owns target.
* @param sel YASL_CLIPBOARD or YASL_PRIMARY.
* @param target Destination surface; NULL or a surface on another display does nothing.
* Results arrive as YASL_EVENT_PASTE, possibly during a later dispatch. No result
* is promised for unavailable data or a failed transfer. Destroying target cancels
* delivery. Unsupported selections do nothing.
*/
YASL_API void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target);
/* A successful drag invokes the copied listener's non-NULL end once, including
* cancel and display close. Failed starts do not invoke it. */
/**
* Start a surface drag from a recent pointer press.
* @param d Display that owns origin.
* @param origin Origin surface.
* @param image Optional copied premultiplied ARGB8888 drag image, or NULL.
* @param l Optional copied completion listener, or NULL.
* @param ud Borrowed listener userdata until completion.
* @return true if started; false if unsupported, invalid, or not currently permitted.
* A successful start invokes a non-NULL end callback exactly once, including
* cancellation or display close. Failed starts do not invoke it. This API carries
* surfaces, not arbitrary MIME payloads; use application state to identify the item.
*/
YASL_API bool yasl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin,
const struct yasl_drag_image *image,
const struct yasl_drag_listener *l, void *ud);
/**
* Cancel the active surface drag, if any.
* @param d Display, or NULL for no action.
* May invoke the end listener synchronously.
*/
YASL_API void yasl_display_drag_cancel(struct yasl_display *d);
/* Surface Lifecycle */
/** Toplevel options. Zero-initialize, then supply a positive logical-pixel size. */
struct yasl_window_spec {
/** Copied UTF-8 title; NULL uses backend defaults. */
const char *title;
/** Requested logical-pixel width and height; both must be positive. */
struct yasl_vector2i size;
/** Request initial fullscreen; actual state is reported asynchronously. */
bool fullscreen;
/** Permit unpaced presentation where supported; a hint, not a guarantee. */
bool tearing;
/** Copied optional window callbacks; NULL disables them. */
const struct yasl_window_cbs *cbs;
/** Borrowed window callback userdata. */
void *ud;
};
/** Popup options, entirely in device pixels, including size.
The content rectangle excludes transparent margins/shadows used for placement.
An axis of content_size equal to zero extends to that axis of size. The offset
and resolved content size must fit in size. Anchor extents must be nonnegative.
Dimensions must be positive, at most 32767, and fit an INT32_MAX-byte buffer;
backends may impose further limits after scaling or resolving placement. */
struct yasl_popup_spec {
/** Full popup framebuffer extent in device pixels, including transparent margins. */
struct yasl_vector2i size;
/** Rectangle in parent framebuffer coordinates, in device pixels. */
struct yasl_rect2i anchor;
/** true prefers right of anchor; false prefers below. Placement may flip/slide. */
bool beside;
/** Top-left content offset within the popup, in device pixels. */
struct yasl_vector2i content_offset;
/** Content extent; zero per axis extends to the corresponding size edge. */
struct yasl_vector2i content_size;
};
/**
* Create a resizable desktop window with CPU drawing targets.
* @param d Owning display.
* @param spec Required copied options; size is in logical pixels.
* @return An owned surface, or NULL with errno set. EINVAL means invalid options,
* including a nonpositive dimension, a dimension above 32767, or a buffer above
* INT32_MAX bytes. ENODEV is the fallback backend failure. Actual configured
* size may differ from the request. The callback table in spec->cbs is copied;
* spec->ud stays borrowed until callbacks are replaced or the surface is destroyed.
* This window's callbacks cannot run before creation returns. Use
* yasl_surface_geometry() for initial geometry; an initial configure callback
* is not guaranteed. Schedule an initial redraw after setting up application state.
* On a display owned by yasl_run(), the runner installs its own callbacks instead;
* use yasl_app_desc.event and yasl_app_desc.frame for application notifications.
*/
YASL_API struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d,
const struct yasl_window_spec *spec);
/**
* Create an owned popup attached to a parent surface.
* @param parent Borrowed parent; destroy the popup before the parent.
* @param spec Required copied options, entirely in device pixels.
* @return A popup, or NULL with errno set: EINVAL for invalid options, ENOTSUP
* for missing popup support, or a backend error (ENODEV if none was supplied).
* Wayland requires a recent input serial for its popup grab. The backend may
* flip or slide placement to fit the screen. See yasl_popup_spec.
*/
YASL_API struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent,
const struct yasl_popup_spec *spec);
/**
* Update a popup's geometry and placement.
* @param popup An existing popup.
* @param spec Required copied replacement options, in device pixels.
* @return true on success; false with errno set: EINVAL for invalid options,
* ENOTSUP for missing popup support, or a backend error (ENODEV if unspecified).
* Older Wayland popup protocol versions can reject repositioning.
*/
YASL_API bool yasl_surface_popup_reposition(struct yasl_surface *popup,
const struct yasl_popup_spec *spec);
/**
* Destroy a surface and cancel its surface timers and pending input.
* @param s Surface, or NULL for no action.
* Destroy child popups first. May be called from a surface callback; return from
* that callback without further use of the surface or its draw target. Every
* pointer or native handle obtained from this surface becomes invalid.
*/
YASL_API void yasl_surface_destroy(struct yasl_surface *s);
/**
* Get the display that owns a surface.
* @param s Surface, or NULL.
* @return A borrowed display, or NULL.
*/
YASL_API struct yasl_display *yasl_surface_display(const struct yasl_surface *s);
/**
* Replace the surface's callback table and userdata.
* @param s Surface, or NULL for no action.
* @param cbs Table copied by value; NULL removes all callbacks. NULL members are ignored.
* @param ud Borrowed userdata passed to the installed callbacks.
* This call only replaces callbacks; it does not invoke them.
* Use yasl_surface_geometry() to read current geometry; do not rely on receiving
* an initial configure callback when registering.
*/
YASL_API void yasl_surface_set_callbacks(struct yasl_surface *s,
const struct yasl_window_cbs *cbs, void *ud);
/**
* Replace the window title with copied UTF-8 text.
* @param s Surface.
* @param title NUL-terminated UTF-8 title. Pass an empty string to clear it.
* Backends may truncate the title.
*/
YASL_API void yasl_surface_set_title(struct yasl_surface *s, const char *title);
/**
* Request a future draw callback, coalescing repeated requests.
* @param s Surface, or NULL for no action.
* Call yasl_display_flush() to service the request. Schedule again from draw to
* animate. Without YASL_CAP_FRAME_PACING, throttle animation with a timer.
*/
YASL_API void yasl_surface_schedule_redraw(struct yasl_surface *s);
/* Present exactly the target supplied to the current draw callback, at most
* once. Targets expire when that callback returns or the surface is destroyed.
* damage is required: n must be -1 or 0..YASL_DAMAGE_MAX. Rectangles must have
* nonnegative extents; they are clipped to the target. Invalid input returns
* false without consuming the frame. See yasl/native.h for native rendering. */
/**
* Submit the current draw target at most once.
* @param s Surface currently being drawn.
* @param fb Exact target from the current draw callback, including its generation.
* @param damage Required damage hint: n is -1 for all pixels or 0..YASL_DAMAGE_MAX.
* @return true if accepted; false for invalid, stale, already presented, or failed targets.
* Rectangles must have nonnegative extents and are clipped to the target. Invalid
* input does not consume the frame. All pixels must be initialized even with
* partial damage. The backend may upload all pixels. Success records submission,
* not confirmed scanout. The target expires when draw returns or s is destroyed.
*/
YASL_API bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb,
const struct yasl_damage_list *damage);
/* Event payload strings are library-owned, NUL-terminated, and valid until
* the next SUCCESSFUL poll on this surface or surface destruction. Dispatch,
* flush and unsuccessful polls do not invalidate them. Copy to keep longer. */
/**
* Remove one input event from a surface's queue without waiting.
* @param s Surface whose queue to drain.
* @param ev Required output event; unchanged when no event is available.
* @return true for an event, false for an empty queue or NULL arguments.
* Dispatch receives native input; polling only drains the queue. Only input
* types (KEY through INPUT_RESET) are returned. Read the union member selected
* by type. Payload strings are borrowed, NUL-terminated, and valid until the
* next successful poll on this queue or its destruction; dispatch, flush, and
* unsuccessful polls do not invalidate them. On Wayland, popup pointer and
* keyboard input can be routed through the root parent's queue; inspect
* ev->surface to identify the receiving surface.
*/
YASL_API bool yasl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev);
/**
* Copy the last frame presented from the surface's CPU target.
* @param s Surface.
* @param out Output for size->x * size->y premultiplied ARGB8888 pixels with
* tightly packed rows, or NULL when cap is zero.
* @param cap Capacity of out in pixels.
* @param size Required output: the frame extent in device pixels, or zero when
* no frame is available.
* @return true if copied. false leaves out unchanged; when *size is nonzero,
* out was too small, so allocate size->x * size->y pixels and call again.
* A frame is available from a successful yasl_surface_present() until the
* next draw callback starts, and again once that draw presents. After a draw
* that does not present or a resize that replaces the target, no frame may be
* available; native rendering never has one. Call outside draw callbacks. This copies YASL's frame, not
* the composited screen: decorations, other windows, and color conversion are
* not included.
*/
YASL_API bool yasl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
struct yasl_vector2i *size);
/**
* Select absolute or relative (locked) pointer input for a surface.
* @param s Surface.
* @param mode YASL_POINTER_NORMAL or YASL_POINTER_RELATIVE.
* @return true if the mode is supported; false leaves the current mode.
* RELATIVE hides the cursor and locks it while the surface is focused;
* POINTER_MOTION then carries unbounded delta. Returning to NORMAL restores
* the cursor selected with yasl_surface_set_cursor(). RELATIVE is supported on
* Wayland compositors with the relative-pointer and pointer-constraints
* protocols; X11 and Cocoa return false.
*/
YASL_API bool yasl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode);
/* Current device-pixel size and scale, matching the configure callback. */
/**
* Read current device-pixel size and scale together.
* @param s Surface, or NULL.
* @return Geometry matching configure callbacks; NULL gives zero size and unit scale.
*/
YASL_API struct yasl_geometry yasl_surface_geometry(const struct yasl_surface *s);
/**
* Read current window state.
* @param s Surface, or NULL.
* @return A bitmask of yasl_surface_state values, or zero for NULL.
* Flags are reported state, not requests; unsupported states are absent.
* Flags may coexist. Windowed and floating have no separate flags. Suspension
* does not imply minimization, and no minimized flag is reported.
*/
YASL_API uint32_t yasl_surface_state(const struct yasl_surface *s);
/* Timing uses monotonic nanoseconds. Check source before treating timestamps
* as presentation confirmation. refresh/next_present are estimates. Without
* YASL_CAP_FRAME_PACING, redraws run on flush without display synchronization.
* Tearing may bypass pacing even when the capability is present. */
/**
* Read frame timing estimates and their provenance.
* @param s Surface, or NULL.
* @return Current timing, or a zero-initialized result for NULL.
* Timestamps use monotonic nanoseconds. Inspect source before treating a timestamp
* as confirmation of presentation. Refresh and next-present values are estimates.
*/
YASL_API struct yasl_timing yasl_surface_timing(const struct yasl_surface *s);
/**
* Get a nondecreasing animation timestamp aligned to estimated refresh.
* @param s Surface, or NULL.
* @param now Current monotonic nanoseconds, usually from yasl_loop_now_ns().
* @return Animation timestamp, or zero for NULL. Before timing is known, returns now.
* Use this to advance animations; it is not presentation feedback.
*/
YASL_API uint64_t yasl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now);
/* Set the mouse and tablet cursor for this surface, including future entries.
* YASL_CURSOR_HIDDEN stays hidden until another cursor is selected. */
/**
* Select the surface's system mouse and tablet cursor.
* @param s Surface.
* @param cursor Cursor shape; YASL_CURSOR_HIDDEN hides the cursor without disabling input.
* The choice persists across motion and re-entry until another shape is selected.
*/
YASL_API void yasl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor);
/**
* Set a backend window background hint.
* @param s Surface.
* @param bg Packed 0xAARRGGBB color; backends may ignore alpha or the entire hint.
* Does not initialize framebuffer pixels or replace drawing.
*/
YASL_API void yasl_surface_set_background_color(struct yasl_surface *s, uint32_t bg);
/* IME Support: preedit is borrowed until the next dispatch or destruction. */
/**
* Read the current uncommitted UTF-8 composition text.
* @param s Surface, or NULL.
* @return Borrowed NUL-terminated text, or NULL if absent/unsupported.
* Copy to retain across the next dispatch or surface destruction. Composition
* changes can schedule a redraw. Insert committed YASL_EVENT_TEXT into documents,
* not preedit text.
*/
YASL_API const char *yasl_surface_ime_preedit(const struct yasl_surface *s);
/**
* Read the cursor/selection range in the preedit string.
* @param s Surface.
* @param begin Required output UTF-8 byte offset for the range start.
* @param end Required output UTF-8 byte offset for the range end.
* @return true if available; false without changing outputs otherwise.
* Currently available on Wayland; Cocoa preedit text has no range result.
*/
YASL_API bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end);
/**
* Place the composition candidate window near the application caret.
* @param s Surface.
* @param caret Caret rectangle relative to the surface, in device pixels.
* Update after caret motion or scale changes. Unsupported backends ignore it.
*/
YASL_API void yasl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret);
/* Client-Side Decorations (CSD) Actions */
/**
* Ask the window system to begin an interactive move.
* @param s Toplevel surface, normally during handling of a pointer press.
* @return true if the request was issued; false if unsupported or not permitted.
* A recent input serial may be required. Success does not confirm a final position.
*/
YASL_API bool yasl_surface_start_interactive_move(struct yasl_surface *s);
/**
* Ask the window system to begin an interactive resize.
* @param s Toplevel surface, normally during handling of a pointer press.
* @param edge One nonzero yasl_resize_edge enumerator.
* @return true if issued; false for an invalid edge, missing support, or rejected request.
*/
YASL_API bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge);
/**
* Ask the window system to display its window management menu.
* @param s Toplevel surface.
* @param pos Surface-relative position in device pixels.
* @return true if issued; false if unsupported or not currently permitted.
*/
YASL_API bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos);
/* Drag Support */
/**
* Attach a toplevel surface to the current surface drag.
* @param s Surface to carry; check YASL_CAP_DRAG_CARRY first.
* @param offset Device-pixel offset of the pointer within the carried surface.
* @return true if accepted; false if unsupported or no compatible drag is active.
*/
YASL_API bool yasl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset);
/**
* Place a toplevel surface relative to the current pointer.
* @param s Surface; check YASL_CAP_PLACE_WINDOW first.
* @param at Device-pixel position within the surface to place under the pointer.
* @return true if issued; false if unsupported or unavailable.
*/
YASL_API bool yasl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at);
#ifdef __cplusplus
}
#endif
#endif /* YASL_H */
|