Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::format::ViewBridge Class Reference

#include <view_bridge.hpp>

Classes

struct  Options

Public Member Functions

 ViewBridge (Processor &processor, state::StateStore &store)
 ViewBridge (Processor &processor, state::StateStore &store, Options options)
 ViewBridge (Processor &processor, state::StateStore &store, runtime::AliveToken::Handle owner_alive)
 ViewBridge (Processor &processor, state::StateStore &store, runtime::AliveToken::Handle owner_alive, Options options)
 ~ViewBridge ()
 ViewBridge (const ViewBridge &)=delete
ViewBridge & operator= (const ViewBridge &)=delete
bool open (std::string *error=nullptr)
 Build the view.
bool prepare_first_frame (view::PluginViewHost &host)
 Content-first open: make host's first frame the settled document.
void notify_attached ()
 Fire on_view_opened(view) — called by the adapter once the view has been attached to its native parent.
std::unique_ptr< view::View > release_view ()
 Transfer ownership of the primary view to the caller while preserving the bridge's lifecycle dispatch.
void close ()
 Destroy the view.
void resize (uint32_t width, uint32_t height)
 Notify the bridge that the host resized the editor.
void notify_processor_destroyed () noexcept
 The owning adapter MUST call this before the referenced Processor is destroyed when the two have independent lifetimes (AU: the audio unit and the view controller).
bool notify_owner_released () noexcept
 Tell every view in the editor tree that the plug-in instance owning it is gone, by calling EditorOwnerObserver::on_owner_released() on each view that implements it (editor_owner.hpp), root first.
bool owner_is_alive () const noexcept
 Whether the adapter instance that owns both Processor and StateStore is still alive.
bool set_preferred_size (uint32_t width, uint32_t height)
 Update the editor's PREFERRED size (and derived aspect ratio) after the plugin requested an editor-initiated resize to a new natural size (see Processor::request_editor_resize).
bool poll_editor_reload ()
 Live editor reload (live-swap 1.9).
bool rebuild_primary_view ()
 Force a rebuild of the open primary view from create_view() now, transplanting the fresh content (children + background) into the stable root View.
bool is_open () const
ViewRole role () const
view::View * view ()
const view::View * view () const
bool uses_script_ui () const
view::HostParamSurface * host_params ()
 The runtime host-parameter surface installed on the open view tree.
void set_host_actions (view::HostActionSurface *actions)
 Install the command channel a routed view's Kind::action clicks reach (route_actions_to_host(true) → View::host_actions()).
view::HostActionSurface * host_actions () const
const std::shared_ptr< std::atomic< bool > > & alive_token () const
 Cross-thread liveness token.
view::ScriptedUiSession * scripted_ui ()
 Access the scripted UI session, if the primary view was built from a JS script.
const view::ScriptedUiSession * scripted_ui () const
void visit_scripted_ui (const std::function< void(view::ScriptedUiSession *)> &visitor)
 Borrow the active scripted UI only for the duration of visitor.
void visit_scripted_ui (const std::function< void(const view::ScriptedUiSession *)> &visitor) const
uint32_t width () const
uint32_t height () const
const ViewSize & size_hints () const
std::uint32_t editor_background_rgb () const
 The editor's background as 0xRRGGBB, resolved when open() builds the view: Processor::editor_background() when the plug-in declares one, else the root view's theme bg.primary, else view::kEditorHostClearRgb.
const std::string & last_error () const
view::View * attach_secondary_view (std::unique_ptr< view::View > view, ViewRole role)
 Attach a secondary inspector view to this bridge.
bool detach_secondary_view (view::View *view)
 Detach and destroy a previously-attached secondary view.
size_t view_count () const
 Total number of attached views (primary + secondary).
view::View * view_at (size_t index)
 Access an attached view by index.
ViewRole role_at (size_t index) const
 Role of the view at index. Returns Editor when out of range.

Static Public Attributes

static constexpr int kFirstFrameSettleRounds = 8
static constexpr double kFirstFrameSettleBudgetMs = 120.0

Friends

struct ViewBridgeTestAccess
 Test-only reach-in, mirroring StandaloneRenderTestAccess.
std::function< void()> make_editor_idle_pump (ViewBridge &bridge)
 The editor idle pump — the sole production caller of the host pull.

Constructor & Destructor Documentation

◆ ViewBridge() [1/5]

pulp::format::ViewBridge::ViewBridge ( Processor & processor,
state::StateStore & store )

