Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::view::WindowHost Class Referenceabstract

#include <window_host.hpp>

Classes

struct  ContentSize
struct  MonitorInfo
 Monitor information. More...

Public Types

enum class  HitTestRegion {
  none , client , caption , resize_n ,
  resize_s , resize_e , resize_w , resize_ne ,
  resize_nw , resize_se , resize_sw , close ,
  minimize , maximize
}
 Hit-test region types for client-decorated windows. More...
using ResizeCallback = std::function<void(uint32_t width, uint32_t height)>
using Factory

Public Member Functions

virtual ~WindowHost ()
 Out-of-line, and NOT trivial: it clears the back-pointer of every NativeViewHost still attached to this host.
virtual void show ()=0
virtual void hide ()=0
virtual bool is_visible () const =0
virtual void repaint ()=0
void mark_dirty ()
void mark_dirty (const Rect &root_rect)
void request_frame ()
bool pending_repaint_is_full () const
bool has_pending_dirty_bounds () const
Rect pending_dirty_bounds () const
void clear_pending_dirty ()
PendingDamage::Snapshot take_pending_dirty ()
void restore_pending_dirty (const PendingDamage::Snapshot &snapshot)
void paint_root (canvas::Canvas &canvas, View &root, bool surface_preserves_outside_clip=false)
void attach_render_loop (render::RenderLoop *loop)
render::RenderLoop * render_loop () const
virtual void position_beside (WindowHost *other)
 Position this window alongside another window.
virtual void * native_window_handle () const
virtual void * native_content_view_handle () const
virtual void * dawn_device_handle () const
virtual void * dawn_queue_handle () const
virtual void * dawn_instance_handle () const
virtual render::GpuSurface * gpu_surface () const
virtual double gpu_render_time_ms () const
 Most recent whole-recording GPU render time in milliseconds for the last presented frame, or 0 when GPU timing is unavailable (CPU host, adapter without timestamp-query, or no sample landed yet).
virtual bool gpu_render_timing_available () const
 Whether whole-recording GPU render timing is available this run — the host owns a SkiaSurface whose SkiaSurface::gpu_render_timing_available() is true.
virtual ContentSize get_content_size () const
virtual bool attach_native_child_view (void *child_view, float x, float y, float width, float height)
virtual bool set_native_child_view_bounds (void *child_view, float x, float y, float width, float height)
virtual void detach_native_child_view (void *child_view)
virtual bool set_native_child_view_clip (void *child_view, bool has_clip, float x, float y, float width, float height)
virtual std::vector< uint8_t > capture_png ()
virtual std::vector< uint8_t > capture_back_buffer_png ()
virtual void invalidate_input_state ()
virtual void request_close ()
virtual void set_idle_callback (std::function< void()> cb)
virtual void set_resize_callback (ResizeCallback cb)
virtual void set_app_key_monitor (std::function< bool(const KeyEvent &)> handler)
virtual void set_open_files_handler (std::function< void(const std::vector< std::string > &)> handler)
virtual void set_close_callback (std::function< void()> cb)=0
virtual void run_event_loop ()=0
virtual void set_client_decoration (bool enabled)
 Remove native title bar and let the app draw its own.
virtual void set_fixed_aspect_ratio (float ratio)
 Constrain window resize to maintain aspect ratio.
virtual void set_always_on_top (bool on_top)
 Keep window above all others.
virtual void request_content_size (float w, float h)
 Request the native window resize so its CONTENT area becomes (w × h), keeping the window's TOP-LEFT corner fixed (so a pinned toolbar / toggle row doesn't jump).
virtual void set_design_viewport (float design_w, float design_h)
 Set a fixed "design viewport".
virtual bool design_viewport_transform (float &sx, float &sy, float &tx, float &ty) const
virtual float dpi_scale () const
 Get the DPI scale factor for this window.
virtual Size max_dimensions () const
 Get the maximum available dimensions for this window's screen.
float convert_to_logical (float px) const
 Convert physical pixels to logical coordinates.
float convert_to_native (float logical) const
 Convert logical coordinates to physical pixels.
virtual std::vector< MonitorInfo > get_monitors () const
 Get list of connected monitors. Default returns one virtual monitor.
