All checks were successful
Check / eval (push) Successful in 3m59s
The logo was off-centre on the external when booting/shutting down docked. Both earlier diagnoses in this repo were WRONG, and that is the part worth keeping: Window.GetWidth() returns max_width (not head 0), and per-head sprites would have DUPLICATED the logo — the script plugin already builds one max_width x max_height canvas, centres every display inside it (script-lib-sprite.c:536) and draws sprites at (sprite.x - display.x), so a canvas-centred sprite is centred on every head. The arithmetic was right. The bug was TIME: every position was a top-level statement evaluated once at parse time. A head arriving/leaving mid-splash resizes the canvas, the plugin re-centres the displays, and frozen sprites end up off by (new_max - old_max)/2 on every head. One monitor never resizes the canvas — hence "only when docked". Fix: one layout(), re-run from the existing refresh callback on canvas change. The trap that cost two attempts (now a comment): in plymouth script a bare assignment inside a function writes the GLOBAL if that name already exists globally. So `canvas_width = Window.GetWidth()` updated global.canvas_width BEFORE the guard compared against it — always false, body never ran, splash rendered as a bare background, NO error logged. Isolated with a 20-line probe. Hence cw/ch. #145 rides along: a VT loads one keymap and knows nothing of per-device layouts, so the passphrase box types with the console layout — worth saying before three wrong tries on a disk you cannot read (same gap as #114, one step earlier). Fedora's mechanism does not port: ply_keymap_icon is a C widget in the two-step plugin (the script plugin has no keyboard API), fed from XKBLAYOUT in /etc/vconsole.conf, which NixOS never writes (plymouth's trace says `XKBLAYOUT: (null)`). So @LAYOUT@ is baked from services.xserver.xkb.layout like the palette — the FIRST of a comma list, since that is what the VT loads. Icon is plymouth's own keyboard.png (the same glyph Fedora shows), copied at build time so no GPL bytes enter the repo, recoloured to subtext. chmod +w after that copy: store files are 444 and recolor rewrites in place. Without it magick fails, the phase aborts, and EVERY LATER SED SILENTLY DOES NOT RUN — the theme shipped with @BG_R@ literals and I only caught it by reading the build log. Verified by render (tools/plymouth-preview.sh, real built theme): both heads centred, password dialog showing padlock + entry + keyboard icon and "us". V3 queued — only a real docked boot can resize a canvas. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
142 lines
6.0 KiB
Nix
142 lines
6.0 KiB
Nix
# Plymouth boot splash — Nomarchy-branded (ported from the legacy
|
||
# iteration), background tinted from the same state.json that
|
||
# drives the desktop (nomarchy.system.stateFile, wired automatically by
|
||
# lib.mkFlake). One caveat by design: theme switches are Home
|
||
# Manager-only and never touch the initrd, so the splash follows the
|
||
# theme as of the last SYSTEM rebuild (`sys-update`), not the last
|
||
# `nomarchy-state-sync apply`.
|
||
{ config, lib, pkgs, ... }:
|
||
|
||
let
|
||
cfg = config.nomarchy.system;
|
||
|
||
state =
|
||
if cfg.stateFile != null
|
||
then import ../state-read.nix { inherit lib; } cfg.stateFile
|
||
else { };
|
||
colorOf = key: fallback: lib.removePrefix "#" ((state.colors or { }).${key} or fallback);
|
||
# Fallbacks match the distro default theme (Boreal) when stateFile is null.
|
||
base = colorOf "base" "#21272F";
|
||
# Splash elements are recolored from the palette so they read on any
|
||
# base, light or dark: foreground glyphs → text, field/track boxes →
|
||
# surface (raised from base in both polarities), the progress fill →
|
||
# accent. The shipped art is a fixed navy that vanished on dark themes.
|
||
text = colorOf "text" "#D3DAE0";
|
||
surface = colorOf "surface" "#303A46";
|
||
accent = colorOf "accent" "#B79BE8";
|
||
# The keyboard hint is a footnote, not a headline: subtext, like the rest of
|
||
# the palette's secondary text (#145).
|
||
subtext = colorOf "subtext" "#97A3B2";
|
||
magick = lib.getExe' pkgs.imagemagick "magick";
|
||
|
||
# What the passphrase prompt actually types with. `services.xserver.xkb.layout`
|
||
# is this distro's single source for the layout, bridged to the console (and
|
||
# so to the initrd prompt) by console.useXkbConfig — see modules/nixos/default.nix.
|
||
# A VT loads exactly ONE keymap, so a comma list ("us,gb") means the FIRST:
|
||
# printing the raw string would lie in precisely the multi-layout case the
|
||
# label exists for.
|
||
kbdLayout = lib.head
|
||
(lib.splitString "," (config.services.xserver.xkb.layout or "us"));
|
||
|
||
# Plymouth's Window.SetBackgroundTopColor takes three floats in
|
||
# 0.0–1.0; the .plymouth metadata's ConsoleLogBackgroundColor takes a
|
||
# 0xRRGGBB hex. Nix has no float math: multiply, integer-divide, pad.
|
||
byteToFloat = n:
|
||
let
|
||
thousandths = (n * 1000) / 255;
|
||
s = toString thousandths;
|
||
padded =
|
||
if lib.stringLength s == 1 then "00${s}"
|
||
else if lib.stringLength s == 2 then "0${s}"
|
||
else s;
|
||
in "0.${padded}";
|
||
channelOf = hex: off: byteToFloat (lib.fromHexString (lib.substring off 2 hex));
|
||
channel = channelOf base; # Window.SetBackground* (the splash base)
|
||
fgChannel = channelOf subtext; # Image.Text needs the same 0.0-1.0 floats
|
||
|
||
nomarchy-plymouth = pkgs.stdenv.mkDerivation {
|
||
pname = "nomarchy-plymouth";
|
||
version = "1.0";
|
||
|
||
src = ./plymouth;
|
||
|
||
nativeBuildInputs = [ pkgs.imagemagick ];
|
||
|
||
installPhase = ''
|
||
themedir=$out/share/plymouth/themes/nomarchy
|
||
mkdir -p "$themedir"
|
||
cp * "$themedir/"
|
||
|
||
# The keyboard glyph for the passphrase hint (#145) is plymouth's own —
|
||
# literally the icon Fedora shows, since its keymap widget loads this same
|
||
# asset. Copied at build time rather than vendored: the bytes stay in
|
||
# nixpkgs' (GPL) plymouth and never enter this repo, and it cannot drift
|
||
# from the plymouth we actually run.
|
||
# chmod: store files are read-only (444) and `recolor` rewrites in place.
|
||
# unpackPhase makes the *source* writable, which is why the art below
|
||
# needs no such thing — a file copied straight from the store does.
|
||
cp ${pkgs.plymouth}/share/plymouth/themes/spinner/keyboard.png "$themedir/keyboard.png"
|
||
chmod +w "$themedir/keyboard.png"
|
||
|
||
# Recolor the splash art from the palette (flat fill, alpha kept) so
|
||
# it reads on any base instead of the shipped fixed navy.
|
||
recolor() { ${magick} "$themedir/$1" -fill "#$2" -colorize 100 "$themedir/$1"; }
|
||
recolor logo.png ${text} # centre monogram
|
||
recolor lock.png ${text} # password-prompt padlock
|
||
recolor bullet.png ${text} # password dots
|
||
recolor entry.png ${surface} # password field box
|
||
recolor progress_box.png ${surface} # progress track
|
||
recolor progress_bar.png ${accent} # progress fill
|
||
recolor keyboard.png ${subtext} # passphrase keyboard-layout hint
|
||
|
||
# Point the .plymouth metadata into the store
|
||
sed -i "s|/usr/share/plymouth/themes/nomarchy|$themedir|g" \
|
||
"$themedir/nomarchy.plymouth"
|
||
|
||
# Bake the theme's base color: RGB floats for the script's
|
||
# Window.SetBackground* calls, 0xRRGGBB for ConsoleLogBackground.
|
||
sed -i \
|
||
-e 's|@BG_R@|${channel 0}|g' \
|
||
-e 's|@BG_G@|${channel 2}|g' \
|
||
-e 's|@BG_B@|${channel 4}|g' \
|
||
-e 's|@FG_R@|${fgChannel 0}|g' \
|
||
-e 's|@FG_G@|${fgChannel 2}|g' \
|
||
-e 's|@FG_B@|${fgChannel 4}|g' \
|
||
-e 's|@LAYOUT@|${kbdLayout}|g' \
|
||
"$themedir/nomarchy.script"
|
||
sed -i 's|@BG_HEX@|${base}|g' \
|
||
"$themedir/nomarchy.plymouth"
|
||
'';
|
||
};
|
||
in
|
||
{
|
||
config = lib.mkIf cfg.plymouth.enable {
|
||
# The systemd initrd (boot.initrd.systemd.enable) and console.earlySetup
|
||
# that Plymouth needs are now distro-wide defaults in ./default.nix
|
||
# (they also carry the keyboard layout to the LUKS prompt), so the
|
||
# splash no longer has to turn them on. Just keep the console quiet
|
||
# around the splash.
|
||
boot.initrd.verbose = lib.mkDefault false;
|
||
boot.consoleLogLevel = lib.mkDefault 0;
|
||
|
||
boot.plymouth = {
|
||
enable = lib.mkDefault true;
|
||
themePackages = lib.mkDefault [ nomarchy-plymouth ];
|
||
theme = lib.mkDefault "nomarchy";
|
||
};
|
||
|
||
# Not mkDefault: kernelParams is a list other modules add to at
|
||
# normal priority — a mkDefault def would be dropped entirely,
|
||
# losing the quiet/splash boot. These merge with the rest.
|
||
boot.kernelParams = [
|
||
"quiet"
|
||
"splash"
|
||
"loglevel=3"
|
||
"rd.systemd.show_status=false"
|
||
"rd.udev.log_level=3"
|
||
"udev.log_priority=3"
|
||
"boot.shell_on_fail"
|
||
];
|
||
};
|
||
}
|