backend/linux is a minimal native Wayland host core. It uses the
wzzc-dev/window@0.5.4-0.1.5 Linux package for Wayland event-loop and window handles,
normalizes window/input events through the shared Event contract, and runs
the Showcase entrypoints through the same renderer/runtime boundary as macOS
and Windows. Application entrypoints supply ordered
RendererProvider values for concrete rendering.
backend/linux exposes the window package's Window::present_rgba_pixels
presenter, raw-byte image I/O, and opaque native surface/display handles; Skia and
WGPU construction stays in moui_skia_renderer and moui_wgpu_renderer.
The Wayland window path requests server-side decorations when the compositor
exposes xdg-decoration. If the compositor falls back to client-side
decorations, backend/linux reserves a small titlebar band above the MoUI
content, draws the window title and basic controls into the renderer command
stream, and translates input coordinates so application views still receive a
content-origin coordinate space.
The same adapter consumes the window package's Wayland key/modifier mapping and
current pointer coordinates: Linux backend tests cover modifier propagation into
shared keyboard events and button events using the position carried by the
window event rather than stale pointer state. The fork also exposes Wayland
data-device clipboard selection and file drag/drop events to MoUI; drag/drop
paths continue through Event::DragDrop before reaching View::on_file_drop.
Text-input focus state and IME requests are synchronized through the shared
TextInputSession path used by other native hosts. That session now records
TextInputImeRequestDiagnostics for each enabled/update request, including
grapheme-normalized cursor/anchor character positions, UTF-8 offsets for
surrounding text, the logical candidate-anchor caret rectangle, and whether
surrounding-text payloads fit the window package's IME contract.
Linux runtime requirements are intentionally native:
-
On a Debian-family host,
scripts/set_linux_deps.shinstalls the package sets below in one step. The default set covers the Wayland window core, GLib/zlib link libraries, Weston for headless compositor checks, zenity, and the Skia renderer stack.--minimalrestricts the install to the core Wayland build/runtime set,--with-webviewadds WebKitGTK development packages for native WebView support, and--print-packagesshows the exact apt list for the selected set before installing:sh scripts/set_linux_deps.sh # full default set (Skia included) sh scripts/set_linux_deps.sh --minimal # core Wayland build/runtime only sh scripts/set_linux_deps.sh --with-webview # + WebKitGTK native WebView sh scripts/set_linux_deps.sh --check # verify the installed set
The individual requirements below describe what each package set provides and how the MoUI prebuilds consume them.
-
A Wayland compositor. For repeatable headless checks, run Weston with the headless backend and point
WAYLAND_DISPLAYat its socket. -
A usable Vulkan stack only when running WGPU diagnostics. Headless software validation can use Mesa llvmpipe through
vulkan-swrast/Lavapipe when hardware Vulkan is not available. -
Wayland development headers and generated xdg-shell protocol sources for the
wzzc-dev/window@0.5.4-0.1.5native stub. -
wl_data_device_managerfrom the compositor for native clipboard selection and file drag/drop runtime behavior. -
XDG desktop integration for Linux services: OpenURI goes through xdg-desktop-portal when available and falls back to the desktop opener; file-dialog selections use xdg-desktop-portal with
zenityas the fallback dialog provider. Installzenityif portal is not available:sudo apt-get install zenity
When neither portal nor zenity is available, file and folder selection silently returns cancelled, and the app prints a diagnostic message to stdout.
-
zlib / pthread / fontconfig system libraries for the final native link.
moui/build.jsowns thebackend/linuxzlib entry,moui_skia/build.jsowns the Skia binding's pthread/fontconfig stack, andmoui_wgpu_renderer/build.jsownsmoui_wgpu_renderer/fontconfig. Linux example entrypoints should not repeat-lzor fontconfig stacks; they only need an emptycc-link-flagsoverride so Moon disablestcc -runwhen required. -
glib-2.0 development headers and runtime library.
backend/linuxdrives@services.TimerSourcesubscriptions through the GLib main loop (g_timeout_add/g_source_remove), so themouiprebuild resolvesglib-2.0throughpkg-configand feeds the resulting-Iinclude flags intostub-cc-flagsand merges the libs into thebackend/linuxlink_configsentry. On hosts wherepkg-configcannot findglib-2.0, both resolve to empty (the C stub body is guarded by#ifdef __linux__and only matters on Linux). Distro-specific setups can override the resolved flags withMOUI_LINUX_GLIB_STUB_CC_FLAGSandMOUI_LINUX_GLIB_CC_LINK_FLAGS. -
WebKitGTK development packages (
libwebkit2gtk-4.1-devor4.0) for native WebView support. Themoui_webviewprebuild auto-detectsgtk+-3.0withwebkit2gtk-4.1orwebkit2gtk-4.0throughpkg-config; if found, it enables the native bridge. Fallback builds do not link WebKitGTK and report WebView unavailable. Distro-specific setups can override the detection withMOUI_LINUX_WEBKITGTK_STUB_CC_FLAGSandMOUI_LINUX_WEBKITGTK_CC_LINK_FLAGS.
MoUI's first Linux RISC-V64 path is an experimental architecture variant of
the canonical linux/skia route. It targets riscv64-linux-gnu (glibc/LP64D)
and deliberately selects Skia Raster with static linking. Vulkan, WGPU,
WebView, and matching-device Wayland runtime claims are outside this first
slice.
The fixture is locked to Ubuntu Base 24.04.4 RISC-V64 and Zig 0.16.0 in
checks/toolchains/linux-riscv64.json. On a Linux host with
qemu-user-static, prepare the sysroot and run the L0-L2 helper:
bash scripts/prepare-linux-riscv64-sysroot.sh \
--output .cache/moui/riscv64/sysroot/ubuntu-24.04.4-riscv64
bash scripts/linux-riscv64-cross-build.sh \
--sysroot .cache/moui/riscv64/sysroot/ubuntu-24.04.4-riscv64 \
--run-qemuThe helper sets MOON_CC/MOON_AR to Zig wrappers, redirects pkg-config to
the target sysroot, builds examples/showcase/linux_skia and the offscreen
Skia renderer/text smokes in Moon Release mode, then verifies ELF64, the RISC-V
machine, the LP64D glibc interpreter, static Skia, and the absence of a Vulkan
dependency. The evidence directory includes the complete target package list,
the sysroot file-checksum manifest, ELF reports, executable checksums, and smoke
logs. --run-qemu chroots into the target rootfs for dynamic libraries,
fontconfig, and fonts, and executes only renderer-owned offscreen smokes; it
does not prove a Wayland compositor, input, IME, clipboard, or desktop service.
Cross-build details worth knowing when touching this route:
- The Zig wrapper links against the sysroot
libstdc++(the Skia release archive is built with libstdc++, not zig's bundled libc++) and drops-lstdc++/-lc++from link inputs so zig does not substitute its own libc++. It also strips the--sysrootprefix from absolute-Lpaths (zig re-adds it) and maps host-absolute library dirs (e.g. the Skia release cache) into the sysroot via stable symlinks. - moon links host-prebuilt x86_64 runtime objects (
simdutf.o,moonbit_simdutf.o,libbacktrace.a) from the toolchain lib dir. The helper hides the simdutf objects and substitutes a small riscv64 libbacktrace implementation (scripts/moui-riscv64-libbacktrace-stub.c) for the duration of the cross build, restoring everything afterwards. - QEMU smokes run under a pseudo-terminal so MoonBit's buffered stdout is
flushed per line; otherwise failure details printed immediately before
fail_smoke'sabort()are lost. - Set
MOUI_RISCV64_QEMU_GDB=1(and installgdb-multiarch) to run the text-emoji smoke under gdb via QEMU's gdbstub and print a backtrace at SIGABRT. - The locked sysroot includes
fonts-noto-cjk: the text/emoji smoke renders CJK composition text, and without a CJK font fontconfig cannot fall back, leaving the composed line without dark pixels.
The helper self-test requires no downloaded sysroot and pins the explicit
failure diagnostics for the wrong architecture/ABI, missing target .pc
files, and accidental Vulkan enablement:
bash scripts/test-linux-riscv64-cross-build.shThe architecture evidence contract lives in
checks/architecture-evidence/linux-skia-riscv64.json. Keep its ready=false
and runtimeL3.status=pending until a real RISC-V64 Wayland device produces
matching-host evidence. A device run uses the existing Linux Showcase command
and remains a separate L3 promotion step. Copy the cross-built Showcase ELF to
the matching RISC-V64 Wayland device, then collect first-frame, input, IME,
clipboard, and service logs with:
MOUI_SKIA_RENDERER=skia-raster ./linux_skia.exeUseful focused commands on a configured Linux host:
moon test moui/backend/linux --target native
moon build examples/showcase/linux_skia --target native
moon run examples/showcase/linux_skia --target nativeThe ordinary Linux Skia entrypoints are interactive app entrypoints. Keep
matching-host first-frame smoke in tester/backend smoke runners and store those
logs under ignored artifacts/ paths when they are needed for release notes.
When validating from a Linux VM mounted over the same checkout as a macOS or
Windows host, keep native build output isolated. Either run moon clean before
switching hosts or copy the checkout to a Linux-local temporary directory
without _build; the native archive and MoonDB files are host-specific and can
be corrupted by cross-host reuse.
The WGPU diagnostic factory composes the Linux moui_wgpu_renderer/fontconfig
provider with the shared Moon Cosmic fallback.
The fontconfig provider includes real fontconfig family resolution, FreeType
rasterization (loaded via dlopen), HarfBuzz shaping, embedded-font registration,
and a narrow color-emoji path; MoonBit tests verify protocol versioning and
native payload parsing on all platforms, while the full shaping/measurement/raster
path runs on Linux with the required C libraries. Choose the engine through
@wgpu_renderer.native(text_engine=...); the canonical linux_wgpu route uses
the fontconfig provider with Moon Cosmic as its internal fallback.
Select the native mainline Skia renderer by importing
wzzc-dev/moui_skia_renderer, adding
@render_skia.from_env(platform=@render_skia.NativeGpuPlatform::Linux) to the
app builder, and capturing LinuxHostAppOptions in @linux.entry. The
provider binds a RendererSession backed by @render_skia.SkiaRasterRenderer
and presents the CPU pixel
frame through a narrow API exposed by
wzzc-dev/window/linux. That window package owns the Wayland objects and
provides Window::present_rgba_pixels, implemented with reusable wl_shm
buffers, buffer-release tracking, wl_surface_attach, damage, commit, and
display flush. Keeping the wl_shm presenter in the window backend avoids
duplicating Wayland registry and buffer ownership in MoUI.
Linux native WebView support is auto-detected via pkg-config. When
WebKitGTK development packages are installed, the host syncs placements from
DrawFrame.platform_views using the Wayland surface handle, offsets placement
below client decorations when needed, pumps the GTK main context from the Linux
event-loop wait path, forwards validated navigation/title/history/bridge events
through the WebViewHost, and drains WebViewController tasks after frame
rendering. macOS, Windows, and Linux native bridges enforce the shared
WebViewSecurityPolicy before committing a navigation; blocked URLs produce a
NavigationFailed event. Matching-host smoke is still required before
promoting Linux WebView runtime observation beyond package-level compile coverage.
The Linux host loop drains RendererEvent image requests, keeps only
cancellable byte-I/O tasks, and returns token-matched completions to the
selected session. Linux reads local files into HostImageSource bytes; the
selected renderer owns decode, resource caches, and completion diagnostics.
Applied completions request redraw for the matching Wayland window, while stale
or disposed tokens are ignored. Package tests cover the host route from a
loading first-frame request through completion, repaint request, and second
presented frame. The required
async second-frame runtime artifact remains matching-host pending until a
Wayland run records it from a Skia composition root. Package tests do not prove
a real Wayland compositor presented Showcase frames;
those claims still require matching-host runtime runs and smoke logs
manifest entries.
For Linux Skia runtime evidence, record these as separate ignored
artifacts/ logs on the matching Wayland host:
MOUI_FIRST_FRAME_EXIT=1 \\
moon run examples/showcase/linux_skia --target native
scripts/run-window-package-smoke.sh linux --runThe Showcase log must include
Linux renderer presented first frame; exiting by request; title=... from the
host loop before they can be cited as app-level runtime evidence. The window
package smoke remains dependency-level evidence for Wayland handles,
present_rgba_pixels, resize/redraw, IME request state, and clean shutdown.
The window package carries a consumer-style Linux smoke for this dependency
surface. On a matching Wayland host, run
scripts/run-window-package-smoke.sh linux --run to exercise
surface creation, public Wayland handles, Window::present_rgba_pixels, resize,
redraw, IME request state, and clean shutdown. Add --require-input or
WINDOW_MOUI_LINUX_REQUIRE_INPUT=1 only when representative pointer/keyboard
input is observed. Linux clipboard selection, file dialogs, text-file reads and
writes, desktop URL opening, IME composition/cursor geometry, and file
drag/drop are implemented host-service/input paths, but they remain
matching-host runtime evidence boundaries: cite only logs that exercised the
actual desktop/compositor service, not the package preflight summary alone.
Record dependency-level facts from the wzzc-dev/window@0.5.4-0.1.5
package smoke artifacts; keep the MoUI Showcase
linux_skia run as the mainline application observation. Keep linux_wgpu as
a WGPU diagnostic observation when a Vulkan/WGPU stack is configured.
Linux WebView runtime evidence belongs to a matching-host tester/backend probe. Package tests cover pure event/command mapping and fallback capability paths, but cannot prove that a real WebKitGTK view was presented.
examples/showcase/linux_skia selects the canonical Linux Skia provider route.
Configure real Skia link flags before relying on native Skia-rendered pixels.
The default JetBrains Linux provider links fontconfig, FreeType, and HarfBuzz;
with those libraries available, moui_skia builds a system FontMgr through
fontconfig and falls back to common font directories such as /usr/share/fonts
when fontconfig reports no families. Missing CJK or emoji glyph coverage still
depends on installed system fonts, and full mixed-script fallback runs remain a
text-system follow-up rather than a Linux backend responsibility.
Linux native context menus use the shared HostServiceBridge::ShowMenu
contract. The backend encodes enabled command rows for a desktop menu picker,
dispatches the selected ActionCommand through HostRuntimeDriver, and reports
an unavailable response when the configured desktop menu tool is absent.
Linux AT-SPI accessibility work stays behind backend/linux: the neutral
binding and native GDBus transport publish committed snapshots/deltas, carry
exact-generation actions back through the shared runtime entrypoint, and tear
down window-scoped objects. Native accessibility readiness remains false until
the transport registers on the AT-SPI accessibility bus/Registry and a real
AT-SPI client proves tree queries, actions, focus, deletion, and announcements.
Session-bus object export and package tests are not matching-host evidence.
Remaining Linux gaps stay visible in backend/linux.readiness():
- Linux clipboard, file-dialog, text-file, open URL, text-input/IME request, and file drag/drop host surfaces are implemented, but passed platform status still requires matching-host Wayland/desktop-service observation rather than package preflight alone.
On 2026-07-11, an end-to-end runtime evidence capture was completed on WSL2 + WSLg (Debian 13 on Windows):
bash window/scripts/capture_moui_runtime_evidence.sh linux \
--log artifacts/platform-evidence/linux/moui-linux-runtime.logPassed:
- ✅ Wayland surface/handles/present/cursor/resize/redraw — all working correctly
- ✅ IME probe: all 8 fields passed (
enabled,hint,surrounding,cursor,updated,updated_hint,updated_cursor,disabledalltrue) - ✅ Clipboard data device:
clipboard=true clipboard_roundtrip=true drag_drop=true - ✅
check_ci.shCI check passed
Still requires a real Wayland desktop:
- ❌ Interactive pointer/keyboard input (cannot be sent automatically in WSL2)
- ❌ Complete destroy sequence (requires focused window interaction)
The IME protocol functionality has been verified via WSL2. Full L3 runtime
pass requires running WINDOW_MOUI_LINUX_REQUIRE_INPUT=1 mode on a real Wayland
desktop (Ubuntu 24.04+) with actual keyboard presses and mouse clicks.