Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Solium

Solium

Solium is the Wayland compositor of Lilium DE, written in Rust on Smithay and configured in Lua. Its window frames, its wallpaper and the desktop shell it hosts are QML running inside the compositor, in one engine, so the desktop can be one design that you change without rebuilding anything.

Captured by the compositor reading back its own framebuffer, in a nested session with the shipped configuration and nothing else, under the light theme Solium shipped until 2026-10-04 and not retaken yet: the shipped theme is now dark grey. The window on the left wears the default frame, a QML scene rendered in-process; the one on the right asked to draw its own and was let. The wallpaper is a QML file too. docs/modes.md has every mode, frame by frame.

Status

Alpha, and not yet anyone’s daily desktop. The first release, v0.1.0, will be a preview release, and it is not cut until the desktop is daily-drivable (the daily-drive label), the native preview shell is written (a bar at the bottom, a dock at the top, quick search, desktop icons, widgets with real data and a native lock screen), and there are packages: the point where it is ready for an open beta. docs/beta.md is that road. It is being readied for daily use, and the daily-driving trial that decides whether it is ready has not happened yet. What stands in the way is the daily-drive label, and the honest reasons are specific:

  • A hosted shell is not yet a whole desktop (#169): it has no icons yet, and the preview shell itself is not written.
  • Suspend and resume have never been tested (#64), and a session on the hardware sometimes starts with no input devices and stops itself (#48).
  • Nothing has run unattended for hours on the hardware (#65).
  • There is no explicit sync, so Vulkan and NVIDIA clients can stutter (#59).
  • There are no touchpad settings, so no tap-to-click (#157); no volume, brightness, media or screenshot keys by default (#151); and logind’s lock and sleep requests are ignored (#153).
  • There are no packages in a repository: a Fedora 44 package, or an install, is built from a checkout (#66).
  • Two tiling bugs found in use are fixed, and stay open until real use bears that out: a window drawing past its tile over its neighbours (#133), and a closing window keeping its tile until its client had gone (#128).

Short of that trial, it has been checked on the hardware at commit dfc95ce, on a desktop with an NVIDIA RTX 3070 and on a Microsoft Surface Pro 7 (Intel Ice Lake integrated graphics), both on Fedora 44. On both, QML renders on the GPU and its animations run, the screens go off when idle, swaylock locks, and the Caps Lock and layout pill works with the shipped configuration. On the Surface Pro 7, closing the lid turned the screen off, though Solium has no lid handling of its own. Touch reaches applications’ windows, but nothing Solium draws itself reacts to touch yet (#181).

And a bug in a compositor takes the session with it. The ones that get found are the ones real use finds, which is why the trial matters more than the test suite.

What works today

  • Layouts. Dwindle tiling, scrolling, floating and overview, each running per monitor, with a workspace per monitor or one for the whole desk. A minimum tile, with a new window overflowing to the next empty workspace (#134); applications’ own minimum and maximum sizes (#115); focus and move by direction, and floating and fullscreen toggles (#150); and a reload that keeps every layout as it was (#116, #118).
  • Windows. A window exists from the keypress that launches it, and shows a loading window until its application has drawn; an X11 application’s window does not yet replace its loading window (#180). Relaunching a single-instance application that is already running brings its existing window forward where it is, instead of moving it into a new slot (#177). Closing and quitting fade out (#126, #127); a resize follows the drag (#113); a modal dialog floats over its parent (#72); a drag shows its icon (#57); and one stacking order serves drawing and clicks, with a fullscreen window covering the bars (#141, #142) and getting its place back after (#92). X11 clients work through XWayland, and menus and tooltips are placed, grabbed and kept on screen.
  • Chrome. Eleven pane styles and per-corner rounding, QML on the GPU by default with a software fallback (#147), and cursor themes and the shapes clients ask for (#81, #24).
  • Shells. shell = { scene = … } hosts one in the compositor’s own QML engine, one instance on every monitor (or on the one shell = { on = … } names), each reading its own monitor live as Solium.monitor (#161). It takes the pointer only where its items take input, so the windows under it can still be clicked (#173). It gets every mouse button, the wheel and the modifiers, a popup holds the pointer with Grab, and an item that asks with Solium.keyboard gets the keyboard (#163). A bar reserves its edge, whatever the scene’s size, with reserve on sol.surface or Solium.surface.reserve from QML (#162). Any layer-shell client works too; bars and lockers both get the frame callbacks they rely on (#149), and a hosted scene’s timers fire on an idle desktop (#164).
  • Monitors. Several at once, each at its own refresh rate, arranged from the configuration or guessed; plugged in and unplugged while the session runs (#43); scaled, worked out from the panel or set. Screens go dark after ten minutes with nobody at the machine, and wlopm and swayidle can turn them off too (#54); the ten-minute screen-off has been seen working on both machines named above.
  • Input. Keyboard layouts, with bindings that keep working under a non-Latin one (#132) and on shifted keys (#121). Escape reaches applications: the overview binds it only while it is open (#174). A small pill near the text field’s caret says Caps Lock is on, or which layout you just switched to; it is written in Lua and QML as configuration rather than in the compositor, and set with keyboard.indicator (#178). On touch screens, touch reaches applications’ windows and a tap focuses the window under it, but nothing Solium draws itself answers touch yet (#181).
  • The lock and idle. The lock fails safe: the session is locked before the locker has drawn, and stays locked if the locker crashes. An idle inhibitor counts only while its window is on screen, and a browser’s D-Bus inhibitor holds the screens on too (#152).
  • The session. Started from the login screen, Solium tells systemd and D-Bus where its display is and starts graphical-session.target and XDG autostart, and stops them when it ends (#146). Programs Solium starts get the environment Solium itself was started with, not the settings Qt has written into it since, and inherit no descriptor beyond stdio (#175).
  • Protocols. xdg-shell, xdg-decoration, xdg-output, xdg-activation, xdg-dialog, wlr-layer-shell, wlr-screencopy, wlr-output-power-management, ext-session-lock, ext-idle-notify, idle-inhibit, cursor-shape, wp-viewporter, wp-fractional-scale, wp-presentation, wp-single-pixel-buffer, linux-dmabuf, relative-pointer, pointer-constraints, primary-selection, text-input-v3 and xwayland-shell. Both selections, copy and paste, cross the X11 boundary. text-input-v3 tells the compositor where the focused text field’s caret is; with no input method yet, no text is sent back (#26).

Not yet

No input method, so no IME and no on-screen keyboard (#26); no clipboard manager, which wants data-control (#52); no window rules (#56); no night light (#69); a virtual machine or a remote desktop cannot have Super (#58); and portal dialogs cannot be parented to the window that opened them (#67). Screenshots and recording come from wlr-screencopy, so grim and wf-recorder should work, but the portals, screen sharing included, have never been tested end to end (#83). Nothing the compositor draws reacts to touch yet (frame buttons, a hosted shell, the overview, window edges), though touch reaches applications’ windows (#181); touch gestures are the touch epic (#7) and come after v0.1.0. docs/gaps.md is everything not built yet, at length.

A first configuration

Everything is a file you write, and none of it needs the compositor rebuilt. ~/.config/solium/user.lua holds only what you want changed, and is merged over the defaults:

return {
    pane = "left",
}

Press super+shift+r and every open window is framed again, with its titlebar down the left side. solium --check says, without starting anything, whether the configuration would load, which bindings it made, which settings it sets that nothing reads, with what you probably meant, and whether every scene it declares at load and every pane style of your own builds without an error or a QML warning. A reload whose configuration fails to load keeps the session as it was, and says why in the log: ~/.local/state/solium/session.log for a session from the login screen or --tty, and the terminal for a nested one.

docs/ricing.md goes on from there, and Every setting lists what there is to set. Your own QML in ~/.config/solium/qml shadows what ships, file by file. A single Solium/Theme.qml there is meant to restyle every frame without copying the rest; until #88 is fixed, the shipped one still wins.

How it is built

Five ideas carry most of the design. Each is a choice with a cost, and the cost is named.

Chrome is QML, inside the compositor

Titlebars, the wallpaper, every pane style and the pointer Solium draws when no cursor theme is set are QML, rendered in-process through QQuickRenderControl and drawn as ordinary render elements. Not a shell talking to a compositor over a protocol — the same process, the same frame. A shell is hosted in that same engine, as QML written against Solium’s own API, through shell = { scene = ... } in the configuration: see docs/shell-boundary.md. Quickshell support was removed (#172).

That is why a titlebar can be reloaded while the session runs, why a hosted shell can read the same theme as the window frames, and why a decoration can be a gradient, a shader or a clock without the compositor learning what any of those are. It is also why Qt is a hard dependency and why the compositor drives Qt by hand. There is no Qt event loop: each frame advances Qt’s animations on the compositor’s clock, and between frames the compositor’s own event loop serves Qt’s timers and the descriptors Qt waits on, on that same clock, so a Timer fires on an idle desktop and costs a frame only when it changes something.

On the hardware, QML renders on the GPU: Qt draws into a buffer the compositor allocated, once a trial render in a short-lived child process has shown that works on this machine. Where it does not, QML renders in software and the log says why. solium --tty --qml gpu or --qml software (or SOLIUM_QML=) forces one, and the last log line beginning QML renderer: says which a session got — see dev/README.md.

A pane owns a window and its chrome

A pane is a window plus the layers drawn around it. Layers have a depth — behind, frame, above — and a bleed, which is how far past the window’s own rectangle a layer may paint. The client is drawn between them, which is the thing a single decoration file cannot express: a glow behind, a titlebar in front, and the window in the middle of its own frame.

A style is a folder under qml/panes/, so a pane style is a bundle you can copy, not a setting you can only choose from.

Everything a window does is one transform

Position, size, opacity, depth and rotation pivot are one Frame per window per moment, and animation is interpolation between two of them. A layout does not move windows; it says where they should be, and the transform layer gets them there. Which is why every mode animates identically and why a thumbnail in overview carries its own titlebar — the frame is part of the window’s transform, not a thing drawn beside it.

The policy is Lua, the arithmetic is a crate

Tiling, scrolling, floating and overview are Lua scripts: when to split, where a new window goes, what a key does. The arithmetic they ask for — the dwindle tree and the scrolling strip — is crates/layout, which knows nothing of windows and also arranges the boxes in dev/preview’s animation page. sol.present hands a script the same transform the compositor uses, so a layout somebody writes is not a second-class one. Bindings and the modes themselves live in Lua too, and --check will tell you what actually loaded — because a configuration that fails to parse at startup leaves a compositor with no layouts and no bindings at all, and that failure should cost a log line rather than a session.

Effects are fragment programs with declared inputs

An effect says what it needs — nothing, or its own pixels — and the compositor arranges the passes. What is behind it is the third input, the one a blur needs, and it is declared and not built yet. Rounded corners are a shader over the window’s own texture, which is why they apply to the pane rather than to the client, and why a corner radius can differ per corner so a titlebar and a window can meet more than one way.

Drawing is GLES2, through Smithay’s GlesRenderer, and the code says so rather than hiding behind Smithay’s generic renderer traits: a window drawn through four corners, the rounded-corner shader and QML on the GPU each need GLES or EGL, which those traits do not offer.

And the parts that are deliberately not ours

Solium does not draw a bar or own a notification of its own: it hosts the shell’s QML as configuration, and layer-shell clients beside it. It tells the session where its display is and leaves starting the rest to systemd. It blanks its screens after ten minutes alone and no other idle policy is its own: it reports idleness and lets a policy daemon act on the rest; it exposes wlr-screencopy and lets a portal record. docs/shell-boundary.md is where that line is drawn and defended.

Why this stack

Smithay is a Wayland compositor library, not a compositor. It hands over protocol plumbing, input and backends while leaving layout, rendering and policy to us — which is where a desktop environment’s character actually lives. niri is Rust-on-Smithay with working touch and gesture support, so the multi-form-factor path has a reference implementation rather than being a bet. Rust because a crash in a compositor takes the session with it, and memory safety removes an entire class of them.

GLES2, because Smithay has no Vulkan renderer — its backend::vulkan is device enumeration only — and niri, the reference implementation this stack was chosen for, uses GLES. The price is that a Vulkan backend would be a port, not a swap: the render elements are typed on GlesRenderer, the four-corner warp calls GL directly, rounded corners are a GLSL ES program, and QML on the GPU shares its buffers through EGL. The spike lists what each would need.

Documentation

All of it is also a website, https://lilium-linux.github.io/solium/, with reference pages made from the code.

ForRead
using itdocs/ricing.md, configuring it — start here; Every setting and the configuration reference, the shipped Key bindings, and Flags and environment
scripting it in Luadocs/modes.md, desktop modes and how to write one; docs/animation.md, the animation engine; the Lua API
styling it in QMLdocs/decorations.md, window frames and everything else drawn in QML; the pane styles’ README, the contract a style is written against
writing a shelldocs/shell-boundary.md, what a hosted shell is given and what belongs to the compositor
working on itCONTRIBUTING.md, how to send a change and the rules it is judged by; docs/architecture.md, how it is built and why; dev/README.md, the build, the gate and the instruments it is tested with; SECURITY.md; THIRD_PARTY.md, what came from other projects
where it standsdocs/status.md, with the roadmap, everything not built yet and the road to a public preview
why it is the way it isthe decision records in docs/spikes/ and docs/design/, each with the evidence that settled it

Thanks to

  • Smithay — Solium is a Smithay compositor, and most of what is hard about being one is Smithay’s work rather than this project’s: DRM, GBM, libinput, the seat, the protocol implementations. Its example compositor is also the first place to look when something here does not make sense.
  • niri — the reference for how a serious Smithay compositor is actually put together, and the answer to more than one “surely this cannot be the way” while reading DRM code. Its scrolling layout is why lua/scrolling.lua exists to be compared against.
  • Hyprland — where this started. Solium began as a Hyprland fork, and that work is archived: it proved the decoration and window-control ideas, and its post-mortem — including why an out-of-process QML shell was the wrong architecture — is in the lilium-de repository. The ideas carried over and none of the code did, but the case that a compositor is allowed to be beautiful and configurable at the same time was made there first.
  • Quickshell — the demonstration that a desktop shell can be QML all the way down. Solium hosts QML inside the compositor rather than beside it, which is a different answer to the same question, and it is a different answer because Quickshell had already shown what the question was.

License

Solium is free software under the GNU General Public License, version 3 only (GPL-3.0-only).

The logo, logotype and default wallpaper are © 2026 Illia Kotomin, licensed CC BY-SA 4.0; see docs/brand.

Contributions are welcome; see CONTRIBUTING.md. Each contributor agrees once to the Contributor License Agreement; the pull request template carries the one sentence that does it. What came from other projects is listed, with its licence, in THIRD_PARTY.md.