191 lines
9.5 KiB
Markdown
191 lines
9.5 KiB
Markdown
# Uhdk
|
|
|
|
A small desktop platform library written in Zig 0.16.0 with a C99 ABI.
|
|
It provides windows, input and IME, clipboard, file dialogs, drag and drop, menus,
|
|
async file operations, file watching, and OpenGL context creation. The macOS
|
|
backend uses AppKit, OpenGL, and FSEvents through a private Objective-C bridge.
|
|
|
|
macOS builds require Xcode Command Line Tools. The Windows Win32/WGL backend
|
|
cross-compiles for `x86_64-windows-gnu`; runtime verification on Windows is pending. It requires
|
|
Windows 10 version 1703 or later and an OpenGL 3.3-capable driver.
|
|
|
|
The Linux Wayland preview implements windows, EGL/OpenGL, keyboard/pointer and
|
|
text input, clipboard/file drops, portal dialogs/preferences, activation, icons,
|
|
file watches, and Plasma app/window menu export, initially tested on KDE Plasma.
|
|
Linux context menus use a small software renderer and a bundled 16px Roboto
|
|
bitmap; X11 remains unsupported. Desktop dependencies are limited to vendored
|
|
ABI headers/protocol bindings and runtime-loaded system libraries. Normal
|
|
builds need Zig alone; the optional `shell.nix` supplies maintainer tools and
|
|
NixOS runtime library paths. See [Linux support and limitations](src/linux/README.md).
|
|
|
|
## Experimental desktop input and window observations
|
|
|
|
macOS uses AppKit editing selectors. Windows and Wayland share Ctrl-based defaults
|
|
for word deletion, paragraph/page movement, clipboard actions, undo/redo, Backtab,
|
|
Shift+Enter and cancellation. Shift extends selection only for movement. Alt/Super
|
|
combinations are not interpreted as Ctrl editing shortcuts. Clipboard shortcuts
|
|
work without a menu, with menu actions taking precedence when available.
|
|
Applications own execution, Unicode boundaries, layout and undo history. Commands
|
|
without customary shortcuts (such as line-boundary deletion on Windows) have no
|
|
invented default binding.
|
|
|
|
Publish an acknowledged text snapshot after consuming text/edit events. On macOS,
|
|
further native text input waits after semantic edits, including ignored commands;
|
|
acknowledge asynchronous paste after applying its result. Disabling text input or
|
|
losing window focus drops that deferred input. Windows IMM emits ordered text
|
|
without surrounding-text replacement ranges. Wayland invalidates old IME snapshot
|
|
serials after document/selection-changing commands; new surrounding text is
|
|
published after acknowledgment. It does not apply AppKit's input-queue mechanism.
|
|
|
|
`WINDOW_METRICS` is the resize and state-change hook. Geometry state is independent
|
|
of minimization. Observation bits indicate which facts are known:
|
|
|
|
| Backend | Known observations |
|
|
| --- | --- |
|
|
| macOS | Minimized, live resize, occluded |
|
|
| Windows | Minimized, interactive size/move loop |
|
|
| Wayland | Resizing; rendering suspension with xdg-shell v6+ |
|
|
|
|
A clear observation bit means unknown, not false. Wayland cannot report minimized
|
|
state or directly undo minimization, and suspension is not proof of occlusion.
|
|
Explicitly unavailable Wayland window-manager actions complete with `UNSUPPORTED`.
|
|
Render hints do not replace presentation pacing or event processing.
|
|
|
|
This library is unreleased and experimental; the changed C struct layout requires
|
|
rebuilding consumers, without ABI compatibility guarantees. Windows and Linux
|
|
runtime qualification must accompany cross-compilation checks.
|
|
|
|
## Build
|
|
|
|
```sh
|
|
zig build # zig-out/lib/libuhdk.a and zig-out/include/
|
|
zig build -Dshared=true # platform shared library and headers
|
|
zig build test # run core library tests on the native target
|
|
zig build -Dtarget=x86_64-windows-gnu
|
|
zig build -Dtarget=x86_64-windows-gnu -Dshared=true
|
|
zig build check -Dtarget=x86_64-windows-gnu # compile only
|
|
```
|
|
|
|
## Use from Zig
|
|
|
|
To use a local checkout at `vendor/uhdk`, add it to `build.zig.zon`:
|
|
|
|
```zig
|
|
.dependencies = .{
|
|
.uhdk = .{ .path = "vendor/uhdk" },
|
|
},
|
|
```
|
|
|
|
In your build, pass the application's target and optimization options:
|
|
|
|
```zig
|
|
const uhdk = b.dependency("uhdk", .{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
});
|
|
exe.root_module.addImport("uhdk", uhdk.module("uhdk"));
|
|
```
|
|
|
|
`@import("uhdk")` provides translated public C declarations, including the bundled
|
|
OpenGL and experimental native-handle declarations, and links the library and required system frameworks. The
|
|
`uhdk` artifact is also available through `uhdk.artifact("uhdk")` for C consumers
|
|
or custom bindings. Set `.shared = true` to build a dynamic library; applications
|
|
must arrange its runtime location/rpath.
|
|
|
|
## C interface
|
|
|
|
- [uhdk.h](include/uhdk.h): desktop API, lifecycle, ownership, and threading contracts.
|
|
- [OPENGL-SOURCES.md](include/OPENGL-SOURCES.md): GLAD generation recipe and Khronos header provenance and licenses.
|
|
|
|
All UHDK declarations live in `uhdk.h`. Before its first inclusion, define
|
|
`UHDK_ENABLE_OPENGL` to include the bundled GLAD 2 OpenGL 3.3 core declarations
|
|
and per-context loader table, and/or `UHDK_ENABLE_NATIVE` for the experimental
|
|
native-window handle API:
|
|
|
|
```c
|
|
#define UHDK_ENABLE_OPENGL
|
|
#define UHDK_ENABLE_NATIVE
|
|
#include "uhdk.h"
|
|
```
|
|
|
|
Presence enables each flag; leave it undefined to disable it. These flags select
|
|
header declarations, not library backends. UHDK's own context-management API is
|
|
always declared. The supplied Zig module enables both sections. The vendored GL
|
|
headers remain unmodified dependencies rather than being pasted into `uhdk.h`.
|
|
|
|
The public interface uses pointer-and-length text, `char` booleans, and `float`
|
|
coordinates. The native bridge is private; consumers do not need Objective-C.
|
|
|
|
The library includes the GLAD loader implementation. With a current context inside
|
|
`uhdk_gl_context_begin_frame` / `uhdk_gl_context_end_frame`, call
|
|
`gladLoadGLContextUserPtr` with a callback that resolves names using
|
|
`uhdk_gl_context_get_proc`. Keep a `GladGLContext` per native context; its members
|
|
are named `Clear`, `DrawArrays`, etc. Check `VERSION_3_3` after loading. Do not define
|
|
`GLAD_GL_IMPLEMENTATION` in your application when linking libuhdk.
|
|
|
|
On Windows, the static artifact is `zig-out/lib/uhdk.lib`; shared builds install
|
|
`zig-out/bin/uhdk.dll` and its import library under `zig-out/lib/`. Keep the DLL
|
|
beside your executable. The first lifecycle caller establishes the Windows
|
|
platform thread; its message loop also owns COM dialogs, menus, clipboard and
|
|
watch completion. Input uses IMM32 composition; TSF reconversion and pen/touch
|
|
are not implemented. Native UTF-16 path bytes are kept separately from the WTF-8
|
|
paths passed to Zig I/O. If a native path cannot be exported losslessly as a shell
|
|
URI, URI export returns `UNSUPPORTED`; native-path export still preserves it.
|
|
|
|
## Borrowed event payloads
|
|
|
|
Events and request results carry `const void *data` plus `data_size` instead of
|
|
public unions. Check the event type (or successful request kind), then read the
|
|
corresponding named payload struct listed in `uhdk.h`. Check `data_size` before
|
|
reading fields; unknown types can be ignored. Payload-free events and unsuccessful
|
|
request results have NULL data and zero size. Failure diagnostics remain available
|
|
in the request envelope.
|
|
|
|
```c
|
|
if (event->type == UHDK_EVENT_KEY &&
|
|
event->data_size >= sizeof(uhdk_key_event)) {
|
|
const uhdk_key_event *key = (const uhdk_key_event *)event->data;
|
|
/* Read key->logical_key, key->pressed, etc. here. */
|
|
}
|
|
```
|
|
|
|
One library-owned scratch arena holds the returned batch, its payload structs,
|
|
and nested spans. The next valid application-thread poll resets it, even if that
|
|
poll is empty or returns an error. Invalid arguments and wrong-thread calls do
|
|
not reset it. App destruction also invalidates everything. Rendering does not
|
|
advance this lifetime. Copy data you need to keep; copying an envelope alone
|
|
copies only its pointers. Retain file/menu references when needed beyond a poll,
|
|
but even retained handles cannot outlive the app.
|
|
|
|
UHDK is pre-release: API and ABI compatibility with earlier revisions is not
|
|
guaranteed. Set creation descriptors to `UHDK_ABI_VERSION`. Event envelopes have
|
|
a fixed array stride within each returned batch.
|
|
|
|
The scratch arena is maintained as UHDK-owned code in
|
|
[src/arenas.zig](src/arenas.zig), including its supporting storage and tests.
|
|
It originated in Ribtech p2; the MIT license and attribution are retained in that file.
|
|
|
|
## Optional profiling
|
|
|
|
`uhdk.h` provides scope, frame, thread-name and named allocation/
|
|
free macros. They compile away without evaluating arguments unless the consumer
|
|
defines `UHDK_PROFILE_BACKEND` to a quoted adapter header before including UHDK.
|
|
An adapter implements the macros documented in that header. Names require static
|
|
lifetime; pair zones on the same thread in nesting order and report frees before
|
|
releasing memory. Do not put allocation expressions inside profiling macros.
|
|
|
|
Internal CPU and memory instrumentation is disabled by default. A static consumer
|
|
can pass `.tracy = true` to `b.dependency("uhdk", ...)` (or `-Dtracy=true` when
|
|
building the archive) and link its own Tracy 0.14.1 client configured with
|
|
`TRACY_ENABLE` and `TRACY_ON_DEMAND`. UHDK never fetches or links Tracy itself.
|
|
Shared instrumentation is unsupported. Run unit tests with the default
|
|
configuration; enabled instrumentation requires the consumer's linked client.
|
|
|
|
The `UHDK` memory pool measures requested Zig backing allocations, including
|
|
objects and arena blocks. Win32 explicit calloc/free blocks use a separate
|
|
`UHDK native` pool. OS/framework allocations and GPU memory are excluded.
|
|
On-demand capture omits allocations made before connection, so it is not a full
|
|
live-heap snapshot. Failed allocations emit nothing; in-place resizes emit
|
|
free + alloc. Moving reallocations use alloc/copy/free to preserve event ordering.
|
|
Disabled builds use the original allocator directly.
|