uhdk/include/uhdk.h

1132 lines
42 KiB
C

#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.
* <stdint.h> 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 <field>_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 <stdint.h>
/* 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 */