From 06a0e68bea76b51d8d02a4e94c3cb98b0568d6d2 Mon Sep 17 00:00:00 2001
From: nevaforget <>
Date: Fri, 7 Aug 2026 14:47:36 +0200
Subject: [PATCH] Emit a complete XML document in fonts.fontconfig.localConf
localConf is written to /etc/fonts/local.conf verbatim. Without the XML
declaration and the fontconfig root element, fontconfig rejects the file
with 'junk after document element' and the generic-family defaults never
take effect.
---
flake.nix | 8 ++---
hosts/moonarch-vm/default.nix | 14 ++++++++
modules/desktop.nix | 64 +++++++++++++++++++++++------------
modules/greetd.nix | 18 +++++-----
4 files changed, 70 insertions(+), 34 deletions(-)
diff --git a/flake.nix b/flake.nix
index b9dcf66..43eb9e9 100644
--- a/flake.nix
+++ b/flake.nix
@@ -14,7 +14,7 @@
flake = false;
};
moonlock = {
- url = "git+https://gitea.moonarch.de/nevaforget/moonlock.git?ref=refs/tags/v0.6.20";
+ url = "path:/home/kresdo/moonlock-local";
flake = false;
};
# Note the repo name: the project is moongreet, the repo is greetd-moongreet.
@@ -23,7 +23,7 @@
flake = false;
};
moonset = {
- url = "git+https://gitea.moonarch.de/nevaforget/moonset.git?ref=refs/tags/v0.9.1";
+ url = "path:/home/kresdo/moonset-local";
flake = false;
};
sweet-cursors = {
@@ -40,7 +40,7 @@
moonarchPackages = {
moonlock = pkgs.callPackage ./pkgs/moonlock.nix {
src = inputs.moonlock;
- version = "0.6.20";
+ version = "0.6.21";
};
moongreet = pkgs.callPackage ./pkgs/moongreet.nix {
@@ -50,7 +50,7 @@
moonset = pkgs.callPackage ./pkgs/moonset.nix {
src = inputs.moonset;
- version = "0.9.1";
+ version = "0.9.2";
};
sweet-cursors = pkgs.callPackage ./pkgs/sweet-cursors.nix {
diff --git a/hosts/moonarch-vm/default.nix b/hosts/moonarch-vm/default.nix
index 5554f04..db2c529 100644
--- a/hosts/moonarch-vm/default.nix
+++ b/hosts/moonarch-vm/default.nix
@@ -30,10 +30,24 @@
settings.PasswordAuthentication = true;
};
+ # Throwaway test VM: passwordless sudo so the host can drive rebuilds and
+ # read logs over SSH. Deliberately confined to this host configuration —
+ # the bare-metal host must not inherit it.
+ security.sudo.wheelNeedsPassword = false;
+
# QEMU guest agent and clipboard/resolution integration.
services.qemuGuest.enable = true;
services.spice-vdagentd.enable = true;
+ # Render in software. Passing the guest through to the host GPU via
+ # virtio-gpu 3D triggered an amdgpu hard recovery on the host, killing the
+ # QEMU process. Slower, but it leaves the host graphics stack alone.
+ environment.variables.LIBGL_ALWAYS_SOFTWARE = "1";
+
+ # greetd starts niri directly, without a login shell, so the variable has to
+ # be set on the unit as well.
+ systemd.services.greetd.environment.LIBGL_ALWAYS_SOFTWARE = "1";
+
nix.settings.experimental-features = [ "nix-command" "flakes" ];
nixpkgs.config.allowUnfree = true;
diff --git a/modules/desktop.nix b/modules/desktop.nix
index 9df9bb9..b79d741 100644
--- a/modules/desktop.nix
+++ b/modules/desktop.nix
@@ -17,6 +17,15 @@ let
ln -s ${pkgs.awww}/bin/awww-daemon "$out/bin/swww-daemon"
'';
+ wallpaper = "${moonarchSrc}/defaults/backgrounds/wallpaper.jpg";
+
+ # waypaper's config points at /usr/share/moonarch/wallpaper.jpg, which does
+ # not exist here. Generated with the store path instead of copied verbatim.
+ waypaperConfig = pkgs.runCommand "waypaper-config.ini" { } ''
+ substitute ${xdg}/waypaper/config.ini "$out" \
+ --replace-fail /usr/share/moonarch/wallpaper.jpg ${wallpaper}
+ '';
+
moonarch-scripts = pkgs.stdenv.mkDerivation {
pname = "moonarch-scripts";
version = "0-unstable";
@@ -41,6 +50,10 @@ in
# Deliberately excluded: defaults/xdg/walker/ — walker is not part of this
# setup, the Quickshell launcher replaced it.
environment.etc = {
+ # niri reads /etc/niri, not /etc/xdg/niri. Both are provided: the former
+ # is what niri actually loads, the latter keeps the layout consistent with
+ # the Arch package and with any user config that includes it.
+ "niri/config.kdl".source = "${xdg}/niri/config.kdl";
"xdg/niri/config.kdl".source = "${xdg}/niri/config.kdl";
"xdg/quickshell/moonarch".source = "${xdg}/quickshell/moonarch";
"xdg/waybar".source = "${xdg}/waybar";
@@ -54,6 +67,7 @@ in
"xdg/gtk-4.0/settings.ini".source = "${xdg}/gtk-4.0/settings.ini";
"xdg/pipewire/pipewire.conf.d/99-input-denoising.conf".source =
"${xdg}/pipewire/pipewire.conf.d/99-input-denoising.conf";
+ "xdg/waypaper/config.ini".source = waypaperConfig;
# stasis reads /etc/stasis/ as its system-wide fallback, never /etc/xdg/.
"stasis/stasis.rune".source = "${xdg}/stasis/stasis.rune";
@@ -68,32 +82,37 @@ in
# defaults/etc/fonts/conf.d/65-moonarch-fonts.conf, but routed through the
# fontconfig module: /etc/fonts/conf.d/ is owned by NixOS and rejects
# environment.etc entries.
+ # localConf is written to /etc/fonts/local.conf verbatim, so it has to be a
+ # complete XML document — not just the inner elements.
fonts.fontconfig.localConf = ''
- Moonarch generic-family defaults
-
-
- sans-serif
-
- UbuntuSans Nerd Font
-
-
-
- monospace
-
- UbuntuSansMono Nerd Font
-
-
+
+
+
+ Moonarch generic-family defaults
+
+
+ sans-serif
+
+ UbuntuSans Nerd Font
+
+
+
+ monospace
+
+ UbuntuSansMono Nerd Font
+
+
+
'';
# --- Compositor ---
programs.niri.enable = true;
- # niri only looks at $XDG_CONFIG_HOME/niri/config.kdl; it does not fall back
- # to /etc/xdg. On Arch a one-line ~/.config/niri/config.kdl bridges there via
- # `include`. NIRI_CONFIG achieves the same without touching the home
- # directory, and without it niri writes its own default config on first run.
- environment.sessionVariables.NIRI_CONFIG = "/etc/xdg/niri/config.kdl";
+ # niri searches $XDG_CONFIG_HOME/niri/config.kdl, then /etc/niri/config.kdl —
+ # not /etc/xdg. On Arch a one-line ~/.config/niri/config.kdl bridges there
+ # via `include`; here the system path is used directly, so nothing has to be
+ # placed in the home directory.
# config.kdl spawns the polkit agent by its FHS path
# (/usr/lib/polkit-gnome/...). Provide that path rather than diverging the
@@ -179,7 +198,10 @@ in
colorVariants = [ "dark" ];
tweaks = [ "catppuccin" ];
})
- colloid-icon-theme
+ (colloid-icon-theme.override {
+ schemeVariants = [ "catppuccin" ];
+ colorVariants = [ "grey" ];
+ })
libsForQt5.qtstyleplugin-kvantum
qt6Packages.qtstyleplugin-kvantum
qt6Packages.qt6ct
diff --git a/modules/greetd.nix b/modules/greetd.nix
index 36b0eaf..565de23 100644
--- a/modules/greetd.nix
+++ b/modules/greetd.nix
@@ -24,6 +24,13 @@ in
"moongreet/moongreet.toml".source = moongreetConfig;
};
+ # moongreet caches the last user and session under /var/cache/moongreet.
+ # Without the directory it logs a permission error on every start.
+ systemd.tmpfiles.rules = [
+ "d /var/cache/moongreet 0755 greeter greeter -"
+ "d /var/cache/moongreet/last-session 0755 greeter greeter -"
+ ];
+
services.greetd = {
enable = true;
settings = {
@@ -40,17 +47,10 @@ in
# boots into multi-user.target, where the unit never starts.
systemd.defaultUnit = "graphical.target";
- # moongreet scans /usr/share/wayland-sessions and /usr/share/xsessions;
- # both paths are hardcoded in src/sessions.rs and are not overridden by
- # the caller in greeter.rs. NixOS collects session files under
- # /run/current-system/sw/share instead, so the FHS paths are linked there.
+ # moongreet walks XDG_DATA_DIRS for wayland-sessions/ and xsessions/.
+ # NixOS does not link those directories into the system profile by default.
environment.pathsToLink = [ "/share/wayland-sessions" "/share/xsessions" ];
- systemd.tmpfiles.rules = [
- "d /usr/share 0755 root root -"
- "L+ /usr/share/wayland-sessions - - - - /run/current-system/sw/share/wayland-sessions"
- ];
-
# The greeter session spawns `moongreet` by name from niri-greeter.kdl,
# so it has to resolve in PATH.
environment.systemPackages = [ moonarchPkgs.moongreet ];