virtual void set_mouse_relative_mode (bool enabled)
 Hide cursor and report relative deltas (infinite drag for knobs).
virtual bool is_gpu_backed () const
virtual bool supports_deferred_close () const
 True only when request_close_deferred() schedules close handling for a later native event-loop turn.
virtual void request_close_deferred ()
 Schedule request_close() for a later native event-loop turn.
virtual bool supports_compositor_capture () const
 True when capture_png() returns pixels from the visible compositor rather than a deterministic host-managed back buffer.
virtual bool event_loop_blocks_until_close () const
 Browser hosts override this query because their page-owned loop returns immediately.
virtual bool event_loop_supports_exit_drain () const
 True only when run_event_loop_until() continues dispatching accepted main-thread work after the native loop receives its stop signal.
virtual void run_event_loop_until (std::function< bool()> ready_to_return)
 Run the event loop, then keep dispatching main-thread work until ready_to_return reports that stack-borrowed state may be destroyed.
virtual bool supports_back_buffer_capture () const
 True when capture_back_buffer_png() is implemented by this host.
virtual bool supports_gpu_submission_evidence () const
 True when this host can report whether the last rendered frame's GPU work was submitted toward its intended output.
virtual bool last_frame_gpu_submission_observed () const
 True when the most recent frame rendered by this host was submitted toward its intended output (a presentable drawable, or the offscreen target when that IS the output).
virtual std::vector< double > frame_time_samples_ms () const
 Main-thread milliseconds spent on each host frame dispatched so far (idle pump + layout + paint + submit + present), oldest first and bounded, so a finite run (a headless screenshot) can report how its frames performed.
virtual void log_gpu_diagnostics_summary ()
 Log this host's GPU-diagnostics summary line(s) — whether the Skia log bridge is wired and what reached it — at info level.
bool feature_marked_unsupported (const char *method) const
 True once note_unsupported_feature(method) has fired for method on this host — i.e.

Static Public Member Functions

static std::unique_ptr< WindowHost > create (View &root, const WindowOptions &options)
static void set_factory (Factory factory)
static void clear_factory ()
static bool has_factory ()
static bool compute_design_viewport_transform (float window_w, float window_h, float design_w, float design_h, float &sx, float &sy, float &tx, float &ty, bool top_align=false)
 Pure math behind set_design_viewport — used both by the platform host's paint_scene and by tests.
static Point design_viewport_window_to_root (Point pt, float window_w, float window_h, float design_w, float design_h, bool top_align=false)
 Inverse of compute_design_viewport_transform: map a HOST/window-space point back into ROOT (design) space.

Protected Member Functions

void note_unsupported_feature (const char *method)
 Log ONCE (per method, per host instance) that a window feature is a base-class no-op on this host, so a silently-degraded CPU / headless host is self-diagnosing instead of a multi-hour debug (see the CLAUDE.md GPU-host gotcha).

Friends

class NativeViewHost

Member Typedef Documentation

◆ Factory

Initial value:
std::function<std::unique_ptr<WindowHost>(
View& root, const WindowOptions& options)>
Definition view.hpp:73
Definition window_host.hpp:29

◆ ResizeCallback

using pulp::view::WindowHost::ResizeCallback = std::function<void(uint32_t width, uint32_t height)>

Member Enumeration Documentation

◆ HitTestRegion

Hit-test region types for client-decorated windows.

Enumerator
none 
client 
caption 
resize_n 
resize_s 
resize_e 
resize_w 
resize_ne 
resize_nw 
resize_se 
resize_sw 
close 
minimize 
maximize 

Constructor & Destructor Documentation

◆ ~WindowHost()

virtual pulp::view::WindowHost::~WindowHost ( )
virtual

Out-of-line, and NOT trivial: it clears the back-pointer of every NativeViewHost still attached to this host.

See the registry at the bottom of this class.

Member Function Documentation

◆ attach_native_child_view()

virtual bool pulp::view::WindowHost::attach_native_child_view ( void * child_view,
float x,
float y,
float width,
float height )
inlinevirtual

◆ attach_render_loop()

void pulp::view::WindowHost::attach_render_loop ( render::RenderLoop * loop)
inline

◆ capture_back_buffer_png()

virtual std::vector< uint8_t > pulp::view::WindowHost::capture_back_buffer_png ( )
inlinevirtual

◆ capture_png()

virtual std::vector< uint8_t > pulp::view::WindowHost::capture_png ( )
inlinevirtual

◆ clear_factory()

void pulp::view::WindowHost::clear_factory ( )
static

◆ clear_pending_dirty()

void pulp::view::WindowHost::clear_pending_dirty ( )
inline

◆ compute_design_viewport_transform()

bool pulp::view::WindowHost::compute_design_viewport_transform ( float window_w,
float window_h,
float design_w,
float design_h,
float & sx,
float & sy,
float & tx,
float & ty,
bool top_align = false )
inlinestatic

Pure math behind set_design_viewport — used both by the platform host's paint_scene and by tests.

Returns false (and leaves outputs untouched) when the inputs don't define a valid transform.

top_align: when true, anchor the scaled design to the TOP of the window (vertical slack falls below it) instead of centering it vertically. Horizontal centering is unchanged. Used by the AU v3 editor so a fixed-aspect design embedded in a taller host pane (e.g. REAPER's FX-chain pane — AU can't negotiate the pane aspect the way CLAP's gui_adjust_size / VST3's checkSizeConstraint do) reads like CLAP/VST3 (content at the top) instead of floating centered between two bands. Default false = centered (every other caller). Must be passed identically at the paint AND input-mapping call sites, or clicks misalign.

◆ convert_to_logical()

float pulp::view::WindowHost::convert_to_logical ( float px) const
inline

Convert physical pixels to logical coordinates.

◆ convert_to_native()

float pulp::view::WindowHost::convert_to_native ( float logical) const
inline

Convert logical coordinates to physical pixels.

◆ create()

std::unique_ptr< WindowHost > pulp::view::WindowHost::create ( View & root,
const WindowOptions & options )
static

◆ dawn_device_handle()

virtual void * pulp::view::WindowHost::dawn_device_handle ( ) const
inlinevirtual

◆ dawn_instance_handle()

virtual void * pulp::view::WindowHost::dawn_instance_handle ( ) const
inlinevirtual

◆ dawn_queue_handle()

virtual void * pulp::view::WindowHost::dawn_queue_handle ( ) const
inlinevirtual

◆ design_viewport_transform()

virtual bool pulp::view::WindowHost::design_viewport_transform ( float & sx,
float & sy,
float & tx,
float & ty ) const
inlinevirtual

◆ design_viewport_window_to_root()

Point pulp::view::WindowHost::design_viewport_window_to_root ( Point pt,
float window_w,
float window_h,
float design_w,
float design_h,
bool top_align = false )
inlinestatic

Inverse of compute_design_viewport_transform: map a HOST/window-space point back into ROOT (design) space.

Every plug-in editor host needs this to hit-test a click against the widget the user visually pointed at, and every one of them had its own byte-identical copy — the macOS GPU and CPU plug-in hosts, the iOS host, and the Windows host (WAH-10). Four copies of an inverse letterbox transform is four chances for one of them to drift from the paint-side transform, and the symptom of that drift is clicks landing on the wrong control, which is not obviously a coordinate bug when you hit it.

Returns pt unchanged when no design viewport is active, which is the identity every caller wants for the un-pinned case.

top_align MUST match the value used at paint, or input misaligns vertically — the same coupling documented on the forward transform.

◆ detach_native_child_view()

virtual void pulp::view::WindowHost::detach_native_child_view ( void * child_view)
inlinevirtual

◆ dpi_scale()

virtual float pulp::view::WindowHost::dpi_scale ( ) const
inlinevirtual

Get the DPI scale factor for this window.

◆ event_loop_blocks_until_close()

virtual bool pulp::view::WindowHost::event_loop_blocks_until_close ( ) const
inlinevirtual

Browser hosts override this query because their page-owned loop returns immediately.

Stack-owned runtime state may use it to fail closed.

◆ event_loop_supports_exit_drain()

virtual bool pulp::view::WindowHost::event_loop_supports_exit_drain ( ) const
inlinevirtual

True only when run_event_loop_until() continues dispatching accepted main-thread work after the native loop receives its stop signal.

◆ feature_marked_unsupported()

bool pulp::view::WindowHost::feature_marked_unsupported ( const char * method) const
inline

