Window-System Integration
A breadth-first survey of how fifteen toolkits, frameworks, and shims actually talk to the platform window system — the seam between an application and Wayland, X11, Win32, AppKit, and the mobile/web hosts. It maps the recurring hazards and design forks of the windowing layer (decorations, coordinates, input translation, the loop, popups, frame pacing) against the consensus the field has converged on, to inform a from-scratch, cross-platform windowing layer for Sparkles — targeting Wayland, X11, Windows (Win32), and macOS (AppKit) first, with Android and iOS/iPadOS to follow. ("Wayland-first" below means preferring Wayland over X11 on Linux, not prioritizing Linux over Windows or macOS.)
This survey answers six questions:
- Concepts — what is the shared vocabulary of the windowing layer, from client-vs-server decorations and the scale factor to the no-buffer-no-window handshake and readiness-vs-completion loop ownership? See concepts.
- How each toolkit integrates — for each surveyed system, how does it create windows, own (or cede) the loop, translate input, speak Wayland, scale, handle multi-window and popups, thread, manage the clipboard, and expose escape hatches? See the master catalog and the per-library deep-dives.
- Where the field is unanimous vs where it forks — which behaviours are table stakes a new toolkit should simply adopt, and which are genuine architectural decisions with a strong argument on each side? See comparison.
- The per-platform gotchas — the concrete, citation-backed traps each platform (Wayland, X11, Win32, macOS) springs on a windowing layer, as a quick-reference cheat sheet. See platform-gotchas.
- The gap a green-field framework faces — where a brand-new windowing layer stands today against the consensus, per dimension. See the delta table in comparison.
- What Sparkles should build — the resolved positions on each fork and a milestoned roadmap for the cross-platform windowing layer. See recommendations.
NOTE
Scope. This is the master index for the window-system-integration research tree. Each row links to a deep-dive that was written and fact-checked independently against the upstream source tree at a pinned commit; where this index summarizes a system, the deep-dive is the source of truth. Each deep-dive applies the same ten-dimension analysis spine (window lifecycle, event loop, input/IME, Wayland specifics, DPI/scaling, multi-window/popups, threading, clipboard/DnD, escape hatches, history/regrets); where a dimension does not apply to a subject, the absence is recorded as a finding.
Last reviewed: June 8, 2026
Master Catalog
One row per surveyed system; the library name links to its deep-dive. Loop ownership is the readiness-vs-completion loop-ownership fork (poll = app-owned; callback = library/OS-owned; hybrid = both, behind one dispatcher). The six platform columns mark the backends each deep-dive covers — ✓ supported, – not. The Wayland column means a native (or GTK-delegated) Wayland client, so Avalonia and Uno — which reach Wayland only through XWayland — show –; additional targets (Web/WASM, KMS/DRM, embedded) are noted in each deep-dive. Wayland decoration is the CSD/SSD strategy — mostly N/A because most subjects are not native Wayland clients, itself the survey's headline finding.
| Library | Language | Category | Loop ownership | Wayland | X11 | Win32 | macOS | Android | iOS | Wayland decoration |
|---|---|---|---|---|---|---|---|---|---|---|
| Winit | Rust | windowing library | callback (own loop) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | own-drawn Adwaita (sctk-adwaita) |
| SDL3 | C | windowing + media library | hybrid (poll / callbacks) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | SSD-first → libdecor CSD |
| GLFW | C | minimal windowing library | poll (app-owned) | ✓ | ✓ | ✓ | ✓ | – | – | libdecor → SSD → own-drawn |
| sokol_app.h | C99 | single-header app shim | callback (own loop) | – | ✓ | ✓ | ✓ | ✓ | ✓ | N/A — no Wayland backend (X11 only) |
| Qt 6 (QPA) | C++ | full GUI framework (QPA layer) | hybrid (Qt owns QEventLoop) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | own-drawn CSD plugins (predates libdecor) |
| GTK 4 / GDK | C (GObject) | full framework (ref. WL client) | callback/hybrid (GLib source) | ✓ | ✓ | ✓ | ✓ | ✓ | – | own-drawn CSD; only KDE SSD protocol |
| Flutter Engine | C/C++ | engine / embedder framework | callback/hybrid (embedder) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | N/A — delegates to GTK 3 / GDK |
| Chromium Ozone | C++ | browser platform-abstraction | hybrid (fd → MessagePump) | ✓ | ✓ | – | – | – | – | SSD-first → own Views-drawn CSD |
| Avalonia | C# (.NET) | from-scratch Skia UI framework | hybrid (managed Dispatcher) | – | ✓ | ✓ | ✓ | ✓ | ✓ | N/A — no native Wayland (X11/XWayland) |
| .NET MAUI | C# (.NET) | native-control-wrapping framework | callback (owns no loop) | – | – | ✓ | ✓ | ✓ | ✓ | N/A — no Linux desktop backend |
| Uno Platform | C# (.NET) | WinUI-over-Skia framework | hybrid (per-platform) | – | ✓ | ✓ | ✓ | ✓ | ✓ | N/A — no native Wayland (XWayland) |
| Slint | Rust | reactive UI on a backend abstr. | hybrid (winit / calloop KMS) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | own-drawn CSD via winit (sctk-adwaita) |
| wxWidgets | C++ | native-widget-wrapping framework | callback/delegated (native) | ✓ | ✓ | ✓ | ✓ | – | – | N/A — delegated to GTK |
| JUCE | C++ | audio-focused GUI framework | hybrid (own / host-as-plugin) | – | ✓ | ✓ | ✓ | ✓ | ✓ | N/A — no Wayland backend (X11 only) |
| Smithay SCTK + libdecor | Rust + C | Wayland client toolkit + CSD helper | poll (caller-owned) | ✓ | – | – | – | – | – | three-tier: SSD → FallbackFrame → libdecor |
Two structural facts fall straight out of this table and frame the whole survey: the "Wayland decoration" column is mostly empty — only ~6 of 15 speak Wayland natively, so native Wayland is the field's single biggest gap (see comparison §Dimension 4); and loop ownership and native coordinate unit are the two axes that genuinely fork — every other dimension trends to a consensus (comparison Part 3).
Taxonomy
Each table re-cuts the same fifteen subjects by one axis; every row links back to a deep-dive. The full per-dimension treatment is in comparison.
By loop ownership
The readiness-vs-completion loop-ownership fork: who calls whom. On Linux every loop is, underneath, a readiness reactor over the display-socket fd.
| Loop ownership | Who drives | Subjects |
|---|---|---|
| Poll (app-owned) | app calls PollEvents / dispatches the loop | GLFW, default SDL3, Smithay SCTK |
| Callback (library/OS-owned) | control inverted; the OS/library calls the app | Winit, sokol, MAUI, opt-in SDL3 |
| Hybrid (own dispatcher over native pump) | toolkit owns a loop wrapping the native source | Qt 6, GTK 4, Avalonia, Uno, JUCE, Flutter, Chromium Ozone, wxWidgets, Slint |
By Wayland decoration strategy
The CSD/SSD choice. "SSD is only a hint", so every native client carries a CSD path; the rest sidestep the question by not being a native Wayland client.
| Decoration strategy | Subjects |
|---|---|
| Own-drawn CSD (no libdecor) | Winit (sctk-adwaita), Qt 6 (plugins), GTK 4, Slint (via winit) |
| SSD-first, own CSD fallback | Chromium Ozone (Views-drawn fallback) |
| SSD-first, libdecor fallback | SDL3 |
| libdecor-first chain | GLFW (libdecor → SSD → own), Smithay SCTK (SSD → FallbackFrame → libdecor) |
| N/A — delegates to GTK | Flutter, wxWidgets |
| N/A — X11-only / XWayland | sokol, Avalonia, Uno, JUCE |
| N/A — no Linux desktop backend | MAUI |
By native coordinate unit
The logical-vs-physical design choice — which unit the API speaks natively.
| Native unit | Subjects |
|---|---|
| Physical (app converts via scale factor) | Winit (PhysicalSize/PhysicalPosition) |
| Logical (DIPs; physical only at the OS seam) | Qt 6, GTK 4, Avalonia, JUCE, Slint, MAUI, Uno, wxWidgets, Chromium Ozone (DIP) |
| Mixed / per-platform native unit | SDL3, GLFW (window-size vs framebuffer-size), Flutter (logical + ratio), sokol |
| Logical, integer scale only | Smithay SCTK (no fractional-scale — the cautionary tale) |
By threading model
The most unanimous dimension: GUI = main thread, forced by macOS AppKit (see comparison §Dimension 7).
| Threading posture | Subjects |
|---|---|
| Main-thread-only for windows/events | SDL3, GLFW, Qt 6, GTK 4, Avalonia, MAUI, wxWidgets, JUCE, Flutter, Chromium Ozone (UI thread), Uno |
| Main-thread on macOS, relaxed elsewhere | Winit (MainThreadMarker; with_any_thread on Linux/Win), Slint |
| No main-thread rule | Smithay SCTK (Wayland has no such constraint) |
By escape-hatch shape
How the native handle is exposed for GPU interop and raw passthrough. Every toolkit exposes something; the fork is how typed it is.
| Escape-hatch shape | Subjects |
|---|---|
Typed raw-window-handle (rwh 0.6) | Winit, Slint (caller bridges raw pointers in Smithay) |
| Typed per-platform accessors | GLFW (glfwGet*Window), Qt 6 (QNativeInterface), Avalonia, GTK 4 |
| ABI-stable property / value-struct lookup | SDL3 (properties bag), sokol (sapp_get_swapchain) |
Type-erased void* | sokol, JUCE ("no guarantees what you'll get back") |
| Literal native window object | MAUI (handler.PlatformView), Flutter (HWND/NSView/GtkWidget) |
| Raw message-pump passthrough | SDL3, Qt 6, Avalonia, Uno, wxWidgets, JUCE, Winit (Win32 msg_hook); GLFW has none |
| Capability probing / mix-in extensions | Avalonia (TryGetFeature<T>), Chromium Ozone (X11Extension), Slint (WindowAdapter) |
Milestones
A timeline of windowing-layer redesigns across the field, drawn from the per-subject history sections. Entries dated 2025–2026 are taken from each project's development tree at the pinned commit ("as observed in this checkout") and are forward-dated relative to general public knowledge; they are marked *.
| Year | Windowing-layer milestone |
|---|---|
| 2012 | Qt 5 ships QPA (originally "Lighthouse"): runtime-loaded platform plugins over QWindow, replacing Qt 4's native per-platform code |
| 2013 | GTK 3.8 introduces GdkFrameClock — one per-surface vsync-synced scheduler |
| 2013–2016 | Chromium Ozone introduced as the Aura platform-abstraction layer ("Interfaces, not ifdefs") |
| 2013–2014 | GNOME/Mutter drops server-side decorations; GTK CSD becomes the de-facto model every Wayland client copies (Smithay/libdecor) |
| 2016 | GLFW 3.2 adds a Wayland backend (crude own-drawn fallback decorations); Qt 5.6 adds opt-in high-DPI scaling |
| 2017 | Avalonia Wayland backend tracked (#1243) — still unshipped at the studied commit |
| 2017–2019 | libdecor created (Ådahl/Rauch) to extract reusable, compositor-portable CSD with a dlopen plugin model |
| 2018–2020 | Winit Event Loop 2.0 (issue #459): the poll_events() iterator replaced by a callback model for Web/iOS/macOS |
| ~2018–2019 | Flutter Linux desktop embedder evolves from a GLFW prototype to the GObject fl_* GTK 3 API |
| 2019 | GLFW 3.3 adds the per-monitor content-scale model; JUCE Wayland gap reported (#549, XWayland-only) |
| ~2020 | Smithay SCTK rewritten onto the wayland-rs 0.30+ handler-trait + Dispatch object model |
| 2020 | GTK 4.0 removes per-widget GdkWindow for one GdkSurface per toplevel; gdk_toplevel_present() single async entry |
| 2020 | Qt 6.0 makes high-DPI scaling always-on (no default rounding); per-monitor-v2 DPI on Windows |
| 2020 | sokol Wayland support requested (issue #245) — still open/unimplemented at the studied commit |
| 2021 | Chromium Ozone adds a dedicated Wayland fd-watch thread (prepare_read/EGL deadlock fix); legacy non-Ozone X11 removed |
| 2021+ | GTK 4 Wayland-first protocol expansion: fractional-scale-v1, xdg-activation, cursor-shape-v1, presentation-time |
| 2022 | .NET MAUI 1.0 replaces Xamarin.Forms renderers with the handler / PropertyMapper architecture |
| 2022–2023 | Flutter multi-view embedder API (FlutterEngineAddView) breaks the one-view-per-engine constraint |
| 2023 | Avalonia Dispatcher rewrite (#10691): ports the WPF priority model over a tiny per-platform IDispatcherImpl |
| 2023–2024 | Avalonia 11 marks platform windowing interfaces [Unstable]/[PrivateApi], removed from the public API |
| 2024 | GLFW 3.4 adds runtime platform selection (single binary holds Wayland+X11) and libdecor decorations (PR #2285) |
| 2024 | JUCE 8 ships the Direct2D rendering backend on Windows alongside legacy GDI |
| 2024 | Uno 5.2 introduces from-scratch Skia desktop hosts (X11/Win32/macOS), dropping GTK+3 (−200 MB); multi-window |
| 2024 | Slint 1.7 upgrades to winit 0.30, forcing the ApplicationHandler + lazy WinitWindowOrNone redesign |
| 2024 | Winit 0.30 ships the ApplicationHandler trait; deprecates closure-based EventLoop::run for run_app |
| 2024–2025* | Flutter desktop multi-window with WindowArchetype; the flutter/engine repo merges into the flutter/flutter monorepo |
| 2025* | SDL 3.2.0 (first stable SDL3): main-callbacks model + properties bag + native-coordinate high-DPI overhaul |
| 2025* | wxWidgets 3.3.x: src/gtk/wayland.cpp opens its own wl_display for pointer-warp-v1, bypassing GDK; GTK4 still not shipping |
| 2025–2026* | sokol decouples window/swapchain from sokol-gfx; macOS/iOS migrate MTKView → CAMetalLayer + CADisplayLink |
| 2025–2026* | Smithay SCTK 0.20.0 + the delegate_dispatch2! collapse (breaking dispatch-model simplification) |
| 2026* | Slint 1.16: winit becomes the default backend on all platforms (Qt no longer default on Linux) |
The two redesigns every verdict converges on regretting: (a) deferring native Wayland inherits XWayland's ceiling (no fractional scale, no frame-callback vsync, no xdg_popup grabs) — the Avalonia (8-year delay), JUCE, Uno, sokol, and MAUI story; and (b) under-specifying the windowing contract (pushing title/decoration/popups/fullscreen onto embedders) yields "silently unsupported per platform" — the Flutter and MAUI story.
Quick Navigation
Suggested reading paths
- "I want the vocabulary first." concepts → one native-Wayland deep-dive (Winit or SDL3) → Smithay + libdecor (the Wayland authority).
- "I want the cross-toolkit comparison." Master catalog → comparison → individual deep-dive rows of interest.
- "I want the per-platform traps." platform-gotchas (Wayland / X11 / Win32 / macOS cheat sheet) → the relevant dimension in comparison.
- "I care about a specific dimension" (e.g. IME, DPI, decorations). concepts section for the term → the matching comparison dimension → the deep-dives it cites.
- "I am designing the new framework windowing layer." comparison (consensus
- forks + the delta table) → recommendations (resolved positions + milestoned roadmap), with platform-gotchas as the trap checklist and Smithay + libdecor as the Wayland-client implementation reference. The windowing loop's relationship to an external async runtime is in the async-io readiness concept.
Concepts
- Window-System Concepts — the shared vocabulary every deep-dive references: CSD vs SSD, scancode/keysym/virtual-key, logical vs physical coordinates, the scale factor & fractional scaling, IME pre-edit, override-redirect vs
xdg_popupgrab, the Win32 modal resize loop, raw vs accelerated pointer, no-buffer-no-window, frame-callback vsync, and readiness vs completion in the loop. Several concepts carry a CI-verified runnable D snippet.
Synthesis
- Comparison — the capstone: the at-a-glance master table, the ten-dimension head-to-head, the consensus standards, the four architectural forks, and the delta table for a green-field framework.
- Platform Gotchas — the per-platform (Wayland / X11 / Win32 / macOS) trap cheat sheet, cross-linked to the deep-dive that hit each one.
- Recommendations — the resolved position on every fork and a prioritized roadmap for the cross-platform Sparkles windowing layer.
Library deep-dives
Grouped by how they relate to the windowing layer. Tier 1 are windowing-layer specialists — native clients you would study to build one. Tier 2 own the pixels (draw everything; the OS window is just a surface). Tier 3 wrap or delegate to a native toolkit, inheriting its windowing posture.
Tier 1 — Windowing libraries & native clients
| Library | One-line |
|---|---|
| Winit | Rust's de-facto windowing library: callback ApplicationHandler, draws nothing, hands a raw-window-handle to the GPU. |
| SDL3 | C windowing + media library: hybrid poll/main-callbacks loop, properties-bag native handles, native-coordinate high-DPI. |
| GLFW | Minimal C windowing library: app-owned poll loop, window-vs-framebuffer split, libdecor→SSD→own CSD. |
| Qt 6 (QPA) | Full C++ framework whose QPA layer is a runtime-loaded platform plugin over QWindow; own-drawn Wayland CSD. |
| GTK 4 / GDK | The reference Wayland client: one GdkSurface per toplevel, GLib-GSource loop, GdkFrameClock vsync. |
| Smithay SCTK + libdecor | Rust Wayland client toolkit + C CSD helper — the Wayland authority for the survey; caller-owned poll loop. |
Tier 2 — Own-the-pixels frameworks
| Library | One-line |
|---|---|
| Chromium Ozone | The browser's platform-abstraction layer ("interfaces, not ifdefs"); native Wayland + X11, fd → base::MessagePump. |
| Avalonia | From-scratch .NET/Skia UI framework: own windowing, WPF-style managed Dispatcher; X11-only (no native Wayland). |
| Uno Platform | WinUI-over-Skia .NET framework: from-scratch X11/Win32/macOS hosts (dropped GTK+3); XWayland on Wayland sessions. |
| Slint | Rust reactive UI on a Platform/WindowAdapter abstraction stacked over winit (+ a bare-KMS backend). |
| Flutter Engine | Engine + per-platform embedder behind a frozen C ABI; Linux embedder is GTK 3 delegating to GDK. |
| sokol_app.h | Single-header C99 app shim (window + 3D context + input + entry point); one global window, X11-only on Linux. |
Tier 3 — Native-toolkit-wrapping frameworks
| Library | One-line |
|---|---|
| wxWidgets | C++ framework wrapping Win32/Cocoa/GTK for native controls; Linux is GTK (X11 + Wayland via GDK). |
| JUCE | Audio-focused C++ framework: small ComponentPeer interface with hand-written native backends; X11-only on Linux. |
| .NET MAUI | Native-control-wrapping .NET framework (Windows/Mac Catalyst/iOS/Android/Tizen); no Linux desktop backend. |
Sources
Each deep-dive carries its own primary-source citations, pinned to a specific upstream commit and quoting the source tree verbatim. The cross-cutting artifacts behind this index's classifications are:
- Shared vocabulary — the concepts page (decorations, coordinates, scale, IME, popups, the loop), each term grounded in a cited deep-dive and the canonical external reference (Wayland protocols on wayland.app, the X11 ICCCM spec, xkbcommon, libdecor, and Wayback-pinned Win32/AppKit docs).
- Per-subject sources — the upstream repository trees and official docs cited in each linked deep-dive: Winit, SDL3, GLFW, sokol, Qt 6, GTK 4, Flutter Engine, Chromium Ozone, Avalonia, .NET MAUI, Uno, Slint, wxWidgets, JUCE, Smithay SCTK + libdecor.
- Cross-tree siblings — the async-io readiness loop concepts the windowing loop shares, and the ui-layout catalog of the rendering/layout layers that sit on top of a windowing substrate.