◆ ViewBridge() [2/5]

pulp::format::ViewBridge::ViewBridge ( Processor & processor,
state::StateStore & store,
Options options )

◆ ViewBridge() [3/5]

pulp::format::ViewBridge::ViewBridge ( Processor & processor,
state::StateStore & store,
runtime::AliveToken::Handle owner_alive )

◆ ViewBridge() [4/5]

pulp::format::ViewBridge::ViewBridge ( Processor & processor,
state::StateStore & store,
runtime::AliveToken::Handle owner_alive,
Options options )

◆ ~ViewBridge()

pulp::format::ViewBridge::~ViewBridge ( )

◆ ViewBridge() [5/5]

pulp::format::ViewBridge::ViewBridge ( const ViewBridge & )
delete

Member Function Documentation

◆ alive_token()

const std::shared_ptr< std::atomic< bool > > & pulp::format::ViewBridge::alive_token ( ) const
inline

Cross-thread liveness token.

Set true at construction, false in the destructor. A callback that outlives or races this bridge — the GPU display-link scripted-idle pump is dispatched to the main queue and can run after the bridge is replaced (host view reload) or freed (teardown ordering / stop-doesn't-join races) — copies this shared token and no-ops when it reads false, instead of dereferencing a freed bridge.

◆ attach_secondary_view()

view::View * pulp::format::ViewBridge::attach_secondary_view ( std::unique_ptr< view::View > view,
ViewRole role )

Attach a secondary inspector view to this bridge.

The bridge takes ownership. Returns a non-owning pointer the caller can use to reference the attached view. Multiple secondary views may share the same role.

◆ close()

void pulp::format::ViewBridge::close ( )

Destroy the view.

Fires on_view_closed only if notify_attached() previously fired, so open/close dispatch stays balanced even when host attachment failed after open(). No-op if not open.

◆ detach_secondary_view()

bool pulp::format::ViewBridge::detach_secondary_view ( view::View * view)

Detach and destroy a previously-attached secondary view.

Returns true if the view was found and removed.

◆ editor_background_rgb()

std::uint32_t pulp::format::ViewBridge::editor_background_rgb ( ) const
inline

The editor's background as 0xRRGGBB, resolved when open() builds the view: Processor::editor_background() when the plug-in declares one, else the root view's theme bg.primary, else view::kEditorHostClearRgb.

Format adapters pass it to the editor host (PluginViewHost::Options::background_rgb) so the frames a host shows before the document mounts are the plug-in's own colour.

◆ height()

uint32_t pulp::format::ViewBridge::height ( ) const
inline

◆ host_actions()

view::HostActionSurface * pulp::format::ViewBridge::host_actions ( ) const
inline

◆ host_params()

view::HostParamSurface * pulp::format::ViewBridge::host_params ( )

The runtime host-parameter surface installed on the open view tree.

A view built by create_view() — in particular an imported DesignFrameView with route_changes_to_host_params(true) — resolves its param_keys against View::host_params(). open() installs this StateStore-backed surface on the tree, so those views drive the processor's parameters with no per-plugin wiring: keys resolve to the store parameter whose ParamInfo::name matches (the convention the design-param generator already emits).

This is the ONLY writer the routed path uses. A consumer that ALSO forwards DesignFrameView::on_element_changed into the same store would deliver each gesture twice; wire one or the other, never both (see routes_changes_to_host_params()).

Null before open() / after close().

◆ is_open()

bool pulp::format::ViewBridge::is_open ( ) const
inline

◆ last_error()

const std::string & pulp::format::ViewBridge::last_error ( ) const
inline

◆ notify_attached()

void pulp::format::ViewBridge::notify_attached ( )

Fire on_view_opened(view) — called by the adapter once the view has been attached to its native parent.

Idempotent: a second call after successful attach is a no-op.

◆ notify_owner_released()

bool pulp::format::ViewBridge::notify_owner_released ( )
noexcept

Tell every view in the editor tree that the plug-in instance owning it is gone, by calling EditorOwnerObserver::on_owner_released() on each view that implements it (editor_owner.hpp), root first.

Delivered at most once per open editor, and only once the owner has retired: a no-op while it is alive, after the first delivery, and with no editor open. The editor idle pump calls this on its first tick after the owner retires, and notify_processor_destroyed() and close() call it as well, so a tree that outlives its instance hears it on the main thread before anything tears it down. Main thread only. Returns whether this call delivered the notification.

◆ notify_processor_destroyed()

void pulp::format::ViewBridge::notify_processor_destroyed ( )
inlinenoexcept

The owning adapter MUST call this before the referenced Processor is destroyed when the two have independent lifetimes (AU: the audio unit and the view controller).

After this, the bridge never dereferences processor_ again – the idle pump and teardown become processor-free no-ops – so a display-link tick or ~ViewBridge that races the Processor's death cannot use-after-free it. Idempotent; safe on the main thread only (same as the pump and teardown).

◆ open()

bool pulp::format::ViewBridge::open ( std::string * error = nullptr)

Build the view.

Returns false if view construction failed; inspect last_error() for details. Calling open() on an already-open bridge is a no-op that returns true.

on_view_opened is not fired here. The adapter must call notify_attached() once the view has been attached to its host parent window. This split avoids firing on_view_opened before the host attach step succeeds.

◆ operator=()

ViewBridge & pulp::format::ViewBridge::operator= ( const ViewBridge & )
delete

◆ owner_is_alive()

bool pulp::format::ViewBridge::owner_is_alive ( ) const
inlinenoexcept

Whether the adapter instance that owns both Processor and StateStore is still alive.

Unlike alive_token(), this fails closed as soon as the adapter begins teardown, even if the host keeps the editor bridge alive.

◆ poll_editor_reload()

bool pulp::format::ViewBridge::poll_editor_reload ( )

Live editor reload (live-swap 1.9).

When the processor supports editor reload (Processor::supports_editor_reload()), the editor idle tick calls this each frame; it compares Processor::editor_reload_generation() to the last seen value and, on a change, rebuilds the OPEN editor in place by re-calling create_view() on the (now hot-swapped) processor and transplanting its content into the SAME root View the host references — so a DAW sees the editor update live without re-instantiating the plugin. Returns true if a rebuild happened (the caller should then relayout + repaint the host). No-op (returns false) for processors that don't support editor reload, when not open, or when the generation is unchanged.

◆ prepare_first_frame()

bool pulp::format::ViewBridge::prepare_first_frame ( view::PluginViewHost & host)

Content-first open: make host's first frame the settled document.

Every adapter calls this once its host exists, the GPU surface is bound and the view is attached (or, for AU v2, right before the factory returns the view). With Options::content_first_open it

  1. evaluates a document open() deferred (ScopedDeferredDocumentLoad) now, after the host and its GPU surface exist, so the document sees the same environment it would on its deferred idle tick;
  2. pumps the editor's idle work while the document asks for settle rounds (ScriptedUiSession::settling()), at most kFirstFrameSettleRounds rounds and kFirstFrameSettleBudgetMs;
  3. asks the host to present that frame now (PluginViewHost::present_first_frame()), so the view's layer holds the UI before the host composites it.

A document that fails to mount reports through its loaded callback as it would from the idle tick, and the host presents whatever the root holds (a fallback editor, or the editor's background). For an editor with no pending document only step 3 runs. Without the option it does nothing: the display link paints the first frame once the host shows the view, as before. Returns true when the host presented a frame. Main thread only.

◆ rebuild_primary_view()

bool pulp::format::ViewBridge::rebuild_primary_view ( )

Force a rebuild of the open primary view from create_view() now, transplanting the fresh content (children + background) into the stable root View.

Returns false if not open or create_view() yields nothing. Normally driven by poll_editor_reload(); exposed for tests. NOTE: the root View OBJECT is preserved (the host holds it by reference); a logic whose create_view() returns a CUSTOM View subclass with root-level paint/behavior gets its children + background refreshed but not the root subclass identity — sufficient for container-root editors (the common case).

◆ release_view()

std::unique_ptr< view::View > pulp::format::ViewBridge::release_view ( )

Transfer ownership of the primary view to the caller while preserving the bridge's lifecycle dispatch.

After release_view():

  • Ownership of the view::View passes to the caller (typically because a container widget like TabPanel::add_tab needs a unique_ptr).
  • view() still returns the raw pointer so resize(), notify_attached(), and close() continue to dispatch Processor::on_view_* callbacks on the released view.
  • The caller must keep the view alive at least until ViewBridge::close() has been called (or the bridge destroys), otherwise lifecycle dispatch will touch freed memory.
  • Returns nullptr if the bridge is not open or the view was already released.

◆ resize()

void pulp::format::ViewBridge::resize ( uint32_t width,
uint32_t height )

Notify the bridge that the host resized the editor.

Dispatches on_view_resized and stores the new size.

◆ role()

ViewRole pulp::format::ViewBridge::role ( ) const
inline

◆ role_at()

ViewRole pulp::format::ViewBridge::role_at ( size_t index) const

Role of the view at index. Returns Editor when out of range.

◆ scripted_ui() [1/2]

view::ScriptedUiSession * pulp::format::ViewBridge::scripted_ui ( )

Access the scripted UI session, if the primary view was built from a JS script.

This includes the framework-owned script the plug-in declares (UI_SCRIPT, see editor_assets.hpp) and custom processors that expose a session through Processor::active_scripted_ui().

◆ scripted_ui() [2/2]

const view::ScriptedUiSession * pulp::format::ViewBridge::scripted_ui ( ) const

◆ set_host_actions()

void pulp::format::ViewBridge::set_host_actions ( view::HostActionSurface * actions)

Install the command channel a routed view's Kind::action clicks reach (route_actions_to_host(true) → View::host_actions()).

Pulp has no native action backing — the destination is host-defined — so this stays caller-supplied and is null by default. The surface must outlive the open view. Applied immediately when the bridge is already open.

◆ set_preferred_size()

bool pulp::format::ViewBridge::set_preferred_size ( uint32_t width,
uint32_t height )

Update the editor's PREFERRED size (and derived aspect ratio) after the plugin requested an editor-initiated resize to a new natural size (see Processor::request_editor_resize).

Recomputes size_hints_'s preferred_width/height and aspect_ratio from (w, h), preserving the declared min/max resize envelope. Returns false (without mutation) when either dimension is zero or falls outside a non-zero bound. Per-format resize-hint reporting (CLAP gui_get_resize_hints / gui_get_size, VST3 checkSizeConstraint) then tracks the new aspect instead of snapping the window back to the old one. Zero min/max values retain their special meanings (fixed/unbounded). No-op when either dimension is zero. Does NOT fire on_view_resized (that is the host→plugin direction, dispatched by resize()).

◆ size_hints()

const ViewSize & pulp::format::ViewBridge::size_hints ( ) const
inline

◆ uses_script_ui()

bool pulp::format::ViewBridge::uses_script_ui ( ) const
inline

◆ view() [1/2]

view::View * pulp::format::ViewBridge::view ( )
inline

◆ view() [2/2]

const view::View * pulp::format::ViewBridge::view ( ) const
inline

◆ view_at()

view::View * pulp::format::ViewBridge::view_at ( size_t index)

Access an attached view by index.

Index 0 is the primary view (if open); indices >=1 are secondary views in attach order. Returns nullptr if index is out of range.

◆ view_count()

size_t pulp::format::ViewBridge::view_count ( ) const

Total number of attached views (primary + secondary).

Zero when not open and no secondaries are attached.

◆ visit_scripted_ui() [1/2]

void pulp::format::ViewBridge::visit_scripted_ui ( const std::function< void(const view::ScriptedUiSession *)> & visitor) const

◆ visit_scripted_ui() [2/2]

void pulp::format::ViewBridge::visit_scripted_ui ( const std::function< void(view::ScriptedUiSession *)> & visitor)

Borrow the active scripted UI only for the duration of visitor.

Processor-owned sessions are visited under the processor's lifetime discipline; framework-owned sessions are already owned by this bridge.

◆ width()

uint32_t pulp::format::ViewBridge::width ( ) const
inline

◆ make_editor_idle_pump

std::function< void()> make_editor_idle_pump ( ViewBridge & bridge)
friend

The editor idle pump — the sole production caller of the host pull.

GPU hosts invoke it once per display-link tick to drain host automation, consume restore/reload edges, and poll scripted UI work while the editor is embedded. Captures the bridge by pointer; the host MUST drop this callback (via detach() / destruction) before the bridge is destroyed — every adapter resets its host before bridge.close().

◆ ViewBridgeTestAccess

friend struct ViewBridgeTestAccess
friend

Test-only reach-in, mirroring StandaloneRenderTestAccess.

Member Data Documentation

◆ kFirstFrameSettleBudgetMs

double pulp::format::ViewBridge::kFirstFrameSettleBudgetMs = 120.0
staticconstexpr

◆ kFirstFrameSettleRounds

int pulp::format::ViewBridge::kFirstFrameSettleRounds = 8
staticconstexpr

The documentation for this class was generated from the following file:
  • /home/runner/work/pulp/pulp/core/format/include/pulp/format/view_bridge.hpp