Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,23 @@ Outputs - Windows: `out/build/Debug/{neui_example.exe, neui.lib, neui-win32host.

**Tests**: `tests/` is a Tier-1 header-only unit suite (`neui_tests`) over the portable logic in `hosts/shared/*.h` - links no host and no backend, builds everywhere including the null platform. Toggle with `-DNEUI_BUILD_TESTS=OFF`. Run directly or via `ctest --test-dir out/build -C Debug`. Linux-only extra targets: `neui_cairo_smoke` (offscreen Cairo, ctest-registered) and `neui_embed_smoke` (fake-DAW embedding, needs a live X display). Windows-only: `neui_embed_smoke_win32` (fake-DAW embedding into a foreign HWND; asserts the client-area contract and that the first `NEUI_EVENT_RESIZE` is in logical px - only meaningful on a scaled display; built but not ctest-registered).

**LVGL host - EXPERIMENTAL, off by default, paused pending hardware.** `-DNEUI_WITH_LVGL=ON` (Windows only) swaps the xpl host's backend + platform layer for `neui-backend-lvgl` + `platform_lvgl.cpp` and adds `neui_lvgl_example`. It is a prototype: several host features are stubbed and it is not for product work. **Read `docs/host-lvgl.md` before touching it** (build flags, what works, what is stubbed); design record + measurements in `plans/lvgl-host-approach-c.md`.

## Per-platform host + backend selection

- **Windows**: `neui-win32host` + `neui-xplhost`; backend `neui-backend-d2d`; xpl platform `platform_win32.cpp`.
- **macOS**: `neui-macoshost` + `neui-xplhost`; backend `neui-backend-cg`; xpl platform `platform_macos.mm`.
- **Linux** (X11): `neui-xplhost` only (no native host); backend `neui-backend-cairo` (software, blitted via XShm/XPutImage); xpl platform `platform_linux.cpp`. Clipboard (CLIPBOARD + PRIMARY + INCR), full XDND v5, neui-drawn message box, in-frame menubar, XI2 smooth scroll, D-Bus theme tracking, and DAW-embedding seams all live here - **see `docs/host-linux.md`**.
- **Other**: `neui-xplhost`; backend `neui-backend-null`; xpl platform `platform_null.cpp`.
- **LVGL** (opt-in, **experimental**, Windows only): `neui-xplhost`; backend `neui-backend-lvgl`; xpl platform `platform_lvgl.cpp`. Replaces the Windows pairing above when `-DNEUI_WITH_LVGL=ON` - **see `docs/host-lvgl.md`**.

Top-level CMakeLists gates each platform-specific subdirectory; the example links the native host only when present.

## Architecture (file map)

- **Public C API** `include/neui/`: `neui.h` (`neui_init` + `neui_register` + `neui_get_api`); sub-headers under `d/`: `api.h`, `keys.h`, `widgets.h`, `events.h`, `items.h`, `tree.h`, `attrs.h`, `clipboard.h`, `dnd.h`, `commands.h`, `renderer.h`, `painter.h`, `gradient.h`, `path_style.h`, `assets.h`, `compound.h`, `behavior.h`, `component.h`, `filter.h`, `grid.h`, `scroll.h`, `menu.h`, `theme.h`, `notify.h`, `metrics.h`, `embed.h`.
- **Public C API** `include/neui/`: `neui.h` (`neui_init` + `neui_register` + `neui_get_api`); sub-headers under `d/`: `api.h`, `keys.h`, `widgets.h`, `events.h`, `items.h`, `tree.h`, `attrs.h`, `clipboard.h`, `dnd.h`, `commands.h`, `renderer.h`, `painter.h`, `gradient.h`, `path_style.h`, `assets.h`, `compound.h`, `behavior.h`, `component.h`, `filter.h`, `grid.h`, `scroll.h`, `menu.h`, `resource.h`, `theme.h`, `notify.h`, `metrics.h`, `embed.h`.
- **Core library** `src/neui.c`: host registry + `neui_init()` (fans out to per-host registration wrappers gated by `NEUI_HAS_*HOST`). Also `src/mujson.{h,cpp}`: `neui::mujson`, a minimal JSON-*like* parser (bare keys, `//` + `/* */` comments, single trailing comma, `\uXXXX`, depth-cap 128) compiled into `libneui`; `parse(str)->object_t` / `serialize(object_t)`; Tier-1 tested via `tests/test_mujson.cpp`.
- **Shared portable utilities** `hosts/shared/`, header-only, ODR-safe via `inline`: `tree.h` (`Tree<T>`), `attrs.h` (`AttrBag` + `attr_as_float` + `k_well_known_attrs`), `asset_store.h` (`AssetStore<Loader>` slot table), `clipboard_item.h`, `dnd_dispatch.h`, `dnd_modifier_suggest.h`, `edit_history.h`, `text_edit.h` (`TextEditState` shared by every text-input surface), `shortcut_format.h`, `theme_palette.h` (`ColorRole` / `Palette` / `current_palette`), `compound.h`, `behavior.h` / `behavior_runtime.h`, `image_filter.h`, `filter_graph.h`, `grid_model.h` / `widget_paint_grid.h`, `scrollbar.h`, `scroll_kinetics.h`, `widget_section_scroll.h`, `painter.h`, `widget_font.h`, `widget_paint_knob.h` / `_section.h` / `_compound.h`, `component_loader.h`. Platform-specific shared code under `hosts/shared/{win32,macos,linux}/`.
- **Shared portable utilities** `hosts/shared/`, header-only, ODR-safe via `inline`: `tree.h` (`Tree<T>`), `attrs.h` (`AttrBag` + `attr_as_float` + `k_well_known_attrs`), `asset_store.h` (`AssetStore<Loader>` slot table), `clipboard_item.h`, `dnd_dispatch.h`, `dnd_modifier_suggest.h`, `edit_history.h`, `text_edit.h` (`TextEditState` shared by every text-input surface), `shortcut_format.h`, `theme_palette.h` (`ColorRole` / `Palette` / `current_palette`), `compound.h`, `behavior.h` / `behavior_runtime.h`, `image_filter.h`, `filter_graph.h`, `grid_model.h` / `widget_paint_grid.h`, `scrollbar.h`, `scroll_kinetics.h`, `widget_section_scroll.h`, `painter.h`, `widget_font.h`, `widget_paint_knob.h` / `_section.h` / `_compound.h`, `component_loader.h`, `image_loader_stb.h` (the stb-based `platform_load_image` for platform layers with no OS imaging framework - Linux and the LVGL host). Platform-specific shared code under `hosts/shared/{win32,macos,linux}/`.
- **Win32 Host** `hosts/win32/`: native HWND host. `window.cpp` WinMain + pump; `host.{cpp,h}` Session + `WidgetData`; `widgets.cpp` full API; `asset_manager_w32.h`.
- **macOS Host** `hosts/macos/`: native AppKit host (`neui.host.macos`). `host.{h,mm}` Session; `widgets.mm` full API; `window.mm` (NSApp + `NEUINativeContentView` `isFlipped=YES` + `NEUINativePaintedView`); `asset_manager_macos.h`.
- **Crossplatform Host** `hosts/crossplatform/`: polymorphic widget hierarchy (`neui.host.crossplatform`). `host.{h,cpp}` `WidgetData` base + per-type subclasses (`FrameWidget` … `GridWidget`) with virtuals; `widgets.cpp` full API + `make_widget()`; `platform.h` cross-cutting seam (window / menubar / image / clipboard / IME / modal / focus); per-OS impls `platform_{win32.cpp,macos.mm,linux.cpp,null.cpp}`.
Expand All @@ -55,7 +58,7 @@ App: `APP_QUIT`. Mouse: `MOUSE_MOVE/ENTER/LEAVE`, `MOUSE_BUTTON_DOWN/UP/CLICK/DB

- **Host registry** - `neui_register(id, api)` at startup; `neui_get_api(NULL)` returns first registered. IDs: `"neui.host.win32"`, `"neui.host.macos"`, `"neui.host.crossplatform"`. Clients call `neui_init()` once to register every compiled-in host (gated on `NEUI_HAS_*HOST`). Order: native first, then xpl.
- **Per-host registration wrappers** - each host static lib exposes `extern "C"` wrappers (`neui_register_xplhost` / `_win32host` / `_macoshost`); they double as the linker forced-symbol references pulling the host's objects out of its static lib.
- **Named interface dispatch** - `get_interface(sess, name)` with version suffix (`/0`). Active: `NEUI_API_WIDGETS/_ITEMS/_TREE/_ATTRS/_CLIPBOARD/_DND/_COMMANDS/_ASSETS/_COMPOUND/_BEHAVIOR/_FILTER/_GRID/_SCROLL/_NOTIFY/_EMBED`. Optional client-side: `_MENU_CLIENT`, `_THEME_CLIENT`, `_GRID_CLIENT`. `_FILTER` is itself optional host-side (a host without off-screen surfaces may return nullptr); `_EMBED` (`d/embed.h`, DAW embedding for PLUGWINDOW) is exposed by the xpl host only.
- **Named interface dispatch** - `get_interface(sess, name)` with version suffix (`/0`). Active: `NEUI_API_WIDGETS/_ITEMS/_TREE/_ATTRS/_CLIPBOARD/_DND/_COMMANDS/_ASSETS/_COMPOUND/_BEHAVIOR/_FILTER/_GRID/_SCROLL/_NOTIFY/_EMBED`. Optional client-side: `_MENU_CLIENT`, `_THEME_CLIENT`, `_GRID_CLIENT`, `_RESOURCE_CLIENT` (client supplies resource bytes by name - images / fonts / component JSON / filmstrip sidecars - asked *before* the host's own filesystem + embedded-resource lookup; see `docs/rendering-and-assets.md`). `_FILTER` is itself optional host-side (a host without off-screen surfaces may return nullptr); `_EMBED` (`d/embed.h`, DAW embedding for PLUGWINDOW) is exposed by the xpl host only.
- **Session model** - 32-bit ID; slot-reused vector. Client passes `neui_client_t` with `get_interface` callback; host passes opaque token back on every callback.
- **Widget IDs** - upper 16 = owning session id, lower 16 = tree slot. Every API entry validates via `get_session_for_widget`; cross-session handles silently dropped. Sentinels (`widget_root = 0`, `widget_none = UINT32_MAX`) pass. Stale-after-slot-reuse not detected (deferred).
- **Deferred HWND** (win32) - logical state stored immediately; HWND / HMENU / HACCEL / HICON created on `widget_show()`; pending state flushed in `create_child_windows()`. Guard every API call with `hwnd == nullptr`.
Expand Down Expand Up @@ -174,6 +177,7 @@ Deep per-subsystem detail lives in `docs/`. **Read the relevant file before doin
- **Theme palette; frame resize / icon / focus** -> `docs/theming.md`.
- **Per-widget internals & enabled/disabled** -> `docs/widget-internals.md`. MULTILINE perf, hover/pressed visuals, DBLCLK->CLICK parity, disabled state per host.
- **Linux (X11 + Cairo) host internals** -> `docs/host-linux.md`. Selections + INCR clipboard, XDND, in-frame menubar, XI2 smooth scroll, D-Bus theme, embedding seams.
- **LVGL host (EXPERIMENTAL)** -> `docs/host-lvgl.md`. Opt-in embedded substrate for the xpl host: build flags, retained mirror layer, what works, what is stubbed, why it is paused pending hardware.
- **Deferred / known-limitation list** -> `docs/deferred-issues.md`.
- **Design rationale for shipped features** -> `docs/design-notes.md`. **Component-widget authoring how-to** -> `docs/component-widgets-howto.md`.

Expand All @@ -184,5 +188,6 @@ Active plans (`plans/`) are open or deferred work only - completed plans were re
- `win32-pointer-and-directmanipulation.md` - WM_POINTER pen/touch + DirectManipulation smooth-scroll on scrolling SECTION + GRID (deferred; binding spec for when it lands).
- `winui3-host.md` - third host backend feasibility analysis (deferred indefinitely).
- `wasm-host.md` - WebAssembly / Canvas-2D host feasibility analysis (deferred; phased path documented).
- `lvgl-port.md` - neui-on-LVGL feasibility investigation (no implementation proposed).
- `lvgl-port.md` - neui-on-LVGL feasibility investigation that picked the approach (superseded by the prototype below; kept for the impedance-mismatch analysis).
- `lvgl-host-approach-c.md` - the **experimental** LVGL host: design record, per-milestone detail and all measurements. Prototype built + evaluated; **work paused until real target hardware is available** (the open items - appearance cache, embedded display driver / VGLite - need a panel and an MCU to evaluate). Reader-facing status: `docs/host-lvgl.md`.
- `how-to-port.md` - reference playbook for new-platform ports.
68 changes: 68 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ option(NEUI_BUILD_TESTS "Build the neui unit tests" ${NEUI_IS_TOP_LEVEL
# don't break the build - the project's documented default); CI turns it ON
# (-DNEUI_WERROR=ON) so a new warning fails the build on every host.
option(NEUI_WERROR "Treat compiler warnings as errors" OFF)
# EXPERIMENTAL LVGL host (docs/host-lvgl.md; design record + measurements in
# plans/lvgl-host-approach-c.md): a rendering backend + xpl platform layer that
# draw through LVGL. OFF by default - existing builds are unaffected, and this is
# not for product work: several host features are stubbed and the remaining work
# is paused until real target hardware is available. When ON, LVGL is fetched
# from git (see backends/lvgl/). Windows-only; configuration FAILS on other
# platforms (no silent fallback).
option(NEUI_WITH_LVGL "EXPERIMENTAL: build the LVGL host + backend (see docs/host-lvgl.md)" OFF)

# iOS is an APPLE platform too, so it must be detected distinctly from macOS:
# the CoreGraphics backend is shared (gated on APPLE), but the AppKit macOS
Expand Down Expand Up @@ -139,6 +147,20 @@ endif()
if(UNIX AND NOT APPLE)
add_subdirectory("backends/cairo")
endif()
# LVGL backend + platform prototype - gated; FetchContent of LVGL lives in the
# subdirectory. Must fail hard on platforms that can't run the LVGL host.
if(NEUI_WITH_LVGL)
if(NOT WIN32)
message(FATAL_ERROR
"NEUI_WITH_LVGL is currently supported only on Windows (it uses LVGL's "
"native Windows display driver, LV_USE_WINDOWS). This platform cannot "
"run the LVGL host - configure with -DNEUI_WITH_LVGL=OFF.")
endif()
message(STATUS
"neui: EXPERIMENTAL LVGL host enabled - prototype only, several host "
"features are stubbed. See docs/host-lvgl.md.")
add_subdirectory("backends/lvgl")
endif()
add_subdirectory("backends/null")
add_subdirectory("hosts/crossplatform")

Expand Down Expand Up @@ -442,6 +464,35 @@ if(APPLE)
target_link_libraries(neui_overlay_example PRIVATE neui-macoshost)
endif()

# Client resource provider example (NEUI_API_RESOURCE_CLIENT). The client builds
# a BMP in memory at startup and hands the bytes to the host on demand, so the
# image resolves under a name that exists nowhere on disk. WIN32 subsystem like
# the other GUI examples - the native Win32 host's window/pump lives behind
# WinMain, so a console-subsystem build never gets a frame.
if(MSVC)
add_executable(neui_resource_client_example WIN32
examples/resource_client_example.cpp
examples/neui_example.rc)
target_link_options(neui_resource_client_example PRIVATE "/MANIFEST:NO")
set_property(TARGET neui_resource_client_example APPEND PROPERTY
LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/examples/neui_example.manifest")
elseif(WIN32)
add_executable(neui_resource_client_example WIN32
examples/resource_client_example.cpp)
elseif(APPLE)
add_executable(neui_resource_client_example MACOSX_BUNDLE
examples/resource_client_example.cpp)
else()
add_executable(neui_resource_client_example examples/resource_client_example.cpp)
endif()
target_link_libraries(neui_resource_client_example PRIVATE neui neui-xplhost)
if(WIN32)
target_link_libraries(neui_resource_client_example PRIVATE neui-win32host)
endif()
if(APPLE)
target_link_libraries(neui_resource_client_example PRIVATE neui-macoshost)
endif()

# Arc example. Value-driven arc / ring / pie compound layers
# (NEUI_COMPOUND_LAYER_ARC) - ring, elliptical ring, filled wedge, and a
# center-polarity bipolar arc, each bound to NEUI_PARAM_VALUE.
Expand Down Expand Up @@ -632,6 +683,23 @@ if(APPLE)
target_link_libraries(neui_tabview_example PRIVATE neui-macoshost)
endif()

# LVGL host prototype example (plans/lvgl-host-approach-c.md): the knob-heavy
# measurement screen for the Milestone 3 FPS/CPU evaluation. Console app so
# LVGL's perf monitor (LOG_MODE) prints sysmon lines to stdout. Pinned to the
# crossplatform host, which this build pairs with the LVGL platform+backend.
if(NEUI_WITH_LVGL)
add_executable(neui_lvgl_example examples/lvgl_example.cpp)
target_link_libraries(neui_lvgl_example PRIVATE neui neui-xplhost)
if(WIN32)
target_link_libraries(neui_lvgl_example PRIVATE neui-win32host)
endif()
# The IMAGE-widget check loads myimage.png from next to the executable.
add_custom_command(TARGET neui_lvgl_example POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/examples/myimage.png"
"$<TARGET_FILE_DIR:neui_lvgl_example>/myimage.png")
endif()

# Minimal README example - input + button + label echo. Pure C; mirrors the
# code block in README.md verbatim. Useful as a smoke test that the public
# C API stays usable from a one-file client.
Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ This includes using fonts, colors and sizes that are defined from the target sys
* iOS / iPadOS (implemented, native UIKit host + crossplatform host)
* crossplatform for all of the above
* embedded (DAW plugin windows via `NEUI_API_EMBED` on windows / macOS / linux, incl. X11 host run-loop integration)

## audio plugin UIs

For audio plugin editors (VST3 / CLAP / AU adapters), **prefer the crossplatform host** - select it explicitly with `neui_get_api("neui.host.crossplatform")` instead of taking the default. It renders pixel-identically on every platform and is the only host that implements `NEUI_API_EMBED` (`<neui/d/embed.h>`), which parents a `NEUI_W_PLUGWINDOW` into the DAW-provided native parent (HWND on windows, NSView* on macOS, X11 Window on linux). In embedded mode neui owns no event loop - never call `run()` or `pump_once()` from a plugin; on windows/macOS the DAW's own pump services the embedded frame, on linux register `embed->event_fd()` with the host run loop and drive `embed->pump_and_tick()` from its timer. The native win32/macOS hosts are for standalone applications and do not support embedding.
* bare-metal / MCU panels via LVGL (**experimental** prototype, opt-in and Windows-hosted for now; work resumes when target hardware is available - see `docs/host-lvgl.md`)

## how to use

Expand Down
Loading