Files
Nomarchy/modules/nixos/greeter.nix
Bernardo Magri d8e1a13d50
Some checks failed
Check / eval (push) Has been cancelled
refactor(#107): theme-state.json → state.json, theme-sync → state-sync
The machine flake's git-tracked settings file is system state, not
"theme" only — rename it to state.json. CLI becomes nomarchy-state-sync
with a nomarchy-theme-sync symlink for scripts and muscle memory.

Eval (mkFlake, doctor, lifecycle) still accepts theme-state.json; the
next write migrates to state.json and removes the legacy file.
Documented in MIGRATION.md; drop the CLI alias after release notes.
2026-07-15 11:26:59 +01:00

134 lines
5.7 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Greeter — greetd/tuigreet, themed from the same state.json that
# drives the desktop (nomarchy.system.stateFile; the Plymouth model:
# baked at SYSTEM rebuild, so it follows the theme as of the last
# sys-update, not the last instant apply).
#
# tuigreet draws on the virtual console with the 16 ANSI slots, so the
# theming is two-part:
# 1. console.colors — the VT palette becomes the theme's ansi[] hexes
# (which also themes raw ttys and the LUKS passphrase prompt: the
# same JSON reaches every pre-session surface).
# 2. --theme — tuigreet components on NAMED slots (its parser is
# ratatui Color::from_str; names map to the standard indexes, e.g.
# blue=4, gray=7, white=15, so the palette above hands them the
# theme's colors). ANSI "black" stays dark even in light themes —
# the greeter reads terminal-dark there, the same convention every
# terminal applies to ANSI colors.
#
# Auto-login is in-flake state like the rest (settings.greeter.autoLogin,
# written by System Auto-login via nomarchy-autologin below), NOT a baked
# line in system.nix: a hand-set `nomarchy.system.greeter.autoLogin` outranks
# the state default, which would leave the menu toggle flipping JSON that
# nothing reads. The installer therefore seeds the STATE on LUKS machines and
# the template keeps its example commented (templates/downstream/system.nix).
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.system;
distroName = config.system.nixos.distroName;
sync = lib.getExe pkgs.nomarchy-state-sync;
# Menu/CLI toggle, same shape as nomarchy-autotimezone: runs as the normal
# user (it owns the flake checkout + writes the state), sudos only the
# system switch. greetd's initial_session is baked at system rebuild, so
# there is nothing to apply live — the next boot is the observable change.
nomarchy-autologin = pkgs.writeShellScriptBin "nomarchy-autologin" ''
set -e
if [ "$(id -u)" -eq 0 ]; then
echo "nomarchy-autologin: run as your normal user (it sudos the rebuild itself)" >&2
exit 1
fi
flake="''${NOMARCHY_PATH:-$HOME/.nomarchy}"
cur=$(${sync} get settings.greeter.autoLogin 2>/dev/null) || cur=null
case "''${1:-toggle}" in
on) new="\"$USER\"" ;;
off) new=null ;;
toggle) case "$cur" in null|""|None) new="\"$USER\"" ;; *) new=null ;; esac ;;
status) echo "$cur"; exit 0 ;;
*) echo "usage: nomarchy-autologin [toggle|on|off|status]" >&2; exit 64 ;;
esac
${sync} --quiet set settings.greeter.autoLogin "$new" --no-switch
notify-send "Auto-login" "Rebuilding the system" 2>/dev/null || true
sudo nixos-rebuild switch --flake "$flake#default"
if [ "$new" = null ]; then
notify-send "Auto-login off" "The greeter asks who you are on the next boot." 2>/dev/null || true
else
notify-send "Auto-login on" "Next boot goes straight to the desktop." 2>/dev/null || true
fi
'';
# Fails closed with an actionable message via state-read.nix, like
# every other stateFile consumer — a raw fromJSON here would bury a bad
# state file under a Nix stack pointing at greeter.nix.
state =
if cfg.stateFile != null
then import ../state-read.nix { inherit lib; } cfg.stateFile
else { };
# The auto-login user from the state, or null. Read here via the state file
# — NOT `config.nomarchy.settings`, which exists only on the Home Manager
# side: on NixOS that attribute is missing, and `or null` swallows the
# error, so the old default silently evaluated to null on every machine.
stateAutoLogin =
let v = (state.settings or { }).greeter.autoLogin or null;
in if builtins.isString v && v != "" then v else null;
# A sparse/hand-rolled state without a proper ansi block just skips the
# theming (stock tuigreet grey) — never an eval error.
ansi = state.ansi or [ ];
themed = builtins.isList ansi && builtins.length ansi == 16;
tuigreetTheme = lib.concatStringsSep ";" [
"container=black" # ansi[0] — the theme's terminal background
"border=blue" # ansi[4] — the accent family in every shipped palette
"title=cyan"
"greet=cyan"
"prompt=green"
"input=white" # ansi[15] — bright foreground
"action=blue"
"button=yellow"
"time=cyan"
"text=gray" # ansi[7] — muted foreground
];
in
{
config = {
# Shipped unconditionally so the menu can turn auto-login back ON while
# it's off — the same reason nomarchy-autotimezone is unconditional.
environment.systemPackages = [ nomarchy-autologin ];
# Track the in-flake flag; mkDefault so a hand-set
# nomarchy.system.greeter.autoLogin in system.nix still wins (the
# autoTimezone pattern).
nomarchy.system.greeter.autoLogin = lib.mkDefault stateAutoLogin;
# VT palette from the theme (RRGGBB, no #; lands as vt.default_* kernel
# params). mkDefault so a downstream console.colors wins.
console.colors = lib.mkIf themed (lib.mkDefault (map (lib.removePrefix "#") ansi));
services.greetd = lib.mkIf cfg.greeter.enable {
enable = lib.mkDefault true;
settings = {
default_session = {
# start-hyprland is Hyprland 0.55's watchdog launcher; running
# the bare binary makes every session print a warning.
command = lib.mkDefault ("${pkgs.tuigreet}/bin/tuigreet --time --remember --greeting 'Welcome to ${distroName}'"
+ lib.optionalString themed " --theme '${tuigreetTheme}'"
+ " --cmd start-hyprland");
user = "greeter";
};
# Boot straight into the session once; logout → normal greeter.
initial_session = lib.mkIf (cfg.greeter.autoLogin != null) {
command = "start-hyprland";
user = cfg.greeter.autoLogin;
};
};
};
};
}