Skip to content

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.

LibraryLanguageCategoryLoop ownershipWaylandX11Win32macOSAndroidiOSWayland decoration
WinitRustwindowing librarycallback (own loop)own-drawn Adwaita (sctk-adwaita)
SDL3Cwindowing + media libraryhybrid (poll / callbacks)SSD-first → libdecor CSD
GLFWCminimal windowing librarypoll (app-owned)libdecor → SSD → own-drawn
sokol_app.hC99single-header app shimcallback (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 / GDKC (GObject)full framework (ref. WL client)callback/hybrid (GLib source)own-drawn CSD; only KDE SSD protocol
Flutter EngineC/C++engine / embedder frameworkcallback/hybrid (embedder)N/A — delegates to GTK 3 / GDK
Chromium OzoneC++browser platform-abstractionhybrid (fd → MessagePump)SSD-first → own Views-drawn CSD
AvaloniaC# (.NET)from-scratch Skia UI frameworkhybrid (managed Dispatcher)N/A — no native Wayland (X11/XWayland)
.NET MAUIC# (.NET)native-control-wrapping frameworkcallback (owns no loop)N/A — no Linux desktop backend
Uno PlatformC# (.NET)WinUI-over-Skia frameworkhybrid (per-platform)N/A — no native Wayland (XWayland)
SlintRustreactive UI on a backend abstr.hybrid (winit / calloop KMS)own-drawn CSD via winit (sctk-adwaita)
wxWidgetsC++native-widget-wrapping frameworkcallback/delegated (native)N/A — delegated to GTK
JUCEC++audio-focused GUI frameworkhybrid (own / host-as-plugin)N/A — no Wayland backend (X11 only)
Smithay SCTK + libdecorRust + CWayland client toolkit + CSD helperpoll (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 ownershipWho drivesSubjects
Poll (app-owned)app calls PollEvents / dispatches the loopGLFW, default SDL3, Smithay SCTK
Callback (library/OS-owned)control inverted; the OS/library calls the appWinit, sokol, MAUI, opt-in SDL3
Hybrid (own dispatcher over native pump)toolkit owns a loop wrapping the native sourceQt 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 strategySubjects
Own-drawn CSD (no libdecor)Winit (sctk-adwaita), Qt 6 (plugins), GTK 4, Slint (via winit)
SSD-first, own CSD fallbackChromium Ozone (Views-drawn fallback)
SSD-first, libdecor fallbackSDL3
libdecor-first chainGLFW (libdecor → SSD → own), Smithay SCTK (SSD → FallbackFrame → libdecor)
N/A — delegates to GTKFlutter, wxWidgets
N/A — X11-only / XWaylandsokol, Avalonia, Uno, JUCE
N/A — no Linux desktop backendMAUI

By native coordinate unit

The logical-vs-physical design choice — which unit the API speaks natively.

Native unitSubjects
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 unitSDL3, GLFW (window-size vs framebuffer-size), Flutter (logical + ratio), sokol
Logical, integer scale onlySmithay 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 postureSubjects
Main-thread-only for windows/eventsSDL3, GLFW, Qt 6, GTK 4, Avalonia, MAUI, wxWidgets, JUCE, Flutter, Chromium Ozone (UI thread), Uno
Main-thread on macOS, relaxed elsewhereWinit (MainThreadMarker; with_any_thread on Linux/Win), Slint
No main-thread ruleSmithay 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 shapeSubjects
Typed raw-window-handle (rwh 0.6)Winit, Slint (caller bridges raw pointers in Smithay)
Typed per-platform accessorsGLFW (glfwGet*Window), Qt 6 (QNativeInterface), Avalonia, GTK 4
ABI-stable property / value-struct lookupSDL3 (properties bag), sokol (sapp_get_swapchain)
Type-erased void*sokol, JUCE ("no guarantees what you'll get back")
Literal native window objectMAUI (handler.PlatformView), Flutter (HWND/NSView/GtkWidget)
Raw message-pump passthroughSDL3, Qt 6, Avalonia, Uno, wxWidgets, JUCE, Winit (Win32 msg_hook); GLFW has none
Capability probing / mix-in extensionsAvalonia (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 *.

YearWindowing-layer milestone
2012Qt 5 ships QPA (originally "Lighthouse"): runtime-loaded platform plugins over QWindow, replacing Qt 4's native per-platform code
2013GTK 3.8 introduces GdkFrameClock — one per-surface vsync-synced scheduler
2013–2016Chromium Ozone introduced as the Aura platform-abstraction layer ("Interfaces, not ifdefs")
2013–2014GNOME/Mutter drops server-side decorations; GTK CSD becomes the de-facto model every Wayland client copies (Smithay/libdecor)
2016GLFW 3.2 adds a Wayland backend (crude own-drawn fallback decorations); Qt 5.6 adds opt-in high-DPI scaling
2017Avalonia Wayland backend tracked (#1243) — still unshipped at the studied commit
2017–2019libdecor created (Ådahl/Rauch) to extract reusable, compositor-portable CSD with a dlopen plugin model
2018–2020Winit Event Loop 2.0 (issue #459): the poll_events() iterator replaced by a callback model for Web/iOS/macOS
~2018–2019Flutter Linux desktop embedder evolves from a GLFW prototype to the GObject fl_* GTK 3 API
2019GLFW 3.3 adds the per-monitor content-scale model; JUCE Wayland gap reported (#549, XWayland-only)
~2020Smithay SCTK rewritten onto the wayland-rs 0.30+ handler-trait + Dispatch object model
2020GTK 4.0 removes per-widget GdkWindow for one GdkSurface per toplevel; gdk_toplevel_present() single async entry
2020Qt 6.0 makes high-DPI scaling always-on (no default rounding); per-monitor-v2 DPI on Windows
2020sokol Wayland support requested (issue #245) — still open/unimplemented at the studied commit
2021Chromium 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–2023Flutter multi-view embedder API (FlutterEngineAddView) breaks the one-view-per-engine constraint
2023Avalonia Dispatcher rewrite (#10691): ports the WPF priority model over a tiny per-platform IDispatcherImpl
2023–2024Avalonia 11 marks platform windowing interfaces [Unstable]/[PrivateApi], removed from the public API
2024GLFW 3.4 adds runtime platform selection (single binary holds Wayland+X11) and libdecor decorations (PR #2285)
2024JUCE 8 ships the Direct2D rendering backend on Windows alongside legacy GDI
2024Uno 5.2 introduces from-scratch Skia desktop hosts (X11/Win32/macOS), dropping GTK+3 (−200 MB); multi-window
2024Slint 1.7 upgrades to winit 0.30, forcing the ApplicationHandler + lazy WinitWindowOrNone redesign
2024Winit 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 catalogcomparison → 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

Concepts

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

LibraryOne-line
WinitRust's de-facto windowing library: callback ApplicationHandler, draws nothing, hands a raw-window-handle to the GPU.
SDL3C windowing + media library: hybrid poll/main-callbacks loop, properties-bag native handles, native-coordinate high-DPI.
GLFWMinimal 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 / GDKThe reference Wayland client: one GdkSurface per toplevel, GLib-GSource loop, GdkFrameClock vsync.
Smithay SCTK + libdecorRust Wayland client toolkit + C CSD helper — the Wayland authority for the survey; caller-owned poll loop.

Tier 2 — Own-the-pixels frameworks

LibraryOne-line
Chromium OzoneThe browser's platform-abstraction layer ("interfaces, not ifdefs"); native Wayland + X11, fd → base::MessagePump.
AvaloniaFrom-scratch .NET/Skia UI framework: own windowing, WPF-style managed Dispatcher; X11-only (no native Wayland).
Uno PlatformWinUI-over-Skia .NET framework: from-scratch X11/Win32/macOS hosts (dropped GTK+3); XWayland on Wayland sessions.
SlintRust reactive UI on a Platform/WindowAdapter abstraction stacked over winit (+ a bare-KMS backend).
Flutter EngineEngine + per-platform embedder behind a frozen C ABI; Linux embedder is GTK 3 delegating to GDK.
sokol_app.hSingle-header C99 app shim (window + 3D context + input + entry point); one global window, X11-only on Linux.

Tier 3 — Native-toolkit-wrapping frameworks

LibraryOne-line
wxWidgetsC++ framework wrapping Win32/Cocoa/GTK for native controls; Linux is GTK (X11 + Wayland via GDK).
JUCEAudio-focused C++ framework: small ComponentPeer interface with hand-written native backends; X11-only on Linux.
.NET MAUINative-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: