Build from source
Building is how you get Moonswing on a non-amd64 machine, on a distribution without
snapd, or with a change of your own in it.
Dependencies
Section titled “Dependencies”Build-time:
libgtk3gtk-layer-shelllibasound2-devlibmpv-dev
On Ubuntu 26.04:
sudo apt install libgtk-3-dev libgtk-layer-shell-dev libasound2-dev libmpv-devTwo more are needed only at runtime, and only by the lock screen. Both are dlopened, so
the shell builds and runs without them — Lock simply does nothing:
sudo apt install libgtk-session-lock0 # plus libpam, which you already haveFlutter
Section titled “Flutter”The shell is built on Flutter’s experimental windowing API and tracks the master
channel, which renames those APIs without notice.
sudo snap install flutter --classicflutter channel masterflutter upgradeflutter config --enable-windowingflutter config --enable-windowing is a one-time, machine-wide setting. Without it the
build fails: every surface the shell draws is a native window.
Build and install
Section titled “Build and install”make install # to ~/.localmake install PREFIX=/usr/local # or anywhere elsemake install builds the release bundle, copies it to $PREFIX/lib/moonswing, installs
the default wallpapers to $PREFIX/share/moonswing, writes a moonswing launcher
into $PREFIX/bin, and registers the ScreenCast portal backend. Make sure $PREFIX/bin is on
your PATH, then:
moonswingTo build without installing:
flutter build linux --release # or: make buildThe two targets that write outside the prefix
Section titled “The two targets that write outside the prefix”The PAM service file
Section titled “The PAM service file”sudo make install-pamWrites /etc/pam.d/moonswing. It needs root, and writes to /etc/pam.d rather than the
prefix, which is why it is a separate target. Without it the lock screen falls back to the
system login service: it still authenticates, it just attributes unlock attempts to login
in the auth logs.
The ScreenCast portal backend
Section titled “The ScreenCast portal backend”make install-portalRun by make install already. It is separate because xdg-desktop-portal discovers backends
through XDG_DATA_HOME and XDG_CONFIG_HOME, never through PREFIX — a custom prefix cannot
move them. It installs moonswing.portal and, if you have no mir-portals.conf of your
own, one naming the shell as the preferred ScreenCast backend. An existing file is kept, and
the target prints what to add to it:
[preferred]org.freedesktop.impl.portal.ScreenCast=moonswingThen pick it up:
systemctl --user restart xdg-desktop-portalThere is deliberately no D-Bus activation file: the shell owns the name from session start, and screen sharing should not be able to start the shell.
Uninstall
Section titled “Uninstall”make uninstallsudo make uninstall-pam # if you installed the PAM service filemake uninstall removes the binary, the app directory and the portal registration file. It
leaves mir-portals.conf in place — that one is user configuration.
Rendering backend
Section titled “Rendering backend”Impeller’s GLES backend is the engine’s Linux default, and is switched off in the runner
(linux/runner/my_application.cc) rather than on the command line — --no-enable-impeller
reaches the engine as an environment variable, which cannot help a snap or a make install
build. To measure with Impeller on:
flutter build linux --profileMOONSWING_IMPELLER=1 ./build/linux/x64/profile/bundle/moonswingThis is expected to be temporary; it is worth re-measuring after an engine bump.