#ifndef UHDK_H_INCLUDED #define UHDK_H_INCLUDED /* * Uhdk desktop platform API — v0.1, macOS, Windows and Linux backends. * * Implementation: Zig. ABI: C. Header baseline: C99 (also usable from C++). * No libc runtime objects or allocation contract. * supplies types only; this header requires no libc function calls. * Native OS frameworks may themselves depend on system runtimes. * * Header configuration (define before the first include): * - UHDK_ENABLE_OPENGL: include bundled GLAD types and per-context loader API. * - UHDK_ENABLE_NATIVE: declare the experimental borrowed native-handle API. * - UHDK_PROFILE_BACKEND: quoted consumer instrumentation adapter header. * Presence enables each feature flag; leave it undefined to disable it. * These select declarations only, not library backend/build features. * * Naming: * - Types: uhdk_type; functions: uhdk_object_verb; constants: UHDK_CATEGORY_VALUE. * - create/destroy allocate and dispose unique objects; retain/release manage * shared ownership. Reference-counted objects also start with create. * - get/set access properties; copy writes caller storage; is/has/can query bools. * - *_async starts a request completed by UHDK_EVENT_REQUEST_DONE. Functions * that merely schedule events (wake/invalidate) do not create async requests. * - *_desc describes creation/configuration; *_fn names callbacks; *_flags names * bitmask types. Pointer/span lengths use len or _len; writable buffers * use capacity. Measurement units are named explicitly where needed. * * Global rules: * - Floating-point values use C float (32-bit / Zig f32 on supported targets). * - All text is UTF-8 pointer + byte length, never implicitly NUL-terminated. * Embedded NUL is rejected where an OS identifier/path cannot represent it. * - NULL is allowed for a span's pointer only when its length is zero. * - Input spans/descriptors are copied before return unless documented otherwise. * - No allocator ownership crosses the ABI. Release objects through Uhdk. * - Event batches and spans are borrowed until the next valid poll on the * application thread, or app destruction (whichever comes first). Copy spans * or retain files/menus to keep them. Every such poll resets storage, including empty/error polls. * Rendering does not reset storage. Each poll returns an immutable batch. * - create/run/destroy belong to OS main. run invokes one application entry on * a second thread. All other calls belong to that application thread except * app_wake, which is thread-safe. Native work is queued to the main thread. * - File operations are async to the caller and executed in bounded platform * batches. Individual filesystem calls can still delay native input collection. * - Descriptors start with struct_size; zero-initialize them, set struct_size to * sizeof(the descriptor), then fill fields. Reserved fields must remain zero. * Unknown trailing fields are ignored; smaller descriptors are rejected. * - Integer enum values have fixed-width typedefs; C enums only name constants. * - For output structs with struct_size, callers set it before the call; Uhdk * writes only the supplied size. Event struct_size describes library output. * - Functions return a status; output arguments are valid only on UHDK_STATUS_OK, * except documented buffer-size queries. * - The application owns rendering, document state, file encoding and layout. * Uhdk provides optional OpenGL 3.3 core context management. Other renderers * may attach through the experimental native-handle API; drawing remains app-owned. */ #include /* Optional, consumer-owned instrumentation; no exported UHDK ABI or dependency. * Define UHDK_PROFILE_BACKEND to a quoted adapter header before including uhdk.h. * That header implements the macros below. Without it, arguments are not evaluated. * Names (zone, thread, pool) must be string literals or have static lifetime. * C zones: UHDK_PROFILE_BEGIN(token, "name"); ... UHDK_PROFILE_END(token); * End on the same thread, in reverse nesting order, including early exits. * C++ may use UHDK_PROFILE_SCOPE("name") for automatic scope exit. * Memory: report successful non-null allocations, then free BEFORE releasing * storage. A successful resize is free + alloc; failed allocations emit nothing. * A pointer must use the same pool name for allocation and free. These hooks do * not allocate/free memory themselves, intercept malloc, or transfer ownership. */ #ifdef UHDK_PROFILE_BACKEND #include UHDK_PROFILE_BACKEND #else #define UHDK_PROFILE_BEGIN(token, name) ((void)0) #define UHDK_PROFILE_END(token) ((void)0) #define UHDK_PROFILE_SCOPE(name) ((void)0) #define UHDK_PROFILE_FRAME() ((void)0) #define UHDK_PROFILE_THREAD(name) ((void)0) #define UHDK_PROFILE_ALLOC(ptr, size, pool) ((void)0) #define UHDK_PROFILE_FREE(ptr, pool) ((void)0) #endif #ifndef UHDK_API #if defined(_WIN32) && defined(UHDK_SHARED) #if defined(UHDK_BUILD) #define UHDK_API __declspec(dllexport) #else #define UHDK_API __declspec(dllimport) #endif #elif defined(__GNUC__) && defined(UHDK_SHARED) #define UHDK_API __attribute__((visibility("default"))) #else #define UHDK_API #endif #endif #if defined(_WIN32) && !defined(_WIN64) #define UHDK_CALL __cdecl #else #define UHDK_CALL #endif #ifdef __cplusplus extern "C" { #endif #define UHDK_ABI_VERSION 1u typedef char uhdk_bool; /* Values are UHDK_FALSE (0) or UHDK_TRUE (1). */ enum { UHDK_FALSE = 0, UHDK_TRUE = 1 }; typedef uint32_t uhdk_status; enum { UHDK_STATUS_OK = 0, UHDK_STATUS_CANCELLED = 1, UHDK_STATUS_UNSUPPORTED = 2, UHDK_STATUS_INVALID_ARGUMENT = 3, UHDK_STATUS_OUT_OF_MEMORY = 4, UHDK_STATUS_PERMISSION_DENIED = 5, UHDK_STATUS_UNAVAILABLE = 6, UHDK_STATUS_NOT_FOUND = 7, UHDK_STATUS_BUSY = 8, UHDK_STATUS_WRONG_THREAD = 9, UHDK_STATUS_OS_ERROR = 10, UHDK_STATUS_BUFFER_TOO_SMALL = 11 }; typedef struct uhdk_app uhdk_app; typedef struct uhdk_window uhdk_window; typedef struct uhdk_gl_context uhdk_gl_context; typedef struct uhdk_file uhdk_file; typedef struct uhdk_watch uhdk_watch; typedef struct uhdk_menu uhdk_menu; typedef uint64_t uhdk_request_id; /* 0 is invalid; IDs never reused per app. */ typedef struct uhdk_text { const char *ptr; uint64_t len; } uhdk_text; typedef struct uhdk_bytes { const uint8_t *ptr; uint64_t len; } uhdk_bytes; typedef struct uhdk_text_list { const uhdk_text *ptr; uint64_t len; } uhdk_text_list; typedef struct uhdk_file_list { uhdk_file *const *ptr; uint64_t len; } uhdk_file_list; typedef struct uhdk_point { float x; float y; } uhdk_point; typedef struct uhdk_size { float width; float height; } uhdk_size; typedef struct uhdk_rect { float x; float y; float width; float height; } uhdk_rect; typedef struct uhdk_image { uint32_t struct_size; uint32_t width; uint32_t height; uint32_t reserved; uint64_t stride_bytes; uhdk_bytes pixels; /* Top-down, straight-alpha RGBA8 in sRGB. */ } uhdk_image; typedef uint32_t uhdk_backend; enum { UHDK_BACKEND_AUTO = 0, UHDK_BACKEND_MACOS = 1, UHDK_BACKEND_WINDOWS = 2, UHDK_BACKEND_WAYLAND = 3, UHDK_BACKEND_X11 = 4 }; typedef uint32_t uhdk_capability; enum { UHDK_CAP_FILE_DIALOGS = 1, UHDK_CAP_CLIPBOARD = 2, UHDK_CAP_DROP_FILES = 4, UHDK_CAP_APP_MENU = 5, UHDK_CAP_CONTEXT_MENU = 6, UHDK_CAP_APP_ICON = 8, UHDK_CAP_POINTER_CAPTURE = 12, UHDK_CAP_TEXT_INPUT = 16, UHDK_CAP_OPENGL = 18 }; /* Every accepted async request returns a nonzero ID and exactly one terminal * REQUEST_DONE during normal draining. Failure to accept produces no event. * Cancellation is best effort; an operation already committed may succeed. * Return from app_main early abandons undelivered results and initiates shutdown. */ typedef struct uhdk_event uhdk_event; typedef struct uhdk_event_list { const uhdk_event *ptr; uint64_t len; } uhdk_event_list; #define UHDK_WAIT_FOREVER UINT64_MAX typedef void(UHDK_CALL *uhdk_app_main_fn)(uhdk_app *app, void *user); typedef struct uhdk_app_desc { uint32_t struct_size; uint32_t abi_version; uhdk_text application_id; uhdk_text display_name; uhdk_backend preferred_backend; uint32_t reserved; } uhdk_app_desc; UHDK_API uint32_t UHDK_CALL uhdk_get_abi_version(void); UHDK_API uhdk_status UHDK_CALL uhdk_app_create(const uhdk_app_desc *desc, uhdk_app **out); UHDK_API uhdk_status UHDK_CALL uhdk_app_run( uhdk_app *app, uhdk_app_main_fn main_fn, void *user ); /* Once. */ /* Zero timeout does not wait; finite timeouts use monotonic nanoseconds. * STOPPED is delivered once; later polls return UNAVAILABLE. */ UHDK_API uhdk_status UHDK_CALL uhdk_app_poll_events( uhdk_app *app, uint64_t timeout_ns, uhdk_event_list *out ); UHDK_API void UHDK_CALL uhdk_app_quit(uhdk_app *app); /* Accept/request shutdown. */ UHDK_API void UHDK_CALL uhdk_app_wake(uhdk_app *app); /* Thread-safe, coalesced WAKE event. */ /* Monotonic nanoseconds since app creation, in the same clock as event.time_ns. */ UHDK_API uint64_t UHDK_CALL uhdk_app_get_time_ns(uhdk_app *app); /* One pending deadline per app. Replaces any previous deadline; 0 cancels it. * A past deadline queues WAKE at the next dispatch. On expiry the deadline is * cleared before delivering WAKE. Explicit wakes may coalesce with expiry but * do not cancel a future deadline. No event is delivered inline. App code maintains * multiple timers by scheduling its earliest deadline. No real-time guarantee. */ UHDK_API uhdk_status UHDK_CALL uhdk_app_set_wake_deadline(uhdk_app *app, uint64_t deadline_ns); UHDK_API void UHDK_CALL uhdk_app_destroy(uhdk_app *app); /* After run; releases child objects. */ /* Linux: AUTO/false before successful startup; thereafter the selected backend * and implemented, available mechanisms. Capabilities do not guarantee success * for a particular operation. Other platforms retain their fixed backend. */ UHDK_API uhdk_backend UHDK_CALL uhdk_app_get_backend(uhdk_app *app); UHDK_API uhdk_bool UHDK_CALL uhdk_app_has_capability(uhdk_app *app, uhdk_capability capability); UHDK_API uhdk_status UHDK_CALL uhdk_request_cancel(uhdk_app *app, uhdk_request_id request_id); /* Application-thread CPU execution time in nanoseconds, not elapsed wall time. * Bracket user update/input processing with begin_tick/end_tick, AFTER polling * events and BEFORE rendering. Nested ticks return BUSY; end without begin is * INVALID_ARGUMENT. Polling/idle time outside the bracket is not counted. * Render timing uses GL begin_frame/end_frame brackets containing a successful * swap_buffers. Setup/cleanup brackets without presentation are not frames. * Blocked waits (including GPU waits), sleep, and time spent descheduled are * excluded. The entire swap_buffers call is excluded, including driver spinning * during presentation/vsync. Other CPU work in GL/driver calls is counted; * this does not measure GPU execution or CPU work on other threads. * tick_time_ns and render_time_ns are the most recent completed phase samples. * frame_time_ns captures the latest completed tick + render at presentation's * end_frame; it stays unchanged until another presented frame completes. * frames_per_second is the reciprocal of elapsed wall time between the last two * presented end_frame calls, across all contexts. It includes idle time, vsync, * and GPU waits; it is NOT derived from CPU frame_time_ns or monitor refresh. * It is zero until two frames complete, then retains the last sample while idle. * Counts are cumulative; timings are zero until a sample is available. Calls * do not generate events. Read the previous frame to display stats while drawing. */ typedef struct uhdk_app_stats { uint32_t struct_size; uint32_t reserved; uint64_t tick_count; uint64_t frame_count; uint64_t event_count; uint64_t tick_time_ns; uint64_t render_time_ns; uint64_t frame_time_ns; float frames_per_second; } uhdk_app_stats; UHDK_API uhdk_status UHDK_CALL uhdk_app_begin_tick(uhdk_app *app); UHDK_API uhdk_status UHDK_CALL uhdk_app_end_tick(uhdk_app *app); UHDK_API uhdk_status UHDK_CALL uhdk_app_get_stats(uhdk_app *app, uhdk_app_stats *out); /* Logical coordinates: top-left origin, x right, y down. Window geometry uses * content-area units. Framebuffer dimensions are pixels; use reported sizes, * not a rounded logical-size * scale calculation. Global positions are not exposed. */ typedef uint32_t uhdk_window_flags; enum { UHDK_WINDOW_RESIZABLE = 1u << 0, UHDK_WINDOW_DECORATED = 1u << 1, UHDK_WINDOW_OPENGL = 1u << 3 /* Prepare a drawable for uhdk_gl_context_create. */ }; typedef uint32_t uhdk_window_state; enum { UHDK_WINDOW_NORMAL = 0, UHDK_WINDOW_MINIMIZED = 1, UHDK_WINDOW_MAXIMIZED = 2, UHDK_WINDOW_FULLSCREEN = 3 }; typedef struct uhdk_window_desc { uint32_t struct_size; uhdk_window_flags flags; uhdk_text title; uhdk_size content_size; uhdk_size min_size; uhdk_size max_size; /* Zero limit = unconstrained. */ uhdk_window *parent; /* Optional; must outlive child. */ } uhdk_window_desc; /* Observation bits say which independent window facts are known. A clear bit * means unknown, not false. Support depends on the backend/protocol version. */ typedef uint32_t uhdk_window_observation_flags; enum { UHDK_WINDOW_OBSERVE_MINIMIZED = 1u << 0, UHDK_WINDOW_OBSERVE_RESIZING = 1u << 1, UHDK_WINDOW_OBSERVE_OCCLUDED = 1u << 2, UHDK_WINDOW_OBSERVE_SUSPENDED = 1u << 3 }; /* WINDOW_METRICS is the resize/state hook; compare snapshots for transitions. * State describes geometry (normal/maximized/fullscreen), independently of * minimized. Wayland does not report minimization; its observation bit is clear. * visible is show/hide visibility, not an occlusion or minimization guarantee; * on Wayland it is application intent. occluded is an optional rendering hint, * not permission to stop processing events. suspended is a separate compositor * hint that content is not ordinarily repainted, not proof of occlusion or * minimization. Resizing is an advisory interactive size/move-loop * hint, not a guaranteed transaction around every size change. * Async size/state completion acknowledges submission; geometry arrives here. */ typedef struct uhdk_window_metrics { uint32_t struct_size; uint32_t framebuffer_width; uint32_t framebuffer_height; uhdk_window_state state; uhdk_size logical_size; float scale; uhdk_bool focused; uhdk_bool visible; uhdk_window_observation_flags observations; uhdk_bool minimized; uhdk_bool resizing; uhdk_bool occluded; uhdk_bool suspended; } uhdk_window_metrics; UHDK_API uhdk_status UHDK_CALL uhdk_window_create_async( uhdk_app *app, const uhdk_window_desc *desc, uhdk_request_id *out ); UHDK_API void UHDK_CALL uhdk_window_destroy(uhdk_window *window); UHDK_API uhdk_status UHDK_CALL uhdk_window_set_title_async( uhdk_window *window, uhdk_text title, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_window_set_size_async( uhdk_window *window, uhdk_size size, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_window_set_state_async( uhdk_window *window, uhdk_window_state state, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_window_set_visible_async( uhdk_window *window, uhdk_bool visible, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_window_request_focus_async( uhdk_window *window, uhdk_request_id *out ); /* OS may decline. */ UHDK_API uhdk_status UHDK_CALL uhdk_window_get_metrics( uhdk_window *window, uhdk_window_metrics *out ); UHDK_API void UHDK_CALL uhdk_window_invalidate(uhdk_window *window); /* Coalesced redraw event. */ /* Optional OpenGL context management (application/render thread). * Define UHDK_ENABLE_OPENGL before including this header for bundled Khronos * GL types, constants, and PFNGL* function-pointer types. Without that flag, * no GL names are introduced. * * Creation requires UHDK_WINDOW_OPENGL and guarantees desktop OpenGL >= 3.3, * core profile, with GLSL >= 3.30. macOS selects its 4.1 core profile because * it cannot create an exact 3.3 profile. No legacy or OpenGL ES fallback. * Initial framebuffer requirement: double-buffered RGBA8. Depth/stencil buffers * are not required or guaranteed. No multisampling or sRGB is requested. * Sharing, debug contexts and custom formats are future work. * * One context per window. Create does NOT change the current context. Explicitly * begin_frame locks drawable updates and makes it current; end_frame unlocks. * ALL raw GL calls (including initialization and deletion) and presentation must * be inside this bracket. Never wait for platform results inside a bracket. * make_current/clear_current are also available inside a bracket. * Contexts cannot migrate to another thread. Destroy detaches a current context; * window destruction also destroys its context and invalidates the handle. * Creation failures leave the window valid and release partially created state. * Uhdk does not modify viewport/scissor or other application GL state on resize; * use WINDOW_METRICS framebuffer dimensions to update these yourself. */ #if defined(_WIN32) #define UHDK_GL_CALL __stdcall #else #define UHDK_GL_CALL #endif /* A function pointer, never a data pointer. Cast to the matching Khronos PFNGL* * type before calling. GL uses stdcall on Win32; Uhdk exports still use C ABI. */ typedef void(UHDK_GL_CALL *uhdk_gl_proc)(void); UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_create_async( uhdk_window *window, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_begin_frame(uhdk_gl_context *context); UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_end_frame(uhdk_gl_context *context); UHDK_API void UHDK_CALL uhdk_gl_context_destroy(uhdk_gl_context *context); UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_make_current(uhdk_gl_context *context); UHDK_API uhdk_status UHDK_CALL uhdk_gl_clear_current(uhdk_app *app); /* Actual negotiated version, not the requested minimum. */ UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_get_version( uhdk_gl_context *context, uint32_t *out_major, uint32_t *out_minor ); /* Wayland returns UNAVAILABLE while hidden or awaiting mapping configuration. */ UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_swap_buffers(uhdk_gl_context *context); /* Explicitly set 0 (unsynchronized) or 1 (vsync); other values are invalid. * No initial interval is guaranteed. Unsupported control returns UNSUPPORTED; * compositor/driver policy may override presentation timing. */ UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_set_swap_interval( uhdk_gl_context *context, int32_t interval ); /* Requires this context current. Accepts pointer+length ASCII GL symbol names. * Missing symbols return NOT_FOUND. Pointers are valid until context destruction * and must only be used with that context current. A non-NULL pointer alone does * not prove extension/version support; check GL version/extension availability. * Loads both platform-library exports and driver-provided GL entry points. */ UHDK_API uhdk_status UHDK_CALL uhdk_gl_context_get_proc( uhdk_gl_context *context, uhdk_text name, uhdk_gl_proc *out ); /* Keyboard events are not text. Physical key = USB HID keyboard-page usage; * 0 means unknown. Logical key = Unicode scalar, or UHDK_KEY_* below. * Modifier bits describe state after the event. Focus loss resets held state. * With text input enabled, text/edit events are authoritative for editing; * raw key events are observational for those actions (do not insert twice). * Registered menu accelerators are dispatched as menu/edit commands instead of * raw key events. Unregistered application shortcuts need their own policy. */ typedef uint32_t uhdk_modifier_flags; enum { UHDK_MOD_SHIFT = 1u << 0, UHDK_MOD_CONTROL = 1u << 1, UHDK_MOD_ALT = 1u << 2, UHDK_MOD_SUPER = 1u << 3, UHDK_MOD_CAPS_LOCK = 1u << 4, UHDK_MOD_NUM_LOCK = 1u << 5 }; typedef uint32_t uhdk_key; enum { UHDK_KEY_UNKNOWN = 0, UHDK_KEY_ESCAPE = 0x110000, UHDK_KEY_ENTER, UHDK_KEY_TAB, UHDK_KEY_BACKSPACE, UHDK_KEY_DELETE, UHDK_KEY_INSERT, UHDK_KEY_LEFT, UHDK_KEY_RIGHT, UHDK_KEY_UP, UHDK_KEY_DOWN, UHDK_KEY_HOME, UHDK_KEY_END, UHDK_KEY_PAGE_UP, UHDK_KEY_PAGE_DOWN, UHDK_KEY_F1 = 0x110100 /* F1..F24 are contiguous. */ }; typedef uint32_t uhdk_pointer_button; enum { UHDK_BUTTON_NONE = 0, UHDK_BUTTON_PRIMARY = 1, UHDK_BUTTON_SECONDARY = 2, UHDK_BUTTON_MIDDLE = 3, UHDK_BUTTON_BACK = 4, UHDK_BUTTON_FORWARD = 5 }; typedef uint32_t uhdk_scroll_unit; enum { UHDK_SCROLL_LOGICAL_PIXELS = 0, UHDK_SCROLL_LINES = 1 }; typedef uint32_t uhdk_scroll_phase; enum { UHDK_SCROLL_DISCRETE = 0, UHDK_SCROLL_BEGIN = 1, UHDK_SCROLL_UPDATE = 2, UHDK_SCROLL_END = 3, UHDK_SCROLL_CANCEL = 4 }; typedef uint32_t uhdk_cursor_shape; enum { UHDK_CURSOR_ARROW = 0, UHDK_CURSOR_TEXT, UHDK_CURSOR_CROSSHAIR, UHDK_CURSOR_HAND, UHDK_CURSOR_RESIZE_HORIZONTAL, UHDK_CURSOR_RESIZE_VERTICAL, UHDK_CURSOR_RESIZE_NW_SE, UHDK_CURSOR_RESIZE_NE_SW, UHDK_CURSOR_MOVE, UHDK_CURSOR_NOT_ALLOWED, UHDK_CURSOR_WAIT, UHDK_CURSOR_HIDDEN }; typedef uint32_t uhdk_pointer_mode; enum { UHDK_POINTER_NORMAL = 0, UHDK_POINTER_CAPTURE = 1 }; UHDK_API uhdk_status UHDK_CALL uhdk_window_set_cursor_shape_async( uhdk_window *window, uhdk_cursor_shape shape, uhdk_request_id *out ); /* Capture delivers selection-drag events outside the window. Focus loss or OS * cancellation restores NORMAL and emits POINTER_MODE_CHANGED. */ UHDK_API uhdk_status UHDK_CALL uhdk_window_set_pointer_mode_async( uhdk_window *window, uhdk_pointer_mode mode, uhdk_request_id *out ); /* Text offsets/lengths are UTF-8 byte counts, always at scalar boundaries. * surrounding_text is a bounded snapshot; document_offset locates it in the * document. Selection and replacement ranges are relative to this snapshot. * Events are ordered. Apply all edits in order, including edits sharing a base * revision; do not discard a burst just because its first edit changed revision. * acknowledged_input_id is the last applied text/edit input ID. Snapshots behind * outstanding native edits are ignored; publish again after consuming the batch. * On macOS, a semantic edit invalidates the native snapshot. Subsequent text * input waits for an acknowledging snapshot, including when an edit is ignored. * Acknowledge asynchronous edits (such as paste) only once their result is applied. * Disabling text input discards deferred input. Raw key IDs are observational; * acknowledge applied text/edit events, not raw keys that may precede them. * Commit/preedit events carry text + an optional replacement range. A preedit * updates temporary composition, never the permanent document by itself. */ typedef struct uhdk_text_state { uint32_t struct_size; uint32_t reserved; uhdk_text surrounding_text; uint64_t revision; uint64_t acknowledged_input_id; uint64_t document_offset; uint64_t selection_start; uint64_t selection_len; uhdk_rect caret; /* Window-local logical coordinates. */ } uhdk_text_state; UHDK_API uhdk_status UHDK_CALL uhdk_window_set_text_input_async( uhdk_window *window, uhdk_bool enabled, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_window_set_text_state( uhdk_window *window, const uhdk_text_state *state ); UHDK_API uhdk_status UHDK_CALL uhdk_window_cancel_composition_async( uhdk_window *window, uhdk_request_id *out ); /* File references preserve native names, including non-UTF-8 POSIX paths. * Display names are UTF-8. URI/native-path export is explicit and owned by the * caller's buffer. Windows native path bytes are UTF-16LE (no terminator), * POSIX native paths are uninterpreted bytes. Windows URI export returns * UNSUPPORTED when the shell cannot represent a native name losslessly (such * as unpaired UTF-16 surrogates); native-path export remains available. * A reference can carry an access * grant; retaining it keeps that grant alive where the OS permits. Persistence * across launches/bookmark serialization is deferred to a future extension. * Buffer queries: NULL/0 gets required byte count in out_len and returns UHDK_STATUS_OK. * Too-small buffers return UHDK_STATUS_BUFFER_TOO_SMALL and required count; no partial copy. */ typedef uint32_t uhdk_file_representation; enum { UHDK_FILE_DISPLAY_NAME = 0, UHDK_FILE_URI = 1, UHDK_FILE_NATIVE_PATH = 2 }; UHDK_API uhdk_status UHDK_CALL uhdk_file_create_from_native_path( uhdk_app *app, uhdk_bytes path, uhdk_file **out ); UHDK_API uhdk_status UHDK_CALL uhdk_file_create_from_uri( uhdk_app *app, uhdk_text uri, uhdk_file **out ); UHDK_API void UHDK_CALL uhdk_file_retain(uhdk_file *file); UHDK_API void UHDK_CALL uhdk_file_release(uhdk_file *file); UHDK_API uhdk_status UHDK_CALL uhdk_file_copy_representation( uhdk_file *file, uhdk_file_representation representation, uint8_t *buffer, uint64_t capacity, uint64_t *out_len ); typedef uint32_t uhdk_file_type; enum { UHDK_FILE_REGULAR = 1, UHDK_FILE_DIRECTORY = 2, UHDK_FILE_OTHER = 3 }; typedef struct uhdk_file_info { uint64_t size_bytes; int64_t modified_time_ns; /* Unix epoch, nanoseconds. */ uhdk_file_type type; uint32_t reserved; } uhdk_file_info; /* FIFO across accepted file requests. Read results are raw bytes, batch-owned. * max_bytes is a hard limit; oversized files return BUFFER_TOO_SMALL. * Read/write accept regular files only; final-component symlinks are rejected. * Write copies bytes at submission, stages an exclusive same-directory temp, * preserves POSIX mode of an existing regular file, then renames atomically. * Windows rejects read-only destinations; replacement gets the directory's * inherited permissions and does not preserve other destination attributes. * Symlinks/nonregular destinations are rejected. ACLs/xattrs/ownership are not * preserved; replacement requires a directory writable by the current user. * New POSIX files use mode 0600. No power-loss durability or conflict detection promise. * No silent truncate fallback. Cancellation before rename leaves the target intact. */ UHDK_API uhdk_status UHDK_CALL uhdk_file_read_all_async( uhdk_file *file, uint64_t max_bytes, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_file_write_all_async( uhdk_file *file, uhdk_bytes bytes, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_file_get_info_async(uhdk_file *file, uhdk_request_id *out); typedef uint32_t uhdk_dialog_kind; enum { UHDK_DIALOG_OPEN_FILE = 0, UHDK_DIALOG_OPEN_FILES = 1, UHDK_DIALOG_OPEN_DIRECTORY = 2, UHDK_DIALOG_SAVE_FILE = 3 }; typedef struct uhdk_file_filter { uhdk_text label; uhdk_text_list extensions; /* Without dots; empty means all files. */ } uhdk_file_filter; typedef struct uhdk_file_dialog_desc { uint32_t struct_size; uhdk_dialog_kind kind; uhdk_window *parent; uhdk_text title; uhdk_text accept_label; uhdk_text suggested_name; /* Empty = native default. */ uhdk_file *initial_directory; const uhdk_file_filter *filters; uint64_t filters_len; } uhdk_file_dialog_desc; UHDK_API uhdk_status UHDK_CALL uhdk_file_dialog_show_async( uhdk_app *app, const uhdk_file_dialog_desc *desc, uhdk_request_id *out ); /* Plain-text clipboard only. Writes copy UTF-8 before returning; empty text is * valid. Reads negotiate native text formats internally and complete with text * borrowed for the event batch lifetime. NOT_FOUND means no text format. * max_bytes bounds the UTF-8 result; excess returns BUFFER_TOO_SMALL, never a * truncated string. A read remains async even when a backend can read instantly. */ UHDK_API uhdk_status UHDK_CALL uhdk_clipboard_write_text_async( uhdk_app *app, uhdk_text text, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_clipboard_read_text_async( uhdk_app *app, uint64_t max_bytes, uhdk_request_id *out ); /* Incoming file drops only. When enabled, Uhdk negotiates copy-only file drops * and emits DROP_FILES with borrowed file references plus the drop position. * Retain references to use beyond the event batch lifetime. Directory references are allowed; * the application decides whether to open them. No source deletion, outgoing * drag, data offers, or application-visible drop-completion transaction. */ UHDK_API uhdk_status UHDK_CALL uhdk_window_set_drop_enabled_async( uhdk_window *window, uhdk_bool enabled, uhdk_request_id *out ); /* Menus are immutable snapshots. Command IDs are application-assigned, nonzero * and unique within a menu tree. Shortcuts use logical keys + modifiers. * Created menus have one reference; attached menus are retained internally. * Retain/release manage references; the final release destroys the menu. */ typedef uint32_t uhdk_menu_role; enum { UHDK_MENU_ROLE_NONE = 0, UHDK_MENU_ROLE_ABOUT, UHDK_MENU_ROLE_QUIT, UHDK_MENU_ROLE_COPY, UHDK_MENU_ROLE_CUT, UHDK_MENU_ROLE_PASTE, UHDK_MENU_ROLE_SELECT_ALL, UHDK_MENU_ROLE_UNDO, UHDK_MENU_ROLE_REDO }; typedef uint32_t uhdk_menu_flags; enum { UHDK_MENU_DISABLED = 1u << 0, UHDK_MENU_CHECKED = 1u << 1, UHDK_MENU_SEPARATOR = 1u << 2 }; typedef struct uhdk_menu_item { uint64_t command_id; uhdk_text label; uhdk_menu_flags flags; uhdk_menu_role role; uhdk_key shortcut_key; uhdk_modifier_flags shortcut_modifiers; uhdk_menu *submenu; /* Retained by the created parent menu. */ } uhdk_menu_item; UHDK_API uhdk_status UHDK_CALL uhdk_menu_create( uhdk_app *app, const uhdk_menu_item *items, uint64_t len, uhdk_menu **out ); UHDK_API uhdk_status UHDK_CALL uhdk_menu_set_item_flags_async( uhdk_menu *menu, uint64_t command_id, uhdk_menu_flags flags, uhdk_request_id *out ); UHDK_API void UHDK_CALL uhdk_menu_retain(uhdk_menu *menu); UHDK_API void UHDK_CALL uhdk_menu_release(uhdk_menu *menu); UHDK_API uhdk_status UHDK_CALL uhdk_app_set_menu_async( uhdk_app *app, uhdk_menu *menu, uhdk_request_id *out ); /* NULL clears. */ UHDK_API uhdk_status UHDK_CALL uhdk_window_set_menu_async( uhdk_window *window, uhdk_menu *menu, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_menu_popup_async( uhdk_menu *menu, uhdk_window *window, uhdk_point position, uhdk_request_id *out ); UHDK_API uhdk_status UHDK_CALL uhdk_app_set_icon_async( uhdk_app *app, const uhdk_image *image, uhdk_request_id *out ); /* Successful shell completion means handed off to the OS, not that a target * application finished loading. URLs are not shell command strings. */ UHDK_API uhdk_status UHDK_CALL uhdk_shell_open_url_async( uhdk_app *app, uhdk_text url, uhdk_request_id *out ); typedef uint32_t uhdk_change_flags; enum { UHDK_CHANGE_CREATED = 1u << 0, UHDK_CHANGE_REMOVED = 1u << 1, UHDK_CHANGE_CONTENT = 1u << 2, UHDK_CHANGE_METADATA = 1u << 3, UHDK_CHANGE_RENAMED = 1u << 4, UHDK_CHANGE_RESCAN_REQUIRED = 1u << 5, UHDK_CHANGE_ROOT_INVALIDATED = 1u << 6 }; /* Watch a document path or a directory's immediate children; never recursive. * Document watches follow the named path across atomic-save replacement, using * parent-directory observation where needed. Removal emits REMOVED; subsequent * recreation emits CREATED. If observation can no longer continue (for example, * parent removal), emit ROOT_INVALIDATED and let the app recreate the watch. * Changes may coalesce and are hints, not a transaction log. Renames may arrive * as remove/create; no rename pairing is promised. RESCAN_REQUIRED means inspect * current state. Pending notifications are suppressed after destroy; already borrowed batches remain valid. */ UHDK_API uhdk_status UHDK_CALL uhdk_watch_create_async(uhdk_file *root, uhdk_request_id *out); UHDK_API void UHDK_CALL uhdk_watch_destroy(uhdk_watch *watch); typedef uint32_t uhdk_appearance; enum { UHDK_APPEARANCE_UNKNOWN = 0, UHDK_APPEARANCE_LIGHT = 1, UHDK_APPEARANCE_DARK = 2 }; typedef struct uhdk_preferences { uint32_t struct_size; uhdk_appearance appearance; uhdk_bool reduced_motion; uhdk_bool high_contrast; } uhdk_preferences; UHDK_API uhdk_status UHDK_CALL uhdk_app_get_preferences(uhdk_app *app, uhdk_preferences *out); /* Event payloads. Window is NULL for app-wide events. time_ns is monotonic time * relative to app creation, not wall time. Do not persist event pointers. * Destroying a window suppresses queued window input/redraw events and cancels * its requests. REQUEST_DONE never includes a dangling window pointer. */ typedef uint32_t uhdk_event_type; enum { UHDK_EVENT_READY = 1, UHDK_EVENT_STOPPED, UHDK_EVENT_WAKE, UHDK_EVENT_QUIT_REQUESTED, UHDK_EVENT_APP_ACTIVATION, UHDK_EVENT_OPEN_FILES, UHDK_EVENT_PREFERENCES_CHANGED, UHDK_EVENT_WINDOW_CLOSE_REQUESTED, UHDK_EVENT_WINDOW_METRICS, UHDK_EVENT_WINDOW_REDRAW, UHDK_EVENT_KEY, UHDK_EVENT_POINTER_ENTER, UHDK_EVENT_POINTER_LEAVE, UHDK_EVENT_POINTER_MOVE, UHDK_EVENT_POINTER_BUTTON, UHDK_EVENT_POINTER_MODE_CHANGED, UHDK_EVENT_SCROLL, UHDK_EVENT_TEXT_COMMIT, UHDK_EVENT_TEXT_PREEDIT, UHDK_EVENT_TEXT_COMPOSITION_END, UHDK_EVENT_EDIT_COMMAND, UHDK_EVENT_MENU_COMMAND, UHDK_EVENT_DROP_FILES, UHDK_EVENT_WATCH, UHDK_EVENT_REQUEST_DONE }; typedef uint32_t uhdk_request_kind; enum { UHDK_REQUEST_FILE_DIALOG = 1, UHDK_REQUEST_CLIPBOARD_TEXT, UHDK_REQUEST_MENU_POPUP, UHDK_REQUEST_SHELL, UHDK_REQUEST_COMMAND, UHDK_REQUEST_WINDOW, UHDK_REQUEST_GL_CONTEXT, UHDK_REQUEST_WATCH, UHDK_REQUEST_FILE_READ, UHDK_REQUEST_FILE_WRITE, UHDK_REQUEST_FILE_INFO }; /* Semantic requests, not edits performed by UHDK. The application owns text, * grapheme/word boundaries, bidi/layout, selection, clipboard actions and history. * LEFT/RIGHT are visual; BACKWARD/FORWARD follow logical document order. * LINE uses visual lines; LEFT_END/RIGHT_END explicitly select their visual edge. * LINE_START/END follow writing direction; PARAGRAPH uses document boundaries. * PAGE moves the caret by a viewport; SCROLL moves the viewport without the caret. * Deletion first removes the selection; otherwise it removes the indicated unit. * NEWLINE requests a paragraph separator; LINE_BREAK requests a soft line break. * TAB/BACKTAB may insert text or traverse focus, as decided by the application. * CANCEL is delivered only after native composition handling declines/finishes it. * Native default gestures vary: macOS uses AppKit selectors, Windows/Wayland * use Ctrl-based editing defaults. Commands without customary platform gestures * remain application-bindable intents, not invented global shortcuts. */ typedef uint32_t uhdk_edit_command; enum { UHDK_EDIT_COPY = 1, UHDK_EDIT_CUT, UHDK_EDIT_PASTE, UHDK_EDIT_SELECT_ALL, UHDK_EDIT_UNDO, UHDK_EDIT_REDO, UHDK_EDIT_DELETE_BACKWARD, UHDK_EDIT_DELETE_FORWARD, UHDK_EDIT_MOVE_LEFT, UHDK_EDIT_MOVE_RIGHT, UHDK_EDIT_MOVE_UP, UHDK_EDIT_MOVE_DOWN, UHDK_EDIT_LINE_START, UHDK_EDIT_LINE_END, UHDK_EDIT_DOCUMENT_START, UHDK_EDIT_DOCUMENT_END, UHDK_EDIT_WORD_LEFT, UHDK_EDIT_WORD_RIGHT, UHDK_EDIT_NEWLINE, UHDK_EDIT_TAB, UHDK_EDIT_BACKTAB, UHDK_EDIT_LINE_BREAK, UHDK_EDIT_CANCEL, UHDK_EDIT_DELETE_WORD_BACKWARD, UHDK_EDIT_DELETE_WORD_FORWARD, UHDK_EDIT_DELETE_LINE_START, UHDK_EDIT_DELETE_LINE_END, UHDK_EDIT_DELETE_PARAGRAPH_START, UHDK_EDIT_DELETE_PARAGRAPH_END, UHDK_EDIT_MOVE_BACKWARD, UHDK_EDIT_MOVE_FORWARD, UHDK_EDIT_WORD_BACKWARD, UHDK_EDIT_WORD_FORWARD, UHDK_EDIT_PARAGRAPH_START, UHDK_EDIT_PARAGRAPH_END, UHDK_EDIT_PAGE_UP, UHDK_EDIT_PAGE_DOWN, UHDK_EDIT_SCROLL_PAGE_UP, UHDK_EDIT_SCROLL_PAGE_DOWN, UHDK_EDIT_SCROLL_DOCUMENT_START, UHDK_EDIT_SCROLL_DOCUMENT_END, UHDK_EDIT_LINE_LEFT_END, UHDK_EDIT_LINE_RIGHT_END }; /* Event data is a pointer to the named payload type selected by type. * All payloads are immutable and naturally aligned. data_size is the payload * size in bytes; check it before accessing fields introduced by newer versions. * Payload-free events have NULL data and data_size == 0. Unknown types may be * ignored. Event envelopes have a fixed stride within each returned batch. */ typedef struct uhdk_key_event { uint32_t physical_key; uhdk_key logical_key; uhdk_modifier_flags modifiers; uhdk_bool pressed; uhdk_bool repeat; } uhdk_key_event; typedef struct uhdk_pointer_event { uhdk_point position; uhdk_point delta; /* delta uses logical units. */ uhdk_pointer_button button; uhdk_modifier_flags modifiers; uhdk_bool pressed; uint32_t click_count; } uhdk_pointer_event; typedef struct uhdk_scroll_event { uhdk_point position; uhdk_point delta; /* Positive scroll = toward right/bottom. */ uhdk_scroll_unit unit; uhdk_scroll_phase phase; uhdk_modifier_flags modifiers; uhdk_bool momentum; } uhdk_scroll_event; typedef struct uhdk_text_event { uhdk_text text; uint64_t revision; uhdk_bool has_replacement; uint32_t reserved; uint64_t replacement_start; uint64_t replacement_len; uint64_t selection_start; uint64_t selection_len; /* Within preedit text. */ } uhdk_text_event; typedef struct uhdk_edit_event { uhdk_edit_command command; uhdk_bool extend_selection; /* Only meaningful for caret movement. */ } uhdk_edit_event; typedef struct uhdk_drop_files_event { uhdk_file_list files; uhdk_point position; } uhdk_drop_files_event; typedef struct uhdk_menu_event { uint64_t command_id; } uhdk_menu_event; typedef struct uhdk_watch_event { uhdk_watch *watch; uhdk_change_flags flags; uhdk_status status; /* Non-OK: watch failure; rescan/recreate as needed. */ uhdk_file *file; /* Nullable when only the watched root is known. */ } uhdk_watch_event; typedef struct uhdk_activation_event { uhdk_bool active; } uhdk_activation_event; typedef struct uhdk_pointer_mode_event { uhdk_pointer_mode mode; } uhdk_pointer_mode_event; typedef struct uhdk_window_result { uhdk_window *window; } uhdk_window_result; typedef struct uhdk_gl_context_result { uhdk_gl_context *gl_context; } uhdk_gl_context_result; typedef struct uhdk_watch_result { uhdk_watch *watch; } uhdk_watch_result; /* Request data mapping (only valid on UHDK_STATUS_OK): * FILE_DIALOG: uhdk_file_list; CLIPBOARD_TEXT: uhdk_text; * WINDOW: uhdk_window_result; GL_CONTEXT: uhdk_gl_context_result; * WATCH: uhdk_watch_result; FILE_READ: uhdk_bytes; FILE_INFO: uhdk_file_info. * SHELL, COMMAND, FILE_WRITE, MENU_POPUP and failures have NULL/0 data. */ typedef struct uhdk_request_result { uhdk_request_id id; uhdk_request_kind kind; uhdk_status status; uhdk_text diagnostic; /* Optional; for humans, never parse for logic. */ const void *data; uint64_t data_size; } uhdk_request_result; /* Event data mapping: * APP_ACTIVATION: uhdk_activation_event; OPEN_FILES: uhdk_file_list; * WINDOW_METRICS: uhdk_window_metrics; PREFERENCES_CHANGED: uhdk_preferences; * KEY: uhdk_key_event; POINTER_ENTER/LEAVE/MOVE/BUTTON: uhdk_pointer_event; * POINTER_MODE_CHANGED: uhdk_pointer_mode_event; SCROLL: uhdk_scroll_event; * TEXT_COMMIT/PREEDIT: uhdk_text_event; EDIT_COMMAND: uhdk_edit_event; * DROP_FILES: uhdk_drop_files_event; MENU_COMMAND: uhdk_menu_event; * WATCH: uhdk_watch_event; REQUEST_DONE: uhdk_request_result. * All other events have NULL/0 data. */ struct uhdk_event { uint32_t struct_size; uhdk_event_type type; uint64_t time_ns; uint64_t input_id; /* Correlates raw key and native edit/text; zero for non-input. */ uhdk_window *window; const void *data; uint64_t data_size; }; /* Lifecycle policy: close/quit requests are advisory. Destroy the window or call * uhdk_app_quit() to accept; otherwise the application stays alive. Closing the * last window does not automatically quit. App destruction invalidates ALL child * handles, including retained files/menus; callers must stop other threads from * waking the app first. Native menu roles produce EDIT_COMMAND/QUIT_REQUESTED * where applicable; custom menu items produce MENU_COMMAND. A popup selection * delivers its command event first and REQUEST_DONE afterward; process the * command only once. Popup dismissal completes as UHDK_STATUS_CANCELLED. * * Boundaries: no widget/layout engine, text rasterizer, high-level renderer, * networking, process spawning, global input hooks, or streaming filesystem API. * Deferred: generic clipboard data, outgoing drag/drop, custom cursors, relative * pointer input, transparent windows, display enumeration/positioning, tray items, * badges/progress/attention, recursive watching, accent colors, accessibility * semantic trees, notifications, persistent access bookmarks and streaming data. */ #ifdef UHDK_ENABLE_NATIVE /* Experimental platform escape hatch; native handles may require OS headers. */ /* Platform escape hatch. Returned handles are borrowed and must not be released. * macOS: primary=NSWindow*, secondary=NSView*; Windows: primary=HWND. * Wayland: primary=wl_display*, secondary=wl_surface*. * X11: primary=Display*, value=XID. Lifetime ends with the Uhdk window. * With UHDK_WINDOW_OPENGL, Uhdk owns drawable setup; do not replace its native * view/surface or pixel format. Otherwise the external renderer owns setup. * Borrowing a handle does not change platform threading rules: AppKit window * and view operations still belong on the macOS main thread. */ typedef struct uhdk_native_window { uint32_t struct_size; uhdk_backend backend; void *primary; void *secondary; uint64_t value; } uhdk_native_window; UHDK_API uhdk_status UHDK_CALL uhdk_window_get_native(uhdk_window *window, uhdk_native_window *out); #endif /* UHDK_ENABLE_NATIVE */ #ifdef __cplusplus } /* extern "C" */ #endif #ifdef UHDK_ENABLE_OPENGL /* GLAD 2: OpenGL 3.3 core, no extensions, explicit per-context function table. * Do not combine with platform GL headers or GL/glcorearb.h. * The loader implementation is linked into libuhdk; consumers must not define * GLAD_GL_IMPLEMENTATION when linking that library. * Load a GladGLContext via gladLoadGLContextUserPtr and an adapter calling * uhdk_gl_context_get_proc, inside a begin_frame/end_frame bracket. Keep one * table per context and use it only while that context is current. */ #if defined(UHDK_SHARED) #define GLAD_API_CALL_EXPORT #endif #include "glad/gl.h" #endif /* UHDK_ENABLE_OPENGL */ #endif /* UHDK_H_INCLUDED */