#ifndef YASL_H #define YASL_H #include #include #include #include #include #include "yasl/timing.h" #ifdef __cplusplus extern "C" { #endif #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 */ struct yasl_vector2i { int x; int y; }; struct yasl_rect2i { int x; int y; int width; int height; }; #define YASL_SCALE_UNIT 120 #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. */ 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 = logical * ((double)scale_120 / YASL_SCALE_UNIT); if (px >= INT_MAX) return INT_MAX; if (px <= INT_MIN) return INT_MIN; return (int)(px < 0 ? px - 0.5 : px + 0.5); } static inline int yasl_scale_to_logical(int device, uint32_t scale_120) { if (!scale_120) return 0; int64_t px = (int64_t)device * YASL_SCALE_UNIT; px = (px + (px < 0 ? -(int64_t)(scale_120 / 2) : scale_120 / 2)) / scale_120; if (px > INT_MAX) return INT_MAX; if (px < INT_MIN) return INT_MIN; return (int)px; } /* Capabilities */ enum yasl_display_cap : uint32_t { YASL_CAP_POPUPS = 1 << 0, YASL_CAP_DRAG = 1 << 1, YASL_CAP_DRAG_CARRY = 1 << 2, YASL_CAP_PLACE_WINDOW = 1 << 3, YASL_CAP_PRIMARY_SELECTION = 1 << 4, YASL_CAP_CSD = 1 << 5, YASL_CAP_NATIVE_GRAPHICS = 1 << 6, YASL_CAP_FRAME_PACING = 1 << 7, /* compositor/display-driven draws */ /* Backend/protocol supports tablet axes, independent of attached hardware. */ YASL_CAP_TABLET = 1 << 8, }; enum yasl_selection : uint8_t { YASL_CLIPBOARD, YASL_PRIMARY, }; enum yasl_surface_state : uint32_t { YASL_STATE_FULLSCREEN = 1 << 0, YASL_STATE_MAXIMIZED = 1 << 1, YASL_STATE_ACTIVATED = 1 << 2, YASL_STATE_SUSPENDED = 1 << 3, YASL_STATE_CSD = 1 << 4, }; 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, /* Hide the system cursor; pointer input is unchanged. */ YASL_CURSOR_NONE = YASL_CURSOR_HIDDEN, /* Compatibility alias. */ }; enum yasl_resize_edge : uint32_t { YASL_EDGE_NONE = 0, YASL_EDGE_TOP = 1, YASL_EDGE_BOTTOM = 2, YASL_EDGE_LEFT = 4, YASL_EDGE_TOP_LEFT = 5, YASL_EDGE_BOTTOM_LEFT = 6, YASL_EDGE_RIGHT = 8, YASL_EDGE_TOP_RIGHT = 9, YASL_EDGE_BOTTOM_RIGHT = 10, }; /* Damage & Framebuffer * CPU pixels are tightly packed premultiplied ARGB8888 (0xAARRGGBB). * Size, pointer positions, IME caret and damage use device pixels; surface * creation sizes use logical pixels. Damage is a hint: backends may upload * the whole frame. Every pixel must contain valid content on each draw. */ #define YASL_DAMAGE_MAX 64 struct yasl_damage_list { int n; /* -1 means full repaint */ struct yasl_rect2i bounds; struct yasl_rect2i rects[YASL_DAMAGE_MAX]; }; struct yasl_framebuffer { uint64_t generation; /* Opaque draw identity; preserve when copying target. */ uint32_t *pixels; struct yasl_vector2i size; }; struct yasl_frame { struct yasl_surface *surface; struct yasl_framebuffer target; }; /* Drag and Drop */ enum yasl_drag_event { YASL_DRAG_OVER, YASL_DRAG_LEAVE, YASL_DRAG_DROP, }; enum yasl_drop_event { YASL_DROP_OVER, YASL_DROP_LEAVE, }; enum yasl_drag_end { YASL_DRAG_DROPPED, YASL_DRAG_CANCELLED, }; struct yasl_drag_image { const uint32_t *pixels; struct yasl_vector2i size; struct yasl_vector2i hotspot; }; struct yasl_drag_listener { void (*end)(void *ud, enum yasl_drag_end how); }; struct yasl_geometry { struct yasl_vector2i size; uint32_t scale_120; }; /* Input Model */ enum yasl_key_action { YASL_KEY_PRESS, YASL_KEY_RELEASE, YASL_KEY_REPEAT, }; enum yasl_mod : uint32_t { YASL_MOD_SHIFT = 1 << 0, YASL_MOD_ALT = 1 << 1, YASL_MOD_CTRL = 1 << 2, YASL_MOD_SUPER = 1 << 3, YASL_MOD_CAPS = 1 << 4, }; enum yasl_pointer_kind { YASL_PTR_PRESS, YASL_PTR_RELEASE, YASL_PTR_MOTION, YASL_PTR_LEAVE, YASL_PTR_SCROLL, }; enum yasl_pointer_device { YASL_PTR_DEV_MOUSE, YASL_PTR_DEV_TOUCH, YASL_PTR_DEV_PEN, YASL_PTR_DEV_ERASER, }; struct yasl_pointer_pen { uint16_t pressure; /* 0..65535, normalized to the device's pressure range. */ int8_t tilt_x; /* Degrees, -90..90; positive toward the right. */ int8_t tilt_y; /* Degrees, -90..90; positive toward the bottom. */ }; struct yasl_key_event { uint32_t key; uint32_t keypad; enum yasl_key_action action; uint32_t mods; uint32_t codepoint; uint32_t shifted; uint32_t base; uint32_t text; }; struct yasl_mouse_event { struct yasl_vector2i pos; int16_t button; /* Press/release: 0 = primary/tip, 1 = middle, 2 = secondary. * Motion/leave: informational; track presses and releases. */ int16_t scroll_lines; enum yasl_pointer_kind kind; enum yasl_pointer_device device; uint32_t mods; float scroll_delta; /* Pen/eraser samples include the current axes, also on pressure-only * motion. Missing axes and non-tablet devices report zero. A primary * release or leave ends contact; zero pressure alone is not a release. */ struct yasl_pointer_pen pen; }; enum yasl_input_kind { YASL_INPUT_KEY, YASL_INPUT_MOUSE, YASL_INPUT_FOCUS, YASL_INPUT_PASTE, YASL_INPUT_DROP_FILES, YASL_INPUT_TEXT, /* The input stream was interrupted (e.g. allocation failure or device * removal). Clear held keys/buttons and composition state. */ YASL_INPUT_RESET, }; struct yasl_focus_event { bool focused; }; struct yasl_text_event { const char *text; size_t len; }; struct yasl_paste_event { const char *text; size_t len; }; struct yasl_drop_files_event { const char *files; size_t len; }; struct yasl_input_event { struct yasl_surface *surface; enum yasl_input_kind kind; uint32_t reserved; union { struct yasl_key_event key; struct yasl_mouse_event mouse; struct yasl_focus_event focus; struct yasl_text_event text; struct yasl_paste_event paste; struct yasl_drop_files_event drop_files; }; }; /* 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 */ struct yasl_display_cbs { void (*quit)(void *ud); void (*reopen)(void *ud); void (*lost)(void *ud); }; struct yasl_surface_cbs { void (*draw)(struct yasl_surface *s, struct yasl_frame *frame, void *ud); void (*configure)(struct yasl_surface *s, struct yasl_geometry geometry, void *ud); void (*close)(struct yasl_surface *s, void *ud); void (*state)(struct yasl_surface *s, uint32_t state, void *ud); void (*drag)(struct yasl_surface *s, enum yasl_drag_event ev, struct yasl_vector2i pos, void *ud); void (*drop)(struct yasl_surface *s, enum yasl_drop_event ev, struct yasl_vector2i pos, void *ud); }; /* Display Lifecycle */ enum yasl_backend { YASL_BACKEND_AUTO = 0, YASL_BACKEND_WAYLAND, YASL_BACKEND_X11, YASL_BACKEND_COCOA, }; struct yasl_display_spec { const char *app_id; const struct yasl_display_cbs *cbs; void *ud; /* Zero selects the native backend. An explicit choice never falls back. */ enum yasl_backend backend; }; /* Return NULL and set errno if the requested backend cannot open. */ YASL_API struct yasl_display *yasl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec); YASL_API enum yasl_backend yasl_display_backend(const struct yasl_display *d); YASL_API void yasl_display_close(struct yasl_display *d); 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. */ YASL_API int yasl_display_dispatch(struct yasl_display *d, int timeout_ms); YASL_API void yasl_display_flush(struct yasl_display *d); YASL_API int yasl_display_modifiers(const struct yasl_display *d); /* Unsupported/invalid selections are no-ops, never clipboard aliases. */ YASL_API void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text); 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. */ 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); YASL_API void yasl_display_drag_cancel(struct yasl_display *d); /* Surface Lifecycle */ struct yasl_surface_spec { const char *title; struct yasl_vector2i size; bool fullscreen; bool tearing; }; struct yasl_popup_spec { struct yasl_vector2i size; struct yasl_rect2i anchor; bool beside; struct yasl_vector2i content_offset; struct yasl_vector2i content_size; }; YASL_API struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d, const struct yasl_surface_spec *spec); YASL_API struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec); YASL_API bool yasl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec); YASL_API void yasl_surface_destroy(struct yasl_surface *s); YASL_API struct yasl_display *yasl_surface_display(const struct yasl_surface *s); YASL_API void yasl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_surface_cbs *cbs, void *ud); YASL_API void yasl_surface_set_title(struct yasl_surface *s, const char *title); 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. */ 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. */ YASL_API bool yasl_surface_input_poll(struct yasl_surface *s, struct yasl_input_event *ev); /* Current device-pixel size and scale, matching the configure callback. */ YASL_API struct yasl_geometry yasl_surface_geometry(const struct yasl_surface *s); 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. */ YASL_API struct yasl_timing yasl_surface_timing(const struct yasl_surface *s); 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. */ YASL_API void yasl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor); 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. */ YASL_API const char *yasl_surface_ime_preedit(const struct yasl_surface *s); YASL_API bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end); YASL_API void yasl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret); /* Client-Side Decorations (CSD) Actions */ YASL_API bool yasl_surface_start_interactive_move(struct yasl_surface *s); YASL_API bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge); YASL_API bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos); /* Drag Support */ YASL_API bool yasl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset); YASL_API bool yasl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at); #ifdef __cplusplus } #endif #endif /* YASL_H */