True once note_unsupported_feature(method) has fired for method on this host — i.e.

a window feature was requested that this host silently no-ops (the base-class default ran because the host did not override it). Diagnostic surface for the "CPU host silently degrades" gotcha; also the test seam for the one-time logging.

◆ frame_time_samples_ms()

virtual std::vector< double > pulp::view::WindowHost::frame_time_samples_ms ( ) const
inlinevirtual

Main-thread milliseconds spent on each host frame dispatched so far (idle pump + layout + paint + submit + present), oldest first and bounded, so a finite run (a headless screenshot) can report how its frames performed.

Hosts that do not measure return an empty vector. Appended at the public vtable tail for downstream WindowHost ABI safety.

◆ get_content_size()

virtual ContentSize pulp::view::WindowHost::get_content_size ( ) const
virtual

◆ get_monitors()

virtual std::vector< MonitorInfo > pulp::view::WindowHost::get_monitors ( ) const
inlinevirtual

Get list of connected monitors. Default returns one virtual monitor.

◆ gpu_render_time_ms()

virtual double pulp::view::WindowHost::gpu_render_time_ms ( ) const
inlinevirtual

Most recent whole-recording GPU render time in milliseconds for the last presented frame, or 0 when GPU timing is unavailable (CPU host, adapter without timestamp-query, or no sample landed yet).

This is render-recording time (Skia Graphite's whole-recording GpuStats elapsed time as exposed by SkiaSurface::gpu_render_time_ms()), NOT total frame time and NOT a per-pass number. GPU-backed platform hosts forward this from their SkiaSurface; the base and CPU/non-GPU hosts return 0.

◆ gpu_render_timing_available()

virtual bool pulp::view::WindowHost::gpu_render_timing_available ( ) const
inlinevirtual

Whether whole-recording GPU render timing is available this run — the host owns a SkiaSurface whose SkiaSurface::gpu_render_timing_available() is true.

When false the inspector should show the honest "GPU timing unavailable" rather than 0.

◆ gpu_surface()

virtual render::GpuSurface * pulp::view::WindowHost::gpu_surface ( ) const
inlinevirtual

◆ has_factory()

bool pulp::view::WindowHost::has_factory ( )
static

◆ has_pending_dirty_bounds()

bool pulp::view::WindowHost::has_pending_dirty_bounds ( ) const
inline

◆ hide()

virtual void pulp::view::WindowHost::hide ( )
pure virtual

◆ invalidate_input_state()

virtual void pulp::view::WindowHost::invalidate_input_state ( )
inlinevirtual

◆ is_gpu_backed()

virtual bool pulp::view::WindowHost::is_gpu_backed ( ) const
inlinevirtual

◆ is_visible()

virtual bool pulp::view::WindowHost::is_visible ( ) const
pure virtual

◆ last_frame_gpu_submission_observed()

virtual bool pulp::view::WindowHost::last_frame_gpu_submission_observed ( ) const
inlinevirtual

True when the most recent frame rendered by this host was submitted toward its intended output (a presentable drawable, or the offscreen target when that IS the output).

Hosts that cannot observe submission keep the false default, so an absent producer is never read as evidence.

Submitted is not displayed, and the name is the honest bound: it means the recording was accepted and the queue submit was issued, NOT that the GPU executed the command buffer or that a viewer saw the pixels. A backend can still reject the buffer at its queue after a successful insert. Display evidence needs a presented-drawable producer, which no Pulp backend has.

◆ log_gpu_diagnostics_summary()

virtual void pulp::view::WindowHost::log_gpu_diagnostics_summary ( )
inlinevirtual

Log this host's GPU-diagnostics summary line(s) — whether the Skia log bridge is wired and what reached it — at info level.

For the end of a bounded run, never per frame. No-op for hosts without a GPU stack. Appended at the public vtable tail for downstream WindowHost ABI safety.

◆ mark_dirty() [1/2]

void pulp::view::WindowHost::mark_dirty ( )

◆ mark_dirty() [2/2]

void pulp::view::WindowHost::mark_dirty ( const Rect & root_rect)

◆ max_dimensions()

virtual Size pulp::view::WindowHost::max_dimensions ( ) const
inlinevirtual

Get the maximum available dimensions for this window's screen.

◆ native_content_view_handle()

virtual void * pulp::view::WindowHost::native_content_view_handle ( ) const
inlinevirtual

◆ native_window_handle()

virtual void * pulp::view::WindowHost::native_window_handle ( ) const
inlinevirtual

◆ note_unsupported_feature()

void pulp::view::WindowHost::note_unsupported_feature ( const char * method)
protected

Log ONCE (per method, per host instance) that a window feature is a base-class no-op on this host, so a silently-degraded CPU / headless host is self-diagnosing instead of a multi-hour debug (see the CLAUDE.md GPU-host gotcha).

A host that really implements the feature overrides the method and never calls this.

◆ paint_root()

void pulp::view::WindowHost::paint_root ( canvas::Canvas & canvas,
View & root,
bool surface_preserves_outside_clip = false )

◆ pending_dirty_bounds()

Rect pulp::view::WindowHost::pending_dirty_bounds ( ) const
inline

◆ pending_repaint_is_full()

bool pulp::view::WindowHost::pending_repaint_is_full ( ) const
inline

◆ position_beside()

virtual void pulp::view::WindowHost::position_beside ( WindowHost * other)
inlinevirtual

Position this window alongside another window.

Places to the right if there's screen space, otherwise to the left.

◆ render_loop()

render::RenderLoop * pulp::view::WindowHost::render_loop ( ) const
inline

◆ repaint()

virtual void pulp::view::WindowHost::repaint ( )
pure virtual

◆ request_close()

virtual void pulp::view::WindowHost::request_close ( )
inlinevirtual

◆ request_close_deferred()

virtual void pulp::view::WindowHost::request_close_deferred ( )
inlinevirtual

Schedule request_close() for a later native event-loop turn.

The base implementation deliberately does not close synchronously.

◆ request_content_size()

virtual void pulp::view::WindowHost::request_content_size ( float w,
float h )
inlinevirtual

Request the native window resize so its CONTENT area becomes (w × h), keeping the window's TOP-LEFT corner fixed (so a pinned toolbar / toggle row doesn't jump).

For content whose intrinsic size changes at runtime — e.g. the Musical Typing Keyboard's piano⇄typing toggle (piano is shorter): the window shrinks/grows in height, top-aligned, while the top-left controls stay put. No-op on hosts that don't override (the content just letterboxes within the current window). Callers that also use a design viewport should update set_design_viewport()/set_fixed_aspect_ratio() to the same (w, h) so paint scale and the OS aspect lock track the resize.

◆ request_frame()

void pulp::view::WindowHost::request_frame ( )
inline

◆ restore_pending_dirty()

void pulp::view::WindowHost::restore_pending_dirty ( const PendingDamage::Snapshot & snapshot)
inline

◆ run_event_loop()

virtual void pulp::view::WindowHost::run_event_loop ( )
pure virtual

◆ run_event_loop_until()

virtual void pulp::view::WindowHost::run_event_loop_until ( std::function< bool()> ready_to_return)
inlinevirtual

Run the event loop, then keep dispatching main-thread work until ready_to_return reports that stack-borrowed state may be destroyed.

Hosts advertising exit-drain support must override this fallback.

◆ set_always_on_top()

virtual void pulp::view::WindowHost::set_always_on_top ( bool on_top)
inlinevirtual

Keep window above all others.

◆ set_app_key_monitor()

virtual void pulp::view::WindowHost::set_app_key_monitor ( std::function< bool(const KeyEvent &)> handler)
inlinevirtual

◆ set_client_decoration()

virtual void pulp::view::WindowHost::set_client_decoration ( bool enabled)
inlinevirtual

Remove native title bar and let the app draw its own.

◆ set_close_callback()

virtual void pulp::view::WindowHost::set_close_callback ( std::function< void()> cb)
pure virtual

◆ set_design_viewport()

virtual void pulp::view::WindowHost::set_design_viewport ( float design_w,
float design_h )
inlinevirtual

Set a fixed "design viewport".

When set, the root view's bounds are pinned to (design_w x design_h) and paint applies an aspect-correct scale + letterbox translate to fit the current window size. Input coordinates receive the inverse transform before hit-testing. Pass (0, 0) to disable.

Use this for content authored at a known fixed size (e.g. an editor imported from Figma / Stitch / v0 / Pencil) that must resize proportionally without re-layout. Combine with set_fixed_aspect_ratio(design_w / design_h) so the OS enforces the aspect on user drag and letterbox bars only appear as a backstop during in-flight drag.

Why this exists (pulp #59 / #63 / #64 / #65 — 2026-05-14): the "obvious" alternatives all failed for native-react renderers like Spectr:

  • Per-child set_scale() on root children scales chrome but canvas widgets record their draw commands at the original size, so contents clip on shrink.
  • Yoga absolute+inset:0 propagation: chains of position:absolute
    • inset:0 collapse to 0×0 in Pulp's runtime-import because Yoga only fills a containing block when the parent has a definite POINTS size — the cascade root→body→wrap→canvas never satisfies that.
  • JS-driven canvas refit via React refs: even with refs wired correctly through getPublicInstance, Spectr's resize() bails on wrapRef.current/canvasRef.current existence checks and never runs. The design-viewport approach sidesteps all of these by doing the resize at the renderer (paint-time scale of the design surface), which is what a browser webview effectively does at the layer level. The root view always thinks it's at design size; canvases record commands at design size; chrome lays out at design size. The WindowHost does the fitting on paint, and inverse-maps mouse coords so hit-test still works.

◆ set_factory()

void pulp::view::WindowHost::set_factory ( Factory factory)
static

◆ set_fixed_aspect_ratio()

virtual void pulp::view::WindowHost::set_fixed_aspect_ratio ( float ratio)
inlinevirtual

Constrain window resize to maintain aspect ratio.

◆ set_idle_callback()

virtual void pulp::view::WindowHost::set_idle_callback ( std::function< void()> cb)
inlinevirtual

◆ set_mouse_relative_mode()

virtual void pulp::view::WindowHost::set_mouse_relative_mode ( bool enabled)
inlinevirtual

Hide cursor and report relative deltas (infinite drag for knobs).

◆ set_native_child_view_bounds()

virtual bool pulp::view::WindowHost::set_native_child_view_bounds ( void * child_view,
float x,
float y,
float width,
float height )
inlinevirtual

◆ set_native_child_view_clip()

virtual bool pulp::view::WindowHost::set_native_child_view_clip ( void * child_view,
bool has_clip,
float x,
float y,
float width,
float height )
inlinevirtual

◆ set_open_files_handler()

virtual void pulp::view::WindowHost::set_open_files_handler ( std::function< void(const std::vector< std::string > &)> handler)
inlinevirtual

◆ set_resize_callback()

virtual void pulp::view::WindowHost::set_resize_callback ( ResizeCallback cb)
virtual

◆ show()

virtual void pulp::view::WindowHost::show ( )
pure virtual

◆ supports_back_buffer_capture()

virtual bool pulp::view::WindowHost::supports_back_buffer_capture ( ) const
inlinevirtual

True when capture_back_buffer_png() is implemented by this host.

Kept independent from compositor capture: an SSH-safe back-buffer path must not be inferred from the ability to capture a visible window. Appended at the public vtable tail for downstream WindowHost ABI safety.

◆ supports_compositor_capture()

virtual bool pulp::view::WindowHost::supports_compositor_capture ( ) const
inlinevirtual

True when capture_png() returns pixels from the visible compositor rather than a deterministic host-managed back buffer.

◆ supports_deferred_close()

virtual bool pulp::view::WindowHost::supports_deferred_close ( ) const
inlinevirtual

True only when request_close_deferred() schedules close handling for a later native event-loop turn.

External standalone hosts must override both methods when inspector startup can occur from an idle callback.

◆ supports_gpu_submission_evidence()

virtual bool pulp::view::WindowHost::supports_gpu_submission_evidence ( ) const
inlinevirtual

True when this host can report whether the last rendered frame's GPU work was submitted toward its intended output.

Appended at the public vtable tail for downstream WindowHost ABI safety.

◆ take_pending_dirty()

PendingDamage::Snapshot pulp::view::WindowHost::take_pending_dirty ( )
inline

◆ NativeViewHost

friend class NativeViewHost
friend

The documentation for this class was generated from the following file: