Skip to content

x.multiwindow #

x.multiwindow

x.multiwindow is the low-level multi-window layer used by the gg multi-window facade. It owns native window lifetimes, backend selection, per-window events, owner-thread dispatch, and optional render scheduling.

Most application code should use gg with -d gg_multiwindow. This module is intended for the gg facade, backend work, and callers that need direct control over native windows and rendering setup.

Scope

The module provides:

  • an App registry for multiple native windows;
  • generation-checked WindowId handles;
  • backend capability reporting and backend selection;
  • lifecycle events routed to a specific window;
  • backend-neutral input events routed to a specific window;
  • an owner-thread job queue for cross-thread work;
  • an opt-in render scheduler and opaque transaction declarations.

It does not provide layout, widgets, text rendering, high-level input semantics, or a default event loop. The gg facade supplies the higher-level loop, gg.Event mapping, and drawing API.

Creating an App

import x.multiwindow

mut app := multiwindow.new_app(backend: .mock, queue_size: 128)!
defer {
    app.stop() or {}
}

win := app.create_window(title: 'Tool', width: 320, height: 200)!
info := app.window_info(win)!
println('${info.title}: ${info.width}x${info.height}')

multiwindow.new_app() uses the values in Config. The low-level default backend is .mock; .auto must be requested explicitly. The gg facade has its own configuration and defaults to .auto.

Config.require_renderer: true asks the selected backend to initialize its renderer during new_app(). The render API requires that creation-time request and Capabilities.explicit_swapchain; x.multiwindow does not lazily initialize a renderer later.

Backend Selection

BackendKind values are:

  • .mock: deterministic in-process backend for tests and event-only code;
  • .x11: Linux X11 backend, compiled only with -d x_multiwindow_x11;
  • .wayland: Linux Wayland backend, compiled only with -d sokol_wayland;
  • .appkit: macOS AppKit backend;
  • .win32: Windows backend;
  • .auto: resolve to a concrete backend.

The .auto policy is platform and environment dependent:

  • Windows selects .win32.
  • macOS selects .appkit.
  • Linux with require_renderer: true prefers X11 only when compiled with -d x_multiwindow_x11 and DISPLAY is set, then Wayland when compiled with -d sokol_wayland and WAYLAND_DISPLAY is set.
  • Linux without require_renderer prefers Wayland when compiled with -d sokol_wayland and WAYLAND_DISPLAY is set, then X11 only when compiled with -d x_multiwindow_x11 and DISPLAY is set.
  • If no native backend is selected, .auto falls back to .mock.

Plain capability probes do not necessarily open a display or create a device. Renderer capability probes and new_app(require_renderer: true) may fail if the display server, graphics device, or platform API is unavailable.

Backend Capabilities

Capabilities describes the selected backend contract:

  • multi_window: backend can manage more than one window;
  • owner_queue: the owner-thread queue is available;
  • explicit_swapchain: the backend can participate in managed explicit-target rendering; it does not expose a public gfx.Swapchain;
  • mock, native, x11, wayland, win32: selected platform flags;
  • gl, metal, d3d11: active renderer API flags;
  • input_events, mouse_events, keyboard_events, text_events, focus_events, drop_events, touch_events: native input classes the backend can actually deliver;
  • cursor_shapes: native hover cursor shape updates are supported via set_window_cursor(id, shape). This is independent from native interactive move/resize support;
  • interactive_move_resize: native user-driven move/resize can be requested when the running backend has the required handles and current user action;
  • native_decorations: native/server-side decorations are effective for the running backend;
  • readback: whether the backend exposes readback support; it is false on the current native backends.

Plain capability probes do not necessarily connect to the display server, so runtime optional globals can be unknown before startup and most of those probes report implementation support. For Wayland, use app.capabilities() after new_app() for the authoritative runtime state: drop_events requires a wl_data_device, touch_events requires wl_touch, and interactive move/resize requires a seat. Wayland cursor-shape reporting is stricter: cursor_shapes is true only after a wp_cursor_shape_device_v1 has been created for the active wl_pointer. Wayland requests server-side decorations through xdg-decoration when the protocol is available; the compositor's configure(mode) decides the effective server_side or client_side mode. If server_side is refused or xdg-decoration is unavailable, apps and examples may draw a client-side fallback. Wayland cursor-shape feedback uses wp_cursor_shape_manager_v1 when the compositor exposes it and the seat has a pointer; this keeps cursor theme selection compositor-side. wl_cursor_theme client-side fallback is not implemented, so app.capabilities() reports cursor_shapes == false on Wayland compositors that do not advertise cursor-shape-v1.

Backend notes:

  • Mock supports lifecycle, events, min-size clamping, and the owner queue, but it has no renderer.
  • X11 is Linux-only and exists only in builds compiled with -d x_multiwindow_x11. It supports native lifecycle, title updates, X11 size hints, borderless/fullscreen hints, optional EGL/OpenGL rendering, and native size queries after create/resize. Programmatic resize is rejected for non-resizable windows.
  • Wayland is Linux-only and exists only in builds compiled with -d sokol_wayland. It requires wl_compositor and xdg_wm_base, rejects visible: false, and currently rejects programmatic resize. Rendering uses Wayland EGL/OpenGL when initialized.
  • AppKit is macOS-only. It must start on the main thread and uses Metal when rendering is required.
  • Win32 is Windows-only and supports native lifecycle and min-size enforcement. D3D11 rendering requires a Windows build with -d sokol_d3d11; without that flag, lifecycle works but managed renderer calls are unsupported. Renderer startup can still fail if D3D11 device or swapchain creation is unavailable, and DXGI occlusion during present is treated as a skipped frame.

Window Lifecycle

create_window() creates the native/backend window and returns a generation-checked WindowId. The stored WindowInfo uses the actual size reported by the backend after clamping or native size queries, not just the requested WindowConfig.

destroy_window() destroys one live window and emits a destroy event. Destroying the last window does not stop the app. stop() destroys all remaining live windows, marks the app stopped, stops the backend, and closes owner-queue admission.

Window handles are generation checked. A handle for a destroyed slot becomes stale if that slot is later reused.

Owner-Thread Rule

The thread that calls new_app() is the App owner thread. Mutating operations, event draining, registry enumeration, owner-queue draining, and rendering must run on that thread. Calls from another thread fail with:

multiwindow: operation requires the owner thread

Use post() or try_post() to enqueue short callbacks from other threads, then call drain_pending() on the owner thread. drain_pending() runs at most the requested number of jobs and rechecks app status between jobs; if a job stops the app, later queued jobs are not run.

The simple read helpers status(), capabilities(), window_exists(), and window_status() do not enforce the owner-thread check.

Events

Events are explicit. Native lifecycle and input events are not delivered to user code until the owner thread calls:

  • poll_events() to collect backend/native events into the App queue;
  • drain_events() to retrieve and clear queued lifecycle events;
  • drain_input_events() to retrieve and clear queued input events without consuming lifecycle events;
  • drain_queued_events() to retrieve lifecycle and input events together in the exact order accepted by App.

Lifecycle event kinds are:

  • .window_created: emitted by create_window() with the actual initial size;
  • .window_destroyed: emitted by destroy_window(), stop(), or accepted backend destroy notifications;
  • .window_close_requested: emitted when the backend reports a close request;
  • .window_resized: emitted after resize_window() or accepted backend resize notifications with the actual size.

Backend events for stale or already-destroyed window handles are filtered.

InputEvent is the low-level backend-neutral payload used by the gg facade to rebuild gg.Event for a specific window. Input kinds include key down/up, char, mouse down/up/move/scroll/enter/leave, resize, iconified/restored, focus/unfocus, clipboard paste, file drop, and touch families. Backends report which families are implemented through the capability booleans above; unsupported input classes must remain false rather than being partially emulated.

Native input events that do not already carry a frame counter are stamped by App.poll_events(); all input events accepted in the same poll cycle share that frame_count. Mock/test events can provide an explicit non-zero frame_count, which is preserved.

Current native input support is intentionally capability-scoped:

  • Mock can synthesize every input family for deterministic tests.
  • Win32 routes mouse, keyboard, text/char, focus, resize, iconified/restored, clipboard paste signals, file drops via WM_DROPFILES, and WM_TOUCH down/move/up touch input. WM_TOUCH handles are read and closed in the window procedure; WM_TOUCH does not expose a cancelled state, so touches_cancelled is not emitted by the Win32 backend unless a future Pointer Input path owns POINTER_FLAG_CANCELED.
  • AppKit routes mouse, keyboard, text/char, focus, resize, iconified/restored, clipboard paste signals, and file drops through NSDraggingDestination file URLs. It also routes AppKit NSResponder touch phases; positions come from NSTouch.normalizedPosition mapped into the current framebuffer, with touches_cancelled emitted only for touchesCancelledWithEvent:.
  • X11 routes mouse, keyboard, text/char, focus, resize, iconified/restored, and clipboard paste signal input events. Text uses Xlib XIM/XIC with Xutf8LookupString; this covers committed UTF-8 text from the active input method without exposing Xlib objects through the public API.
  • X11 receives file drops with XDND text/uri-list selection conversion. It decodes local file:// URIs and queues .files_dropped with routed dropped_files.
  • Wayland routes pointer, keyboard, text/char through xkb keymap/state, focus, clipboard paste signal, resize input events, touch when the seat exposes wl_touch, and file drops when wl_data_device/wl_data_offer text/uri-list is available. Data-offer payloads are received through a non-blocking fd and drained from the owner poll path; the backend only sends wl_data_offer.finish after a valid copy or move action has been received. Pending drops whose source never closes the transfer fd are rejected and cleaned up after a bounded number of owner poll cycles. Wayland text follows the existing sapp xkb_state_key_get_utf8 model for key presses; full IME/composed text is not implemented. Wayland synthesizes key-repeat from compositor repeat_info/xkb; pointer frame batching is not synthesized yet; event callbacks are routed as the compositor delivers them.
  • Native drop and touch input are false unless a backend explicitly reports the corresponding capability. Clipboard paste is an input signal; clipboard contents are not stored on InputEvent.

QueuedEvent is the ordered queue entry used when lifecycle and input events must be consumed as a single stream. Its kind field tells whether the entry contains a lifecycle Event or an InputEvent. Use drain_queued_events() when relative ordering matters, for example input -> close-request -> input; use the separate drain_events() and drain_input_events() helpers only when that cross-family ordering is not needed.

Rendering

Rendering is optional. The render-facing source is selected by -d gg_multiwindow or -d x_multiwindow_render; plain lifecycle and .mock imports remain the no-flag isolation case.

The render contract does not expose gfx.Environment, gfx.Swapchain, native drawables, command buffers, RenderFrame, or present authority. It provides owner-thread opaque batch and target leases, backend-issued ready credits, immutable metrics and target snapshots, late target acquisition, one global commit, ordered finalization, and a private recovery anchor. Each acquired target and its stored slot share a nonzero per-window lease epoch; zero, mismatched, rotated, copied, or expired epochs are rejected.

Render-capable windows support exactly sample_count: 1; other sample counts are rejected. Current native backends report Capabilities.readback == false. The gg facade reports both window-capture and offscreen-image readback capabilities as false, and its readback request methods return gg.multiwindow: requested readback is not supported.

The normative graphics references are the V-vendored Sokol API and matching pinned upstream sokol_gfx.h. Backend lifetime and sequencing must also follow DXGI Present, DXGI ResizeBuffers, D3D11 threading, CAMetalLayer, EGL make-current, and EGL swap contracts. Local precedent does not override those lifetime and threading rules.

Relationship With gg

gg.App is the user-facing multi-window facade and is enabled with -d gg_multiwindow. It maps gg types to x.multiwindow and declares the managed sokol.gfx/sokol.sgl surface.

examples/gg/multiwindow.v is the interactive example:

./v -d gg_multiwindow run examples/gg/multiwindow.v

For Linux X11 native rendering, including Xvfb runs, add the X11 backend flag:

xvfb-run -a ./v -d gg_multiwindow -d x_multiwindow_x11 run examples/gg/multiwindow.v

For Wayland, build with -d sokol_wayland. The default build can still fall back to .mock when no enabled native backend is available. The example creates two gg windows, handles lifecycle events, and tolerates backends that reject programmatic resize.

examples/gg/multiwindow_render_runtime.v is the unattended renderer probe used by CI. The backend lanes compile it with -d gg_multiwindow and the matching native flag (-d x_multiwindow_x11, -d sokol_wayland, -d sokol_metal, or -d sokol_d3d11), select the backend with V_MULTIWINDOW_PROBE_BACKEND, and launch it through the process-tree watchdog that owns its parent gate. The probe emits {"example":"multiwindow_render_runtime","status":"PASS","cleanup":"complete"} only after renderer and window cleanup completes.

Limitations

  • X11 support is compiled only with -d x_multiwindow_x11; without that flag, the X11 backend is unsupported and X11/EGL/OpenGL libraries are not linked by low-level lifecycle or .mock imports.
  • Wayland support is compiled only with -d sokol_wayland; without that flag, the Wayland backend is unsupported and Wayland libraries are not linked.
  • Wayland hidden window creation (visible: false) is rejected.
  • Wayland programmatic resize is currently unsupported.
  • X11 programmatic resize is rejected for non-resizable windows.
  • Native app creation can still fail even when plain capabilities report that a backend is supported, for example when a display cannot be opened.
  • The mock backend is not a renderer and cannot produce render targets.
  • Multi-window render targets support only sample_count: 1.
  • Native backends report readback capabilities as false; readback requests made through gg.App return an unsupported error.
  • The module has no layout, widget, text rendering, or drawing abstraction.

Validation

The no-flag lifecycle/source check is:

./v test vlib/x/multiwindow/multiwindow_test.v

This command is a non-render isolation check; it does not establish native renderer behavior. Renderer proofs run in the dedicated X11, Wayland, AppKit, and Win32 CI lanes. Each lane sets VGG_MULTIWINDOW_RUNTIME_PROBES=1, VGG_MULTIWINDOW_RUNTIME_BACKEND, and V_MULTIWINDOW_PROBE_BACKEND, compiles with its native renderer flags, and executes each test and runtime probe through the process-tree watchdog. The watchdog supplies the private parent gate, enforces the deadline, reaps child processes, and checks the final cleanup JSON.

fn capabilities_for_backend #

fn capabilities_for_backend(kind BackendKind) !Capabilities

capabilities_for_backend reports capabilities without creating an App.

fn capabilities_for_backend_with_renderer #

fn capabilities_for_backend_with_renderer(kind BackendKind, require_renderer bool) !Capabilities

capabilities_for_backend_with_renderer reports capabilities with the same backend resolution policy used by new_app(...).

fn capabilities_for_config #

fn capabilities_for_config(config Config) !Capabilities

capabilities_for_config reports capabilities without starting an App while respecting Config.require_renderer for .auto backend selection.

fn new_app #

fn new_app(config Config) !&App

new_app creates a low-level multi-window App with the mock backend by default.

fn AppKitWindowDrawableReleaseMode.from #

fn AppKitWindowDrawableReleaseMode.from[W](input W) !AppKitWindowDrawableReleaseMode

fn AppStatus.from #

fn AppStatus.from[W](input W) !AppStatus

fn BackendKind.from #

fn BackendKind.from[W](input W) !BackendKind

fn BackendTargetStatus.from #

fn BackendTargetStatus.from[W](input W) !BackendTargetStatus

fn CursorShape.from #

fn CursorShape.from[W](input W) !CursorShape

fn EglBindingKind.from #

fn EglBindingKind.from[W](input W) !EglBindingKind

fn EventDeliveryState.from #

fn EventDeliveryState.from[W](input W) !EventDeliveryState

fn EventKind.from #

fn EventKind.from[W](input W) !EventKind

fn InputEventKind.from #

fn InputEventKind.from[W](input W) !InputEventKind

fn InternalFaultStage.from #

fn InternalFaultStage.from[W](input W) !InternalFaultStage

fn NativeLifetimeReleaseKind.from #

fn NativeLifetimeReleaseKind.from[W](input W) !NativeLifetimeReleaseKind

fn NativeLifetimeTicketState.from #

fn NativeLifetimeTicketState.from[W](input W) !NativeLifetimeTicketState

fn NativeLocalValidation.from #

fn NativeLocalValidation.from[W](input W) !NativeLocalValidation

fn NativeOperationAuthorityScope.from #

fn NativeOperationAuthorityScope.from[W](input W) !NativeOperationAuthorityScope

fn NativeOperationTraceMilestone.from #

fn NativeOperationTraceMilestone.from[W](input W) !NativeOperationTraceMilestone

fn NativeRenderCallSite.from #

fn NativeRenderCallSite.from[W](input W) !NativeRenderCallSite

fn NativeRenderDisposition.from #

fn NativeRenderDisposition.from[W](input W) !NativeRenderDisposition

fn NativeRenderDomain.from #

fn NativeRenderDomain.from[W](input W) !NativeRenderDomain

fn NativeRenderOperation.from #

fn NativeRenderOperation.from[W](input W) !NativeRenderOperation

fn NativeRenderScope.from #

fn NativeRenderScope.from[W](input W) !NativeRenderScope

fn NativeRendererHealth.from #

fn NativeRendererHealth.from[W](input W) !NativeRendererHealth

fn QueuedEventKind.from #

fn QueuedEventKind.from[W](input W) !QueuedEventKind

fn RenderAcquireStatus.from #

fn RenderAcquireStatus.from[W](input W) !RenderAcquireStatus

fn RenderBlockReason.from #

fn RenderBlockReason.from[W](input W) !RenderBlockReason

fn RenderFinalizeStatus.from #

fn RenderFinalizeStatus.from[W](input W) !RenderFinalizeStatus

fn RenderRedrawMode.from #

fn RenderRedrawMode.from[W](input W) !RenderRedrawMode

fn RenderWindowRuntimeStatus.from #

fn RenderWindowRuntimeStatus.from[W](input W) !RenderWindowRuntimeStatus

fn RendererFaultMilestone.from #

fn RendererFaultMilestone.from[W](input W) !RendererFaultMilestone

fn RendererShutdownPath.from #

fn RendererShutdownPath.from[W](input W) !RendererShutdownPath

fn WindowDestroyStage.from #

fn WindowDestroyStage.from[W](input W) !WindowDestroyStage

fn WindowResizeEdge.from #

fn WindowResizeEdge.from[W](input W) !WindowResizeEdge

fn WindowStatus.from #

fn WindowStatus.from[W](input W) !WindowStatus

type AppJobFn #

type AppJobFn = fn (mut app App) !

AppJobFn is executed later by the owner queue while App is being pumped.

type RenderBatchFn #

type RenderBatchFn = fn (RenderBatchLease, []RenderWindowSnapshot) !

type RenderPassFn #

type RenderPassFn = fn () !

enum AppStatus #

enum AppStatus {
	running
	stopped
}

AppStatus describes the application lifecycle owned by App.

enum BackendKind #

enum BackendKind {
	auto
	mock
	x11
	wayland
	appkit
	win32
}

BackendKind selects the platform implementation. The .auto policy resolves to a concrete backend before startup/capabilities are reported.

enum CursorShape #

enum CursorShape {
	default
	pointer
	move
	n_resize
	s_resize
	e_resize
	w_resize
	ne_resize
	nw_resize
	se_resize
	sw_resize
	ew_resize
	ns_resize
	nesw_resize
	nwse_resize
	grab
	grabbing
}

CursorShape identifies a native cursor image for client-side chrome hover feedback. Cursor support is independent from native interactive move/resize.

enum EventKind #

enum EventKind {
	window_created
	window_destroyed
	window_close_requested
	window_resized
}

EventKind describes core window lifecycle events emitted by the backend.

enum InputEventKind #

enum InputEventKind {
	invalid
	key_down
	key_up
	char
	mouse_down
	mouse_up
	mouse_scroll
	mouse_move
	mouse_enter
	mouse_leave
	touches_began
	touches_moved
	touches_ended
	touches_cancelled
	resized
	iconified
	restored
	focused
	unfocused
	suspended
	resumed
	quit_requested
	clipboard_pasted
	files_dropped
}

InputEventKind mirrors the input/event classes exposed by sokol/gg while remaining independent from gg and sokol imports.

enum QueuedEventKind #

enum QueuedEventKind {
	lifecycle
	input
}

enum RenderAcquireStatus #

enum RenderAcquireStatus {
	ready
	transient_unavailable
}

enum RenderBlockReason #

enum RenderBlockReason {
	none
	no_workload
	not_configured
	frame_callback_pending
	hidden
	minimized
	occluded
	unmapped
	not_viewable
	zero_sized
	resize_pending
	drawable_unavailable
	backend_unavailable
	renderer_failed
}

RenderBlockReason records the backend fact which withheld a ready credit.

enum RenderRedrawMode #

enum RenderRedrawMode {
	on_demand
	continuous
}

RenderRedrawMode controls low-level frame eligibility for one window.

enum WindowResizeEdge #

enum WindowResizeEdge {
	top
	bottom
	left
	right
	top_left
	top_right
	bottom_left
	bottom_right
}

WindowResizeEdge identifies the edge or corner used for an interactive, user-driven native resize operation.

enum WindowStatus #

enum WindowStatus {
	invalid
	alive
	destroyed
}

WindowStatus describes the lifecycle of a registered window slot.

struct App #

@[heap]
struct App {
mut:
	config                      Config
	instance_id                 u64
	status                      AppStatus = .running
	stopping                    bool
	backend                     Backend
	windows                     []WindowSlot
	events                      []QueuedEvent
	event_deliveries            map[u64]EventDeliveryState
	next_event_delivery_token   u64 = 1
	event_dispatch_events       []QueuedEvent
	event_dispatch_active       bool
	event_dispatch_index        int
	event_delivery_terminal     bool
	teardown_acceptance_order   []WindowId
	window_finished             map[string]bool
	window_terminal             map[string]string
	frame_count                 u64
	deferred_poll_error         string
	deferred_poll_error_active  bool
	deferred_poll_barrier_token u64
	backend_event_terminal      string
	render_runtime              RenderRuntimeState
	render_bridge               voidptr
	owner_thread_id             u64
	admission_open              bool = true
	admission_epoch             u64  = 1
	owner_callback_depth        int
	next_stop_serial            u64 = 1
	stop_serial                 u64
	stop_prepared               bool
	stop_terminal               string
	pending_stop_errors         []string
	stop_native_retry_passes    u8
	internal_fault              InternalFaultPlan
	state_mutex                 &sync.Mutex        = sync.new_mutex()
	fault_mutex                 &sync.Mutex        = sync.new_mutex()
	owner                       &executor.Executor = unsafe { nil }
}

App owns the low-level multi-window registry and owner queue.

fn (App) begin_window_move #

fn (mut app App) begin_window_move(id WindowId) !

begin_window_move starts a user-driven native move for a live window. Backends that require a recent native input serial may reject the request when it is not made from a deliberate user-action path.

fn (App) begin_window_resize #

fn (mut app App) begin_window_resize(id WindowId, edge WindowResizeEdge) !

begin_window_resize starts a user-driven native resize for a live resizable window.

fn (App) capabilities #

fn (app &App) capabilities() Capabilities

capabilities reports the selected backend capabilities.

fn (App) create_window #

fn (mut app App) create_window(config WindowConfig) !WindowId

create_window creates a backend window and returns its generation-checked id.

fn (App) destroy_window #

fn (mut app App) destroy_window(id WindowId) !

destroy_window destroys one live window. The app remains alive when any window, including the last one, is destroyed.

fn (App) drain_events #

fn (mut app App) drain_events() ![]Event

drain_events returns and clears pending lifecycle events.

fn (App) drain_input_events #

fn (mut app App) drain_input_events() ![]InputEvent

drain_input_events returns and clears pending input events without consuming lifecycle events.

fn (App) drain_pending #

fn (mut app App) drain_pending(max_jobs int) !int

drain_pending executes up to max_jobs queued owner callbacks while the app is running. After stop(), accepted-but-pending callbacks are not executed and this returns multiwindow: app is stopped.

fn (App) drain_queued_events #

fn (mut app App) drain_queued_events() ![]QueuedEvent

drain_queued_events returns and clears pending lifecycle/input events in the exact order accepted by App.

fn (App) drain_render_teardown_notices #

fn (mut app App) drain_render_teardown_notices() ![]RenderTeardownNotice

fn (App) finish_stop #

fn (mut app App) finish_stop(ticket AppStopTicket, prior_errors []string) !

fn (App) finish_window_destroy #

fn (mut app App) finish_window_destroy(ticket WindowDestroyTicket, prior_errors []string) !

finish_window_destroy is irreversible. It attempts backend cleanup even when earlier package cleanup failed and records one aggregate terminal outcome.

fn (App) instance_id #

fn (app &App) instance_id() u64

instance_id is process-monotonic and never reused. It stamps every handle, scheduler lease and accepted owner wrapper belonging to this App.

fn (App) logical_to_pixel_render_rect #

fn (app &App) logical_to_pixel_render_rect(id WindowId, metrics_sequence u64, x f32, y f32, width f32, height f32) !(int, int, int, int)

fn (App) pixel_to_logical_render_rect #

fn (app &App) pixel_to_logical_render_rect(id WindowId, metrics_sequence u64, x int, y int, width int, height int) !(f32, f32, f32, f32)

fn (App) poll_events #

fn (mut app App) poll_events() !int

poll_events lets the backend route native lifecycle and input events into App events.

fn (App) post #

fn (mut app App) post(f AppJobFn) !

post submits a short owner-thread callback if queue capacity is available.

fn (App) prepare_stop #

fn (mut app App) prepare_stop() !AppStopTicket

fn (App) prepare_window_destroy #

fn (mut app App) prepare_window_destroy(id WindowId) !WindowDestroyTicket

fn (App) prepare_window_destroy_for_stop #

fn (mut app App) prepare_window_destroy_for_stop(id WindowId) !WindowDestroyTicket

fn (App) render_window_eligible #

fn (app &App) render_window_eligible(id WindowId) !bool

fn (App) render_window_snapshot #

fn (app &App) render_window_snapshot(id WindowId) !RenderWindowSnapshot

fn (App) request_redraw #

fn (mut app App) request_redraw(id WindowId) !

request_redraw is thread-safe admission. Accepted foreign wrappers are app/epoch stamped and become cancellation-only work after stop closes admission; x.executor itself remains unchanged.

fn (App) resize_window #

fn (mut app App) resize_window(id WindowId, width int, height int) !

resize_window requests a native resize and then updates the authoritative App state.

fn (App) rollback_window_destroy #

fn (mut app App) rollback_window_destroy(ticket WindowDestroyTicket) !

fn (App) seal_window_destroy #

fn (mut app App) seal_window_destroy(ticket WindowDestroyTicket) !

fn (App) seal_window_destroy_terminal_for_stop #

fn (mut app App) seal_window_destroy_terminal_for_stop(id WindowId) !WindowDestroyTicket

seal_window_destroy_terminal_for_stop is the irreversible fallback used only after normal prepare/seal failed during stop. Validation happens before the slot is sealed; an accepted live generation already owns its fallback serial.

fn (App) set_render_workload #

fn (mut app App) set_render_workload(id WindowId, enabled bool) !

fn (App) set_window_cursor #

fn (mut app App) set_window_cursor(id WindowId, shape CursorShape) !

set_window_cursor updates the native hover cursor for a live window when the selected backend reports capabilities().cursor_shapes.

fn (App) set_window_title #

fn (mut app App) set_window_title(id WindowId, title string) !

set_window_title updates the native title and then the authoritative App state.

fn (App) status #

fn (app &App) status() AppStatus

status reports the application lifecycle state.

fn (App) stop #

fn (mut app App) stop() !

stop destroys live windows, closes owner-queue admission and marks the app stopped. Pending owner callbacks are canceled logically: public drain_pending() refuses to run them after stop().

fn (App) try_post #

fn (mut app App) try_post(f AppJobFn) !

try_post submits a short owner-thread callback without waiting for capacity. It follows x.executor's ! contract: queue-full and closed-queue states are returned as errors instead of a bool.

fn (App) window_exists #

fn (app &App) window_exists(id WindowId) bool

window_exists reports whether id currently points to a live window.

fn (App) window_ids #

fn (app &App) window_ids() ![]WindowId

window_ids returns live window ids in stable slot order.

fn (App) window_info #

fn (app &App) window_info(id WindowId) !WindowInfo

window_info returns a snapshot of the authoritative App-side window state.

fn (App) window_infos #

fn (app &App) window_infos() ![]WindowInfo

window_infos returns live window snapshots in stable slot order.

fn (App) window_status #

fn (app &App) window_status(id WindowId) !WindowStatus

window_status returns the lifecycle status for a valid generation.

fn (App) with_legacy_render_batch #

fn (mut app App) with_legacy_render_batch(f RenderBatchFn) !RenderBatchOutcome

fn (App) with_scheduled_render_batch #

fn (mut app App) with_scheduled_render_batch(f RenderBatchFn) !RenderBatchOutcome

with_scheduled_render_batch opens one authoritative global transaction. The callback is invoked even with no candidates so app-scoped resource work can initialize and advance without a window frame callback.

fn (App) with_teardown_render_batch #

fn (mut app App) with_teardown_render_batch(f RenderBatchFn) !RenderBatchOutcome

struct AppStopTicket #

struct AppStopTicket {
	app_instance u64
	serial       u64
}

struct Capabilities #

struct Capabilities {
pub:
	backend                 BackendKind
	mock                    bool
	native                  bool
	multi_window            bool
	owner_queue             bool
	explicit_swapchain      bool
	readback                bool
	d3d11                   bool
	metal                   bool
	x11                     bool
	wayland                 bool
	win32                   bool
	gl                      bool
	input_events            bool
	mouse_events            bool
	keyboard_events         bool
	text_events             bool
	focus_events            bool
	drop_events             bool
	touch_events            bool
	cursor_shapes           bool
	interactive_move_resize bool
	native_decorations      bool
}

Capabilities reports what the selected backend can do.

struct Config #

@[params]
struct Config {
pub:
	backend          BackendKind = .mock
	queue_size       int         = 128
	require_renderer bool
}

Config configures a multi-window App.

struct Event #

struct Event {
pub:
	kind      EventKind
	window_id WindowId
	width     int
	height    int
}

Event is always routed to a specific WindowId.

struct InputEvent #

struct InputEvent {
pub:
	kind               InputEventKind
	window_id          WindowId
	frame_count        u64
	key_code           int
	char_code          u32
	key_repeat         bool
	modifiers          u32
	mouse_button       int = input_event_invalid_mouse_button
	mouse_x            f32
	mouse_y            f32
	mouse_dx           f32
	mouse_dy           f32
	scroll_x           f32
	scroll_y           f32
	num_touches        int
	touches            [8]InputTouchPoint
	window_width       int
	window_height      int
	framebuffer_width  int
	framebuffer_height int
	dropped_files      []string
}

InputEvent is always routed to a specific WindowId. It carries the full backend-neutral payload needed to rebuild gg.Event in the gg facade.

struct InputTouchPoint #

struct InputTouchPoint {
pub:
	identifier       u64
	pos_x            f32
	pos_y            f32
	android_tooltype int
	changed          bool
}

InputTouchPoint is the backend-neutral representation of one touch point.

struct QueuedEvent #

struct QueuedEvent {
	delivery_token u64
pub:
	kind      QueuedEventKind
	lifecycle Event
	input     InputEvent
}

QueuedEvent preserves backend ordering between lifecycle and input events.

struct RenderBatchLease #

struct RenderBatchLease {
	app_instance u64
	epoch        u64
	include_all  bool
}

RenderBatchLease is opaque authority for one owner-thread transaction.

fn (RenderBatchLease) epoch_for_gg #

fn (lease RenderBatchLease) epoch_for_gg() u64

epoch_for_gg exposes immutable validation data without exposing scheduler mutation or finish authority.

struct RenderBatchOutcome #

struct RenderBatchOutcome {
	suppressed_callback_target  RenderTargetLease
	suppressed_callback_outcome NativeRenderResult
	suppressed_callback_message string
pub:
	batch_epoch           u64
	committed             bool
	had_gpu_work          bool
	completed_user_passes int
	finalized_submissions int
	error                 string
}

struct RenderMetricsSnapshot #

struct RenderMetricsSnapshot {
pub:
	logical_width        f32
	logical_height       f32
	framebuffer_width    int
	framebuffer_height   int
	dpi_scale            f32
	metrics_sequence     u64
	metrics_available    bool
	conversion_available bool
}

RenderMetricsSnapshot contains only backend-observed values. Conversion is unavailable unless the backend can reproduce it for metrics_sequence.

struct RenderTargetAcquisition #

struct RenderTargetAcquisition {
pub:
	status       RenderAcquireStatus
	lease        RenderTargetLease
	snapshot     RenderWindowSnapshot
	block_reason RenderBlockReason
}

struct RenderTargetLease #

struct RenderTargetLease {
	app_instance u64
	batch_epoch  u64
	target_epoch u64
	window_epoch u64
	window       WindowId
}

RenderTargetLease identifies one acquired target without exposing its swapchain, drawable, command buffer, framebuffer, or native handles.

struct RenderTargetSnapshot #

struct RenderTargetSnapshot {
pub:
	target_identity u64
	color_format    int
	depth_format    int
	sample_count    int
}

struct RenderTeardownNotice #

struct RenderTeardownNotice {
pub:
	window   WindowId
	snapshot RenderWindowSnapshot
	ticket   WindowDestroyTicket
}

struct RenderWindowSnapshot #

struct RenderWindowSnapshot {
pub:
	window               WindowId
	redraw_mode          RenderRedrawMode
	dirty_epoch          u64
	consumed_epoch       u64
	frame_serial         u64
	submitted_frame      u64
	metrics              RenderMetricsSnapshot
	target               RenderTargetSnapshot
	eligibility_sequence u64
	block_reason         RenderBlockReason
	focus_known          bool
	focused              bool
	minimized_known      bool
	minimized            bool
	batch_epoch          u64
}

RenderWindowSnapshot is immutable scheduler and backend state. Batch candidates expose the checked next serial without mutating persistent state.

struct RendererConfig #

struct RendererConfig {
pub:
	buffer_pool_size      int = 128
	image_pool_size       int = 1024
	sampler_pool_size     int = 128
	shader_pool_size      int = 128
	pipeline_pool_size    int = 256
	attachments_pool_size int = 256
}

struct RendererInfo #

struct RendererInfo {
pub:
	color_format int
	depth_format int
	sample_count int
}

struct WindowConfig #

@[params]
struct WindowConfig {
pub:
	title        string = 'V Window'
	width        int    = 800
	height       int    = 600
	min_width    int
	min_height   int
	resizable    bool = true
	visible      bool = true
	high_dpi     bool = true
	borderless   bool
	fullscreen   bool
	sample_count int              = 1
	redraw_mode  RenderRedrawMode = .on_demand
	// render_workload is set by the gg facade. A window without work is never
	// claimed merely because it was exposed or marked dirty.
	render_workload bool
}

WindowConfig describes one window at creation time.

struct WindowDestroyTicket #

struct WindowDestroyTicket {
	app_instance u64
	window       WindowId
	serial       u64
}

WindowDestroyTicket is opaque and app/generation/serial checked at every stage. Only an unsealed ticket can be rolled back.

struct WindowId #

struct WindowId {
	app_instance u64
	slot         int
	generation   u32
}

WindowId is an opaque generation-checked handle to a window.

fn (WindowId) str #

fn (id WindowId) str() string

str returns a diagnostic representation of a WindowId without exposing its fields as public mutable state.

fn (WindowId) app_instance_for_gg #

fn (id WindowId) app_instance_for_gg() u64

app_instance_for_gg exposes immutable identity data needed by the gg facade to reject foreign handles before any local slot lookup.

struct WindowInfo #

struct WindowInfo {
pub:
	id                 WindowId
	status             WindowStatus
	title              string
	width              int
	height             int
	min_width          int
	min_height         int
	resizable          bool
	visible            bool
	high_dpi           bool
	borderless         bool
	fullscreen         bool
	native_decorations bool
}

WindowInfo is a snapshot of the authoritative App-side window state.