Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Custom plug-in UI, base layer: concepts, editor seam, gestures, message bus#161

Open
jcelerier wants to merge 8 commits into
mainfrom
ui-split-2-ui-core
Open

Custom plug-in UI, base layer: concepts, editor seam, gestures, message bus#161
jcelerier wants to merge 8 commits into
mainfrom
ui-split-2-ui-core

Conversation

@jcelerier

@jcelerier jcelerier commented Jul 6, 2026

Copy link
Copy Markdown
Member

The UI base layer on top of the backend fixes: concepts, host glue and the message bus — no bundled UI framework. After this PR, a plug-in can ship an editor written with any UI stack (Dear ImGui, Qt, JUCE, raw platform code…) and get it embedded, clocked and wired to automation in CLAP, VST2 and VST3 alike.

What's in

  • Concepts (avnd/concepts/gui_window.hpp): avnd::gui_windowed_uiT::ui::window with open(gui_parent, gui_host) / close() / idle() / size(); gui_host is a fn-ptr automation-gesture protocol (flat param index, normalized [0,1], one begin/end per drag).
  • Editor resolution seam (binding/ui/editor.hpp): author window wins; a reference editor for declarative layouts only participates when a soft-UI backend is compiled in (AVND_SOFT_UI_EDITOR, PR 3) — this PR introduces the seam, not the backend.
  • Message bus (binding/ui/message_transport.hpp + serialization.hpp): ui::bus / Tier C send_message/process_message endpoints over lock-free moodycamel queues (audio thread never allocates or blocks); pfr-based serializer for string/vector/aggregate payloads.
  • Binding glue: clap.gui + clap.timer-support; VST2 EditOpen/EditClose/EditIdle/EditGetRect + audioMaster gestures; VST3 IPlugView with IPlugViewContentScaleSupport, per-platform UI tick (SetTimer / host IRunLoop / CFRunLoopTimer) and the IMessage bus across the component/controller split (UI-thread-only IMessage allocation, see binding/vst3/bus.hpp).
  • Tier C end-to-end: examples/Advanced/UI/CustomUiWindow.hpp — a gain whose editor is raw Win32/GDI on Windows and raw Xlib on Linux, no library; tests/ui/test_custom_ui_window.cpp embeds it in a real window, injects a drag, and asserts gestures + bus round-trip (passes on X11 with no soft stack present — the layering is real).

Validation

  • Linux (clang 19): clean build, 102/102 tests (the 2 new ones: Tier C editor, and the shared Win32/X11 GUI-test host layer they run on).

Part 2/3; stacks on #160, and #162 (the bundled Nuklear/pugl/canvas_ity editor) builds on this.

🤖 Generated with Claude Code

jcelerier and others added 7 commits July 6, 2026 18:10
avnd::gui_windowed_ui (T::ui::window with open/close/idle/size),
avnd::gui_parent / gui_host (host parent handle + automation-gesture
fn-ptr protocol: flat param index, normalized [0,1]), and
avnd::make_ui_editor which resolves, in order: the author-provided
Tier C window, then — only when a target is built with a soft-UI
backend (AVND_SOFT_UI_EDITOR) — the reference editor for declarative
layouts. Architecture plan in CUSTOM_UI_PLAN.md.

Co-Authored-By: Claude Fable 5 <[email protected]>
ui::bus / Tier C send_message/process_message endpoints over moodycamel
queues: UI thread enqueues (may allocate), audio thread try_enqueues on
preallocated storage and never blocks. avnd::bus_serial handles
non-trivially-copyable payloads (string/vector/aggregate); payloads a
serializer can't handle silently disable that bus direction, by design.

Co-Authored-By: Claude Fable 5 <[email protected]>
Editors resolve through avnd::make_ui_editor (author-provided Tier C
window, or the reference editor when a soft-UI backend is enabled at
build time). Automation gestures flow editor -> lock-free gesture queue
-> host params request_flush -> CLAP_EVENT_PARAM_GESTURE_BEGIN /
PARAM_VALUE / GESTURE_END in params.flush. The message bus rides the
same lock-free transport, drained in process() on the audio side and on
the editor timer on the UI side.

Co-Authored-By: Claude Fable 5 <[email protected]>
Same editor resolution and bus transport as the CLAP binding;
automation gestures map to audioMaster BeginEdit/Automate/EndEdit and
processor->UI messages drain on EditIdle and the editor frame hook.

Co-Authored-By: Claude Fable 5 <[email protected]>
- plug_view drives an avnd::gui_windowed_ui editor: controller-side
  model instance, IComponentHandler begin/perform/endEdit gestures,
  IPlugViewContentScaleSupport, and a UI-thread tick (Win32 SetTimer /
  host IRunLoop on Linux / CFRunLoopTimer on macOS) that runs
  editor->idle() and the bus pump.
- The message bus crosses the component/controller split via IMessage
  with the pfr serializer; IMessage is only allocated/sent on the UI
  thread (see the bus.hpp header comment for the pump protocol), the
  audio thread only enqueues.
- The prototype defines the plug-view + Linux windowing IIDs
  (IRunLoop, ITimerHandler, IEventHandler): an unresolved reference to
  any of them would pull commoniids.cpp.o out of libsdk_common.a, which
  then collides with every DEF_CLASS_IID under GNU ld.

Co-Authored-By: Claude Fable 5 <[email protected]>
CustomUiWindow: a gain with a hand-rolled editor (raw Win32 + GDI on
Windows, raw Xlib on Linux) through the avnd::gui_windowed_ui seam — no
UI library at all, so the contract is the whole story. Tested through
the CLAP binding by tests/ui/test_custom_ui_window.cpp: embed in a real
parent window, injected drag, gesture flow, message-bus round trip.
tests/ui/gui_test_host.hpp is the shared Win32/X11 host layer (parent
window, event pump with per-tick host callback, child discovery,
synthetic input, PPM capture) that the soft-editor tests reuse later.

Co-Authored-By: Claude Fable 5 <[email protected]>
The end-to-end editor tests embed real windows; without X11 dev headers
they are skipped at configure time, and without a display they cannot
run. Install libx11/xrandr/xext/xcursor + xvfb and run the test steps
under xvfb-run on Linux.

Co-Authored-By: Claude Fable 5 <[email protected]>
@jcelerier jcelerier changed the base branch from ui-split-1-backend-fixes to main July 6, 2026 22:18
@jcelerier jcelerier force-pushed the ui-split-2-ui-core branch from 5afa08b to 02e4ebc Compare July 6, 2026 22:18
CustomUiWindow.hpp carried both platform implementations of its author
window inline, guarded by a #if/#elif that made the file two editors in a
trenchcoat and dragged <windows.h>/<X11/Xlib.h> (plus their macro-hygiene
#undefs) into the middle of every plug-in TU that includes it.

Hide the window behind a forward-declared pimpl: the header now declares
only the avnd::gui_windowed_ui contract (open/close/idle/size) plus the
message-bus endpoints the bindings introspect, and holds a
std::unique_ptr<struct impl>. The two implementations move to sibling
translation units, CustomUiWindow.win32.cpp (raw Win32 + GDI) and
CustomUiWindow.x11.cpp (raw Xlib), each compiled into the plug-in modules
by examples.cmake for its platform (and linking X11 there, as before). The
header pulls in no windowing header at all -- exactly how a real toolkit
editor would hide behind its own opaque handle.

Behaviour is unchanged: the window class name, 360x120 size, gain gesture
flow and message bus are identical. test_custom_ui_window still passes
(clap module, live X display); vst3/vintage editor modules still build.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant