Windowing Backends

SimVX ships three Vulkan windowing backends behind a single WindowBackend protocol (simvx.graphics.platform._base). The default selection order is SDL3 → GLFW → Qt (platform/__init__.py:_BACKENDS), and the first import that succeeds wins. You can pin one explicitly:

from simvx.graphics import App

app = App(width=1280, height=720, title="Game", backend="glfw")

Each backend implements every method of the WindowBackend Protocol; the matrix below describes behaviour and feature coverage, not protocol completeness.

Feature matrix

Legend: ✓ supported · partial · ✗ not supported.

Capability

SDL3

GLFW

Qt (PySide6)

Keyboard input

Mouse buttons + cursor pos

Mouse scroll wheel

✓ (mouse_button)

Cursor shape

Gamepad (buttons + axes)

Touch events

✓ (set_touch_callback)

IME / text input (Unicode)

✓ (always-on; see TODO #181)

✓ (always-on)

Clipboard read/write

✗ (use OS shim)

✗ (use OS shim)

✗ (use OS shim)

Drag-and-drop (file paths)

Native file dialogs

✗ (use SimVX UI FileBrowser)

✗ (use SimVX UI FileBrowser)

✗ (use SimVX UI FileBrowser)

Multi-window

✗ (single window)

✗ (single window)

✗ (single window)

Headless / hidden window

✓ (visible=False)

✓ (visible=False)

partial (Qt creates a real window)

Fullscreen toggle

✓ (set_fullscreen)

App lifecycle hooks

✓ (set_lifecycle_callback, mobile)

Pause-when-backgrounded

✓ (paused)

HiDPI content scale

Vulkan surface creation

When to pick each backend

SimVX is backend-agnostic: any windowing library that can hand out a Vulkan surface works. The three implementations shipped today differ only in their input/lifecycle feature set:

  • SDL3: default at autoselect time. Picked first because it is the only backend with multi-touch (mobile, touchscreen kiosks) and full app-lifecycle callbacks. Loaded via PySDL3; falls back to GLFW if the binding is missing.

  • GLFW: desktop fallback. Smaller dependency, faster cold start, and the only backend with built-in fullscreen toggle. Use for kiosk-style desktop apps and headless rendering. No touch and no app-lifecycle callbacks.

  • Qt (PySide6): embedding into Qt host apps. Pick this only when the Vulkan surface needs to live inside an existing Qt main loop (third-party tooling). Touch, gamepad, fullscreen toggle, and headless are all missing : App(visible=False) will still spawn a window. PySide6 is an optional install (simvx-graphics[qt]).

Headless rendering

All three backends support an invisible window via App(visible=False) and App.run_headless(scene, frames=N), but only SDL3 and GLFW honour the window-visibility flag fully. Qt creates a real (off-screen-positioned) window because PySide6 cannot create a Vulkan-capable surface without one. For CI and pytest captures use SDL3 or GLFW.

Gaps tracked in TODO.md

  • Per-focus SDL3 text input toggle (mobile IME flicker): see ## Cross-Platform Preparation.

  • Drag-and-drop file path delivery, not yet plumbed on any backend.

  • Multi-window: out of scope until the multi-threaded renderer entry lands.