diff --git a/README.md b/README.md index 33cc332..ba0e045 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,11 @@ Defined in the host table in [`flake.nix`](./flake.nix): | Configuration | System | Machine | | ------------------- | --------------- | ---------------------------------------- | -| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) | -| `lyrathorpe-x1c` | `x86_64-linux` | ThinkPad X1 | -| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) | -| `lyrathorpe-mac` | `aarch64-darwin`| macOS (nix-darwin) | +| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) | +| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./system/machine/T400/README.md) | +| `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](./system/machine/MacPro31/README.md) | +| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) | +| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) | Shared layers: `lyrathorpe/home` (home-manager: shell, git, editor), `system/modules/common-nixos.nix` (all NixOS hosts), and @@ -27,6 +28,21 @@ sudo nixos-rebuild switch --flake .# darwin-rebuild switch --flake .#lyrathorpe-mac ``` +## Keybindings + +All Sway / tmux / foot / zsh keyboard shortcuts are documented in +[`lyrathorpe/home/KEYBINDINGS.md`](./lyrathorpe/home/KEYBINDINGS.md). + +## Login / greeter + +Graphical (Sway) hosts log in through a Wayland greeter — `greetd` running +ReGreet inside the `cage` kiosk compositor — configured centrally in +[`lyrathorpe/swaywm.nix`](./lyrathorpe/swaywm.nix), gated on +`features.swayDesktop.enable`. The greeter is forced to Dvorak to match the +console and Sway session. Hosts with `features.swayDesktop.enable = false` (the +WSL work box) keep plain TTY login. The target account needs a password +(`passwd `) before it can log in. + ## MacBook (Asahi) firmware The MBP host references `system/modules/firmware/` for Apple peripheral diff --git a/flake.lock b/flake.lock index 19b36f0..581ed2b 100644 --- a/flake.lock +++ b/flake.lock @@ -17,6 +17,28 @@ "type": "github" } }, + "firefox-addons": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "dir": "pkgs/firefox-addons", + "lastModified": 1780977789, + "narHash": "sha256-UFJfQlvInbsVaTK5XC2lafdqWlwiNP5LuQFYfDKq6Dc=", + "owner": "rycee", + "repo": "nur-expressions", + "rev": "0b627f105ea3baa2fa10308a6a67a8f8cbbb3e2a", + "type": "gitlab" + }, + "original": { + "dir": "pkgs/firefox-addons", + "owner": "rycee", + "repo": "nur-expressions", + "type": "gitlab" + } + }, "flake-compat": { "locked": { "lastModified": 1761640442, @@ -204,6 +226,7 @@ }, "root": { "inputs": { + "firefox-addons": "firefox-addons", "flake-parts": "flake-parts", "home-manager": "home-manager", "nix-darwin": "nix-darwin", diff --git a/flake.nix b/flake.nix index fe4d937..07ee130 100644 --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,11 @@ # Provides mkFlake: the systems/perSystem scaffolding used below. flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; + # Declarative Firefox add-ons (e.g. the Catppuccin theme); see lyrathorpe/user.nix. + firefox-addons = { + url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = @@ -192,12 +197,12 @@ ]; }; - lyrathorpe-x1c = { + lyrathorpe-t400 = { system = "x86_64-linux"; username = "lyrathorpe"; fullName = "Lyra Thorpe"; modules = [ - ./system/machine/X1/configuration.nix + ./system/machine/T400/configuration.nix ./system/modules/laptop.nix ./lyrathorpe/swaywm.nix ]; @@ -207,6 +212,22 @@ ]; }; + lyrathorpe-macpro31 = { + system = "x86_64-linux"; + username = "lyrathorpe"; + fullName = "Lyra Thorpe"; + portable = false; + modules = [ + ./system/machine/MacPro31/configuration.nix + ./system/modules/desktop.nix + ./lyrathorpe/swaywm.nix + ]; + homeModules = [ + ./lyrathorpe/home + ./lyrathorpe/home/desktop.nix + ]; + }; + emmathorpe-edaas = { system = "x86_64-linux"; username = "emmathorpe"; diff --git a/lyrathorpe/catppuccin-mocha.nix b/lyrathorpe/catppuccin-mocha.nix new file mode 100644 index 0000000..d621b95 --- /dev/null +++ b/lyrathorpe/catppuccin-mocha.nix @@ -0,0 +1,26 @@ +# Catppuccin Mocha palette. Raw 6-digit hex (no leading "#"); consumers add a +# "#" where their format needs it. Shared by the Sway desktop theming +# (home/sway.nix) and the ReGreet greeter (swaywm.nix) so the two stay in sync. +{ + base = "1e1e2e"; + mantle = "181825"; + crust = "11111b"; + surface0 = "313244"; + surface1 = "45475a"; + surface2 = "585b70"; + overlay0 = "6c7086"; + subtext0 = "a6adc8"; + subtext1 = "bac2de"; + text = "cdd6f4"; + rosewater = "f5e0dc"; + red = "f38ba8"; + maroon = "eba0ac"; + peach = "fab387"; + yellow = "f9e2af"; + green = "a6e3a1"; + teal = "94e2d5"; + sapphire = "74c7ec"; + blue = "89b4fa"; + mauve = "cba6f7"; + pink = "f5c2e7"; +} diff --git a/lyrathorpe/home/KEYBINDINGS.md b/lyrathorpe/home/KEYBINDINGS.md new file mode 100644 index 0000000..dc9a4cf --- /dev/null +++ b/lyrathorpe/home/KEYBINDINGS.md @@ -0,0 +1,172 @@ +# Keybindings reference + +Every keyboard shortcut configured across this desktop, and where it is defined. +Everything here is managed declaratively through Nix — edit the listed file and +rebuild, never the generated dotfiles. + +| Area | Defined in | +| --- | --- | +| Sway (compositor) | [`sway.nix`](./sway.nix) `config.keybindings` + `config.modes`, plus the home-manager Sway module's built-in defaults | +| tmux | [`shell.nix`](./shell.nix) `programs.tmux` | +| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` | +| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) | + +**Conventions** + +- **Super** is the `Mod4` / logo (Windows/Command) key; **Alt** is `Mod1`. +- Letter keys are **keysyms** (the character produced), not physical positions. + The keyboard is **Dvorak** (`us`/`dvorak`), so e.g. "Super+s" is whatever key + types `s` in Dvorak. +- Shortcuts apply to every Sway host (MBP, T400, Mac Pro); brightness keys are + laptop-only, as noted. + +--- + +## Sway + +### Applications & session + +| Shortcut | Action | +| --- | --- | +| `Super`+`Return` | Open a terminal (foot) | +| `Super`+`Space` | App launcher (sway-launcher-desktop in a floating foot) | +| `Super`+`d` | App launcher (same as above; module default) | +| `Super`+`e` | File manager (nemo) | +| `Super`+`c` | Clipboard history picker (clipman → fuzzel) | +| `Super`+`l` | Lock screen (swaylock) | +| `Super`+`Shift`+`q` | Close the focused window | +| `Super`+`Shift`+`c` | Reload the Sway config | +| `Super`+`Shift`+`e` | Exit Sway (asks for confirmation) | + +### Focus + +| Shortcut | Action | +| --- | --- | +| `Super`+`←`/`↓`/`↑`/`→` | Move focus by direction | +| `Super`+`h`/`j`/`k` | Move focus left / down / up (vim-style) | +| `Super`+`a` | Focus the parent container | +| `Super`+`Alt`+`Space` | Toggle focus between tiling and floating | + +> Note: vim focus-right would be `Super`+`l`, but that is bound to **lock** here; +> use `Super`+`→`. + +### Moving windows + +| Shortcut | Action | +| --- | --- | +| `Super`+`Shift`+`←`/`↓`/`↑`/`→` | Move the window by direction | +| `Super`+`Shift`+`h`/`j`/`k`/`l` | Move the window left / down / up / right | +| `Super`+`Shift`+`Space` | Toggle the window floating | + +Mouse (with `Super` held): left-drag moves a window, right-drag resizes it. + +### Layout + +| Shortcut | Action | +| --- | --- | +| `Super`+`b` | Split horizontally | +| `Super`+`v` | Split vertically | +| `Super`+`s` | Stacking layout | +| `Super`+`w` | Tabbed layout | +| `Super`+`f` | Toggle fullscreen | +| `Super`+`y` | **Layout submenu**: `s` stacking · `w` tabbed · `e` toggle split · `Return`/`Esc` exit | + +> The layout submenu's `e` (toggle split) is the home for that action since +> `Super`+`e` now opens the file manager. + +### Workspaces + +| Shortcut | Action | +| --- | --- | +| `Super`+`1`…`0` | Switch to workspace 1…10 | +| `Super`+`Shift`+`1`…`0` | Move the window to workspace 1…10 | +| `Super`+`z` | Previous workspace | +| `Super`+`x` | Next workspace | + +### Scratchpad + +| Shortcut | Action | +| --- | --- | +| `Super`+`Shift`+`-` | Move the window to the scratchpad | +| `Super`+`-` | Show / cycle the scratchpad | + +### Modes (submenus) + +| Shortcut | Action | +| --- | --- | +| `Super`+`r` | **Resize mode**: arrow keys resize; `Return`/`Esc` exit | +| `Super`+`y` | **Layout mode** (see Layout above) | +| `Super`+`Shift`+`x` | **Power menu**: `l` lock · `e` log out · `s` sleep · `r` reboot · `Shift`+`s` shutdown · `Return`/`Esc` exit | + +### Screenshots + +| Shortcut | Action | +| --- | --- | +| `Print` | Select a region → swappy (annotate/save) | +| `Shift`+`Print` | Focused window → swappy | + +### Audio & media + +| Shortcut | Action | +| --- | --- | +| `XF86AudioRaiseVolume` / `XF86AudioLowerVolume` | Volume ±5% (wpctl) | +| `XF86AudioMute` | Toggle output mute | +| `XF86AudioMicMute` | Toggle microphone mute | +| `XF86AudioPlay` | Play/pause (playerctl) | +| `XF86AudioNext` / `XF86AudioPrev` | Next / previous track | + +### Brightness — laptops only + +| Shortcut | Action | +| --- | --- | +| `XF86MonBrightnessUp` / `XF86MonBrightnessDown` | Backlight ±5% (brightnessctl) | + +Present only on portable hosts (T400, MBP); desktops have no internal backlight. + +--- + +## tmux + +Prefix is **`Ctrl`+`b`** (default). Copy mode uses **vi** keys. + +| Shortcut | Action | +| --- | --- | +| `Ctrl`+`b` then `v` | Split into left/right panes | +| `Ctrl`+`b` then `s` | Split into top/bottom panes | +| `Alt`+`←`/`→`/`↑`/`↓` | Switch pane by direction (no prefix needed) | +| `Ctrl`+`b` then `[` | Enter copy mode (then vi motions; `Space`/`Enter` to select/copy) | +| `Ctrl`+`b` then `z` | Zoom / unzoom the focused pane | +| `Ctrl`+`b` then `c` | New window | +| `Ctrl`+`b` then `n` / `p` | Next / previous window | +| `Ctrl`+`b` then `d` | Detach | +| Mouse | Enabled — click to focus, drag borders, scroll, select | + +> The stock split keys `%` and `"` are unbound; use `v` / `s` above. `Ctrl`+`b` +> then `s` is therefore a split, not the session tree. + +--- + +## foot (terminal) + +Only colours are themed; these are foot's default key bindings. + +| Shortcut | Action | +| --- | --- | +| `Ctrl`+`Shift`+`c` / `Ctrl`+`Shift`+`v` | Copy / paste (clipboard) | +| `Shift`+`Insert` | Paste primary selection | +| `Ctrl`+`Shift`+`r` | Search scrollback | +| `Ctrl`+`+` / `Ctrl`+`-` / `Ctrl`+`0` | Font larger / smaller / reset | +| `Ctrl`+`Shift`+`u` | URL mode (jump to/open links) | +| `Ctrl`+`Shift`+`n` | Spawn a new terminal | +| `Shift`+`PageUp` / `Shift`+`PageDown` | Scroll back / forward | + +--- + +## zsh + +| Shortcut | Action | +| --- | --- | +| `↑` / `↓` | History **substring** search — type a fragment first, then the arrows cycle matching past commands | + +Bound for both CSI and SS3 cursor sequences, so it works in foot, iTerm2 and +the Linux TTY alike. diff --git a/lyrathorpe/home/desktop.nix b/lyrathorpe/home/desktop.nix index f77ee2c..da6f757 100644 --- a/lyrathorpe/home/desktop.nix +++ b/lyrathorpe/home/desktop.nix @@ -1,7 +1,14 @@ -# Graphical desktop layer: GUI apps, Wayland session env, cursor theme, and the -# tty1 Sway autostart. Imported only on hosts that run Sway (MBP, X1); never -# pulled onto the headless WSL host. -{ pkgs, lib, ... }: +# Graphical desktop layer: GUI apps, Wayland session env, and cursor theme. +# Imported only on hosts that run Sway (MBP, T400, Mac Pro); never pulled onto +# the headless WSL host. Login (and the Sway session launch) is handled by the +# greetd/ReGreet greeter -- see ../swaywm.nix -- so there is no tty1 autostart. +{ + pkgs, + config, + inputs, + username, + ... +}: { imports = [ ./sway.nix @@ -10,6 +17,7 @@ home.packages = [ pkgs.element-desktop pkgs.legcord + pkgs.nemo # file manager (launched via Mod+e, see ./sway.nix) #pkgs.plex-desktop #pkgs.plexamp ]; @@ -19,6 +27,32 @@ XDG_CURRENT_DESKTOP = "sway"; }; + # Theme GTK apps (nemo, etc.) to match the Catppuccin Mocha desktop. Under + # Sway there is no XSettings daemon, so GTK reads these from the generated + # ~/.config/gtk-{3,4}.0/settings.ini directly. The Mocha theme is dark by + # design, so no separate prefer-dark hint is needed. + gtk = { + enable = true; + # Theme GTK4 apps too (for any added later). GTK4 ignores gtk-theme-name, + # but home-manager turns this into an `@import` of the theme's + # gtk-4.0/gtk.css into ~/.config/gtk-4.0/gtk.css -- which even libadwaita + # honours, since that file overrides the named colours it uses + # (window_bg_color, accent_bg_color, ...). Set explicitly (same value as the + # legacy default) so it also silences the stateVersion<26.05 warning. + gtk4.theme = config.gtk.theme; + theme = { + name = "catppuccin-mocha-blue-standard"; + package = pkgs.catppuccin-gtk.override { + accents = [ "blue" ]; + variant = "mocha"; + }; + }; + iconTheme = { + name = "Adwaita"; + package = pkgs.adwaita-icon-theme; + }; + }; + home.pointerCursor = { gtk.enable = true; x11 = { @@ -30,10 +64,30 @@ size = 24; }; - # Start Sway automatically on the first virtual terminal. - programs.zsh.initContent = lib.mkOrder 1500 '' - if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then - exec sway - fi - ''; + # Firefox is themed at the browser level (it does not follow the GTK theme). + # The system installs the binary (programs.firefox in ../user.nix); here + # home-manager owns only the profile, hence package = null. Apply the + # Catppuccin Mocha theme add-on (only the mauve accent is packaged upstream; + # the rest of the desktop uses blue) and make content + UI dark. + programs.firefox = { + enable = true; + package = null; + profiles.${username} = { + id = 0; + isDefault = true; + extensions = { + force = true; + packages = [ + inputs.firefox-addons.packages.${pkgs.system}.catppuccin-mocha-mauve + ]; + }; + settings = { + # Enable bundled add-ons automatically so the theme applies on first run. + "extensions.autoDisableScopes" = 0; + # Dark chrome + dark page content. + "ui.systemUsesDarkTheme" = 1; + "layout.css.prefers-color-scheme.content-override" = 0; + }; + }; + }; } diff --git a/lyrathorpe/home/shell.nix b/lyrathorpe/home/shell.nix index 2eb0679..f9ce679 100644 --- a/lyrathorpe/home/shell.nix +++ b/lyrathorpe/home/shell.nix @@ -6,7 +6,21 @@ enableCompletion = true; enableVteIntegration = true; autosuggestion.enable = true; - historySubstringSearch.enable = true; + # Bind Up/Down for history-substring-search in BOTH cursor-key modes: CSI + # (^[[A/^[[B -- normal mode, and what the Linux TTY sends) and SS3 + # (^[OA/^[OB -- application mode, used by foot, tmux and iTerm2). Binding + # only the default CSI form leaves it dead at the prompt in foot/iTerm2. + historySubstringSearch = { + enable = true; + searchUpKey = [ + "^[[A" + "^[OA" + ]; + searchDownKey = [ + "^[[B" + "^[OB" + ]; + }; history.append = true; oh-my-zsh = { enable = true; @@ -17,9 +31,7 @@ theme = "robbyrussell"; }; syntaxHighlighting.enable = true; - # Prefix the prompt with the hostname over SSH. The graphical autostart - # (exec sway on tty1) lives in ./desktop.nix so it never runs on headless - # hosts. + # Prefix the prompt with the hostname over SSH. initContent = lib.mkOrder 1500 '' if [ "$SSH_CLIENT" ] || [ "$SSH_TTY" ]; then export PS1="%M $PS1" @@ -36,14 +48,33 @@ terminal = "tmux-direct"; newSession = true; keyMode = "vi"; - historyLimit = 50000; + historyLimit = 500000; mouse = true; + # `reverseSplit = true` already binds s -> vertical and v -> horizontal + # split (the dotfiles' vim-style splits). extraConfig = '' + # Run a non-login shell in new panes/windows. + set -g default-command "''${SHELL}" + + # Drop the stock split keys in favour of the s/v binds above. + unbind % + unbind '"' + # Alt+Arrow pane navigation bind -n M-Left select-pane -L bind -n M-Right select-pane -R bind -n M-Up select-pane -U bind -n M-Down select-pane -D + + # Tell tmux which capabilities the foot terminal supports, so truecolor, + # synchronised output, the system clipboard (OSC 52), window titles and + # cursor styling all pass through. + set -as terminal-features ",foot*:RGB" + set -as terminal-features ",foot*:sync" + set -as terminal-features ",foot*:clipboard" + set -as terminal-features ",foot*:title" + set -as terminal-features ",foot*:ccolour" + set -as terminal-features ",foot*:cstyle" ''; }; } diff --git a/lyrathorpe/home/sway.nix b/lyrathorpe/home/sway.nix index 0d0d709..118a65f 100644 --- a/lyrathorpe/home/sway.nix +++ b/lyrathorpe/home/sway.nix @@ -14,6 +14,25 @@ portable ? true, ... }: +let + # Catppuccin Mocha (shared with the ReGreet greeter). Raw hex; prefix "#" + # where a consumer needs it -- Sway/i3status/dunst want "#", foot/swaylock do + # not. + ctp = import ../catppuccin-mocha.nix; + + # Focused-window screenshot -> swappy editor (the dotfiles' grimshot.sh logic). + # Full store paths so it needs nothing on PATH. + screenshotWindow = pkgs.writeShellScript "screenshot-window" '' + ${pkgs.grim}/bin/grim -g "$(${pkgs.sway}/bin/swaymsg -t get_tree \ + | ${pkgs.jq}/bin/jq -r '.. | select(.focused?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"')" \ + - | ${pkgs.swappy}/bin/swappy -f - + ''; + + # Binding-mode names. The string is both the `modes` attr key and what the + # bar's mode indicator shows, so the keys are spelled out in the label. + layoutMode = "layout: [s]tacking [w]tabbed [e]split"; + systemMode = "system: [l]ock [e]xit [s]leep [r]eboot [Shift+s]shutdown"; +in { wayland.windowManager.sway = { enable = true; @@ -26,7 +45,54 @@ # Launcher: sway-launcher-desktop running inside a floating foot window. menu = "${pkgs.foot}/bin/foot --app-id=launcher ${pkgs.sway-launcher-desktop}/bin/sway-launcher-desktop"; - input."type:keyboard".xkb_layout = "dvorak"; + # Dvorak is a variant of the "us" layout, not a standalone layout -- + # `xkb_layout = "dvorak"` fails to compile (no symbols/dvorak) and wlroots + # silently falls back to QWERTY. Use the variant. + input."type:keyboard" = { + xkb_layout = "us"; + xkb_variant = "dvorak"; + }; + # Touchpads (laptops): tap-to-click and natural scrolling. Inert on the + # desktop hosts, which have no touchpad. + input."type:touchpad" = { + tap = "enabled"; + natural_scroll = "enabled"; + }; + + # Solid Catppuccin Mocha base as the wallpaper (no image dependency). + output."*".bg = "#${ctp.base} solid_color"; + + # Window borders -- Catppuccin Mocha (blue accent on the focused window). + colors = { + focused = { + border = "#${ctp.blue}"; + background = "#${ctp.base}"; + text = "#${ctp.text}"; + indicator = "#${ctp.blue}"; + childBorder = "#${ctp.blue}"; + }; + focusedInactive = { + border = "#${ctp.surface0}"; + background = "#${ctp.base}"; + text = "#${ctp.subtext0}"; + indicator = "#${ctp.surface0}"; + childBorder = "#${ctp.surface0}"; + }; + unfocused = { + border = "#${ctp.surface0}"; + background = "#${ctp.base}"; + text = "#${ctp.subtext0}"; + indicator = "#${ctp.surface0}"; + childBorder = "#${ctp.surface0}"; + }; + urgent = { + border = "#${ctp.red}"; + background = "#${ctp.base}"; + text = "#${ctp.text}"; + indicator = "#${ctp.red}"; + childBorder = "#${ctp.red}"; + }; + }; window.commands = [ { @@ -35,6 +101,33 @@ } ]; + # Binding modes (submenus). Entered from keybindings below; each action + # returns to the default mode. mkOptionDefault-merged with the module's + # built-in "resize" mode. + modes = { + # Layout submenu (Mod+y). Mirrors Sway's default s/w/e layout keys -- + # notably it restores split-toggle, which moved off Mod+e when that + # became the nemo launcher. + ${layoutMode} = { + "s" = "layout stacking, mode default"; + "w" = "layout tabbed, mode default"; + "e" = "layout toggle split, mode default"; + "Return" = "mode default"; + "Escape" = "mode default"; + }; + # Power menu (Mod+Shift+x). Lock reuses the themed swaylock; the rest go + # through systemd/logind (allowed for the active local session). + ${systemMode} = { + "l" = "exec ${pkgs.swaylock}/bin/swaylock -f, mode default"; + "e" = "exec ${pkgs.sway}/bin/swaymsg exit, mode default"; + "s" = "exec systemctl suspend, mode default"; + "r" = "exec systemctl reboot, mode default"; + "Shift+s" = "exec systemctl poweroff, mode default"; + "Return" = "mode default"; + "Escape" = "mode default"; + }; + }; + bars = [ { position = "top"; @@ -46,16 +139,83 @@ ]; size = 11.0; }; + # Bar background + workspace buttons -- Catppuccin Mocha. The mantle + # background matches i3status-rust's idle_bg below for a uniform strip. + colors = { + background = "#${ctp.mantle}"; + statusline = "#${ctp.text}"; + separator = "#${ctp.surface0}"; + focusedWorkspace = { + border = "#${ctp.blue}"; + background = "#${ctp.blue}"; + text = "#${ctp.base}"; + }; + activeWorkspace = { + border = "#${ctp.surface0}"; + background = "#${ctp.surface0}"; + text = "#${ctp.text}"; + }; + inactiveWorkspace = { + border = "#${ctp.mantle}"; + background = "#${ctp.mantle}"; + text = "#${ctp.subtext0}"; + }; + urgentWorkspace = { + border = "#${ctp.red}"; + background = "#${ctp.red}"; + text = "#${ctp.base}"; + }; + }; } ]; + # NB: this whole set is wrapped in mkOptionDefault so it MERGES with the + # home-manager module's default keybindings (same priority) rather than + # replacing them. Do not wrap it in mkMerge with a normal-priority attr -- + # that makes the normal-priority def win and silently drops every default + # bind (terminal, movement, workspaces, ...). keybindings = lib.mkOptionDefault ( { + # Launcher on Mod+Space. mkForce overrides the module's own default + # Mod+Space (focus mode_toggle); a plain value would conflict with it + # at equal priority. Mod+d also still runs the launcher (module default). + "${modifier}+space" = lib.mkForce "exec ${menu}"; + # File manager. mkForce overrides the module default (layout toggle split). + "${modifier}+e" = lib.mkForce "exec ${pkgs.nemo}/bin/nemo"; "${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f"; - "Print" = "exec ${pkgs.grim}/bin/grim ~/screenshot-$(date +%F-%H%M%S).png"; + + # Cycle workspaces. + "${modifier}+z" = "workspace prev"; + "${modifier}+x" = "workspace next"; + + # focus mode_toggle (tiling <-> floating focus) -- re-homed off + # Mod+Space, which is now the launcher. + "${modifier}+Mod1+space" = "focus mode_toggle"; + + # Enter the binding-mode submenus defined above. + "${modifier}+y" = "mode \"${layoutMode}\""; + "${modifier}+Shift+x" = "mode \"${systemMode}\""; + + # Clipboard history: pick a past entry through fuzzel (clipman stores + # it -- see services.clipman below). + "${modifier}+c" = + "exec ${pkgs.clipman}/bin/clipman pick -t CUSTOM --tool-args=\"${pkgs.fuzzel}/bin/fuzzel --dmenu\""; + + # Screenshots -> swappy editor: Print = drag a region, Shift+Print = + # the focused window. + "Print" = + "exec ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" - | ${pkgs.swappy}/bin/swappy -f -"; + "Shift+Print" = "exec ${screenshotWindow}"; + "XF86AudioRaiseVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"; "XF86AudioLowerVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"; "XF86AudioMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; + "XF86AudioMicMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"; + + # Media keys (MPRIS via playerctl). + "XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause"; + "XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next"; + "XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous"; } # Screen backlight: laptops only (no internal backlight on a desktop). // lib.optionalAttrs portable { @@ -66,13 +226,114 @@ }; }; - programs.swaylock = { + # Terminal: Catppuccin Mocha. foot reads ~/.config/foot/foot.ini; the Sway + # `terminal` above still launches the same binary, now themed. + programs.foot = { + enable = true; + # foot 1.27: the bare [colors] section is deprecated in favour of + # [colors-dark] (the default theme), and the cursor colour moved out of + # [cursor] (where `color` is now rejected) into a `cursor` key here, written + # " " (man foot.ini(5): "ff0000 00ff00" => green cursor, red + # text). Only colors-dark is needed; we never set initial-color-theme=light. + settings = { + main = { + # Advertise as xterm-256color so remote hosts without foot's terminfo + # still behave (tmux re-adds foot's RGB/sync/etc. features -- see + # shell.nix). The [main] section is the unheadered top of foot.ini. + term = "xterm-256color"; + }; + scrollback.lines = 100000; + "colors-dark" = { + background = ctp.base; + foreground = ctp.text; + regular0 = ctp.surface1; + regular1 = ctp.red; + regular2 = ctp.green; + regular3 = ctp.yellow; + regular4 = ctp.blue; + regular5 = ctp.pink; + regular6 = ctp.teal; + regular7 = ctp.subtext1; + bright0 = ctp.surface2; + bright1 = ctp.red; + bright2 = ctp.green; + bright3 = ctp.yellow; + bright4 = ctp.blue; + bright5 = ctp.pink; + bright6 = ctp.teal; + bright7 = ctp.subtext0; + "selection-foreground" = ctp.base; + "selection-background" = ctp.rosewater; + cursor = "${ctp.base} ${ctp.rosewater}"; + }; + }; + }; + + # Clipboard history: a user service runs `wl-paste --watch clipman store`, + # bound to the Wayland session, so copies persist and Mod+c (above) can pick + # an old entry through fuzzel. + services.clipman.enable = true; + + # fuzzel: the dmenu picker used by clipman, themed Catppuccin Mocha to match + # (fuzzel colours are RRGGBBAA -- 8 hex digits). + programs.fuzzel = { enable = true; settings = { - color = "1e1e2e"; + main = { + font = "Noto Sans:size=12"; + prompt = "\"clipboard \""; + }; + border = { + width = 2; + radius = 8; + }; + colors = { + background = "${ctp.base}f0"; + text = "${ctp.text}ff"; + prompt = "${ctp.subtext0}ff"; + input = "${ctp.text}ff"; + match = "${ctp.blue}ff"; + selection = "${ctp.surface1}ff"; + selection-text = "${ctp.text}ff"; + selection-match = "${ctp.blue}ff"; + border = "${ctp.blue}ff"; + }; + }; + }; + + programs.swaylock = { + enable = true; + # Catppuccin Mocha (swaylock colours are hex without "#"). + settings = { + color = ctp.base; indicator-radius = 100; indicator-thickness = 7; show-failed-attempts = true; + font = "Noto Sans"; + + inside-color = ctp.base; + inside-clear-color = ctp.base; + inside-ver-color = ctp.base; + inside-wrong-color = ctp.base; + + ring-color = ctp.surface1; + ring-clear-color = ctp.yellow; + ring-ver-color = ctp.blue; + ring-wrong-color = ctp.red; + + key-hl-color = ctp.blue; + bs-hl-color = ctp.red; + + line-color = ctp.base; + line-clear-color = ctp.base; + line-ver-color = ctp.base; + line-wrong-color = ctp.base; + separator-color = "00000000"; + + text-color = ctp.text; + text-clear-color = ctp.text; + text-ver-color = ctp.text; + text-wrong-color = ctp.text; }; }; @@ -98,16 +359,30 @@ services.dunst = { enable = true; + # Catppuccin Mocha notifications (dunst colours need a leading "#"). settings = { global = { font = "Noto Sans 11"; - frame_color = "#89b4fa"; + frame_color = "#${ctp.blue}"; + frame_width = 2; separator_color = "frame"; offset = "10x10"; corner_radius = 5; }; + urgency_low = { + background = "#${ctp.base}"; + foreground = "#${ctp.text}"; + frame_color = "#${ctp.surface1}"; + }; + urgency_normal = { + background = "#${ctp.base}"; + foreground = "#${ctp.text}"; + frame_color = "#${ctp.blue}"; + }; urgency_critical = { - frame_color = "#fab387"; + background = "#${ctp.base}"; + foreground = "#${ctp.text}"; + frame_color = "#${ctp.peach}"; timeout = 0; }; }; @@ -116,8 +391,29 @@ programs.i3status-rust = { enable = true; bars.default = { - theme = "gruvbox-dark"; + # Catppuccin Mocha: a flat "plain" base recoloured via overrides. Idle + # blocks sit on mantle (matching the Sway bar background) with light text; + # only warning/critical states get a loud tinted background. The `theme` + # bar option is shallow-merged away by `settings.theme`, so set the base + # theme and its overrides together here. icons = "awesome6"; + settings.theme = { + theme = "plain"; + overrides = { + idle_bg = "#${ctp.mantle}"; + idle_fg = "#${ctp.text}"; + info_bg = "#${ctp.mantle}"; + info_fg = "#${ctp.blue}"; + good_bg = "#${ctp.mantle}"; + good_fg = "#${ctp.green}"; + warning_bg = "#${ctp.peach}"; + warning_fg = "#${ctp.base}"; + critical_bg = "#${ctp.red}"; + critical_fg = "#${ctp.base}"; + separator_bg = "#${ctp.mantle}"; + separator_fg = "#${ctp.surface1}"; + }; + }; blocks = [ { block = "disk_space"; diff --git a/lyrathorpe/swaywm.nix b/lyrathorpe/swaywm.nix index eb14ab9..9c5551b 100644 --- a/lyrathorpe/swaywm.nix +++ b/lyrathorpe/swaywm.nix @@ -7,6 +7,8 @@ let cfg = config.features.swayDesktop; + # Catppuccin Mocha (shared with the Sway desktop, see lyrathorpe/home/sway.nix). + ctp = import ./catppuccin-mocha.nix; in { options = { @@ -35,6 +37,10 @@ in brightnessctl foot grim + slurp # region selection for screenshots + swappy # screenshot annotation/save + jq # used by the focused-window screenshot bind + playerctl # MPRIS media keys sway-launcher-desktop pavucontrol ]; @@ -45,6 +51,104 @@ in font-awesome ]; + # Wayland login screen (replaces console/getty login on every Sway host). + # greetd runs ReGreet inside the cage kiosk compositor; the Sway session is + # offered automatically because programs.sway registers itself via + # services.displayManager.sessionPackages. Hosts that turn off + # features.swayDesktop (e.g. EDaaS) keep plain TTY login. + programs.regreet.enable = true; + # Theme the greeter to match the Sway desktop (Catppuccin Mocha). ReGreet is + # GTK; recolour via CSS (covering both libadwaita named colours and plain + # GTK node selectors) and use the same Noto Sans as the bar/notifications. + programs.regreet.font = { + name = "Noto Sans"; + package = pkgs.noto-fonts; + size = 16; + }; + programs.regreet.extraCss = '' + /* GTK4 Adwaita legacy names (what plain GTK4 actually references). */ + @define-color theme_bg_color #${ctp.base}; + @define-color theme_fg_color #${ctp.text}; + @define-color theme_base_color #${ctp.mantle}; + @define-color theme_text_color #${ctp.text}; + @define-color theme_selected_bg_color #${ctp.blue}; + @define-color theme_selected_fg_color #${ctp.base}; + @define-color insensitive_bg_color #${ctp.mantle}; + @define-color insensitive_fg_color #${ctp.overlay0}; + @define-color borders #${ctp.surface1}; + @define-color warning_color #${ctp.peach}; + @define-color error_color #${ctp.red}; + @define-color success_color #${ctp.green}; + + /* libadwaita names (inert on plain GTK4, kept for forward-compat). */ + @define-color window_bg_color #${ctp.base}; + @define-color window_fg_color #${ctp.text}; + @define-color view_bg_color #${ctp.mantle}; + @define-color view_fg_color #${ctp.text}; + @define-color card_bg_color #${ctp.surface0}; + @define-color card_fg_color #${ctp.text}; + @define-color accent_bg_color #${ctp.blue}; + @define-color accent_fg_color #${ctp.base}; + @define-color accent_color #${ctp.blue}; + @define-color destructive_bg_color #${ctp.red}; + @define-color destructive_fg_color #${ctp.base}; + + window { + background-color: #${ctp.base}; + color: #${ctp.text}; + } + + label { + color: #${ctp.text}; + } + + entry { + background-color: #${ctp.surface0}; + color: #${ctp.text}; + border: 1px solid #${ctp.surface1}; + } + + entry:focus-within { + border-color: #${ctp.blue}; + } + + button, + combobox button { + background-color: #${ctp.surface0}; + color: #${ctp.text}; + border: 1px solid #${ctp.surface1}; + } + + button:hover { + background-color: #${ctp.surface1}; + } + + button:active, + button:checked { + background-color: #${ctp.blue}; + color: #${ctp.base}; + } + ''; + # cage reads the XKB_* environment at startup, so force the greeter onto the + # same Dvorak layout as the Sway session (home/sway.nix) -- otherwise the + # password field would be QWERTY. Dvorak is the "us" layout's variant, NOT a + # layout of its own: "dvorak" alone has no symbols/ file, so the keymap + # fails to compile and the greeter ends up with no keyboard at all (the + # greeter has no fallback, unlike a running Sway session). This overrides the + # greetd command regreet sets with mkDefault. + services.greetd.settings.default_session.command = + let + greeter = pkgs.writeShellScript "regreet-cage" '' + export XKB_DEFAULT_LAYOUT=us + export XKB_DEFAULT_VARIANT=dvorak + # ReGreet is plain GTK4 (no libadwaita); force the dark Adwaita variant + # so the extraCss accents sit on a dark base instead of light Adwaita. + export GTK_THEME=Adwaita:dark + exec ${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} -s -- ${lib.getExe config.programs.regreet.package} + ''; + in + "${greeter}"; + # Desktop portals: enables screen sharing (wlroots) and native file pickers # for Wayland apps such as Element and Firefox. xdg.portal = { diff --git a/system/machine/MBP-Asahi/configuration.nix b/system/machine/MBP-Asahi/configuration.nix index 07bbb62..24dad33 100644 --- a/system/machine/MBP-Asahi/configuration.nix +++ b/system/machine/MBP-Asahi/configuration.nix @@ -7,7 +7,9 @@ ./hardware-configuration.nix ]; - # Asahi manages the EFI vars from macOS; do not touch them from NixOS. + # UEFI boot via systemd-boot. Asahi manages the EFI vars from macOS, so do not + # touch them from NixOS. + boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = false; networking.hostName = "Emma-Asahi"; diff --git a/system/machine/MacPro31/README.md b/system/machine/MacPro31/README.md new file mode 100644 index 0000000..e459b1e --- /dev/null +++ b/system/machine/MacPro31/README.md @@ -0,0 +1,60 @@ +# Mac Pro 3,1 (Early 2008) — install notes + +Flake host: `lyrathorpe-macpro31`. Desktop (`portable = false`, imports +`../../modules/desktop.nix`). Files: `configuration.nix`, +`hardware-configuration.nix`. + +## Hardware configuration + +`hardware-configuration.nix` here is a hand-written **placeholder**. On the real +machine, run `nixos-generate-config`, replace the file, and commit it. It assumes +by-label partitions — ESP `ESP` (vfat, mounted at `/boot`), root `nixos` (ext4), +and `swap` — so either label them at install time or swap in the generated UUIDs. + +## Bootloader + +The Mac Pro 3,1 has **64-bit EFI**, so it uses **systemd-boot** (no GRUB/CSM +shim). `canTouchEfiVariables = false` because Apple's firmware does not reliably +accept `efibootmgr` NVRAM writes. + +Apple-EFI quirk: if the firmware boot picker does not show NixOS after install, +either + +- uncomment `boot.loader.efi.efiInstallAsRemovable = true;` in + `configuration.nix` (installs the fallback `\EFI\BOOT\BOOTX64.EFI`), and/or +- "bless" the ESP from macOS. + +Partition the disk GPT with an ESP (vfat). + +## Graphics + +The stock card varies between units — **ATI Radeon HD 2600 XT** or **NVIDIA +GeForce 8800 GT**. No proprietary driver is hardcoded; Sway relies on in-tree KMS: + +- ATI Radeon HD 2600 XT → `radeon` (or `amdgpu`) KMS +- NVIDIA GeForce 8800 GT → `nouveau` KMS + +These come up automatically. If a card needs forcing, set +`services.xserver.videoDrivers` and/or add the module to +`boot.initrd.kernelModules` for early KMS (see the comment in +`configuration.nix`). + +## Networking + +Wired Ethernet via NetworkManager (from `desktop.nix`) — the Mac Pro has two +gigabit ports. + +## Login + +Graphical login via a Wayland greeter — `greetd` running ReGreet inside the +`cage` kiosk compositor — configured centrally in `lyrathorpe/swaywm.nix` for +every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced +to the Dvorak layout to match the console and Sway session. Set the user +password (`passwd lyrathorpe`) after install, or the greeter cannot +authenticate. Requires working KMS (radeon/nouveau — see Graphics). + +## Apply + +```sh +sudo nixos-rebuild switch --flake .#lyrathorpe-macpro31 +``` diff --git a/system/machine/MacPro31/configuration.nix b/system/machine/MacPro31/configuration.nix new file mode 100644 index 0000000..1d41ad5 --- /dev/null +++ b/system/machine/MacPro31/configuration.nix @@ -0,0 +1,58 @@ +# Apple Mac Pro 3,1 (Early 2008, dual Xeon Harpertown, x86_64). Desktop host: +# shared graphical/wired options live in ../../modules/desktop.nix; only +# host-specific settings are here. Install notes (EFI booting, GPU, partitions): +# see ./README.md. +{ ... }: + +{ + imports = [ + ./hardware-configuration.nix + ]; + + # The Mac Pro 3,1 has 64-bit EFI (confirmed by the owner), so boot via + # systemd-boot like the MBP -- no GRUB/BIOS shim needed. + boot.loader.systemd-boot.enable = true; + # Apple's EFI does not reliably support efibootmgr NVRAM writes; leave the + # firmware vars untouched. + boot.loader.efi.canTouchEfiVariables = false; + # Apple-EFI quirk: if the Mac does not pick up the bootloader at the boot + # picker, install it to the fallback path \EFI\BOOT\BOOTX64.EFI and/or + # "bless" the ESP from macOS. Uncomment to write the removable fallback path: + # boot.loader.efi.efiInstallAsRemovable = true; + + networking.hostName = "MacPro31-NixOS"; + + # This host accepts SSH, so open 22 (the firewall itself is enabled in + # workstation.nix with a default-deny policy). + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 ]; + + services.pipewire = { + enable = true; + pulse.enable = true; + }; + + # No fingerprint hardware; empty service still lets swaylock authenticate via + # password. + security.pam.services.swaylock = { }; + + # Dual Harpertown Xeon microcode + redistributable firmware (e.g. GPU/NIC + # blobs). + hardware.cpu.intel.updateMicrocode = true; + hardware.enableRedistributableFirmware = true; + + # GPU note: the stock card varies between units -- ATI Radeon HD 2600 XT or + # NVIDIA GeForce 8800 GT. Sway needs a working KMS/modesetting driver; do NOT + # install a proprietary blob here. Depending on the installed card, rely on + # the open kernel driver: + # - ATI Radeon HD 2600 XT -> "radeon" (older) or "amdgpu" KMS + # - NVIDIA GeForce 8800 GT -> "nouveau" KMS + # These come up automatically via the in-tree drivers + KMS, and the graphics + # stack itself is enabled by swaywm.nix. If a card needs to be forced, add it + # here, e.g. `services.xserver.videoDrivers = [ "radeon" ];` (or "nouveau"), + # and/or `boot.initrd.kernelModules = [ "radeon" ];` in + # hardware-configuration.nix for early KMS. + + # See `man configuration.nix` / the stateVersion docs before changing. + system.stateVersion = "26.05"; +} diff --git a/system/machine/MacPro31/hardware-configuration.nix b/system/machine/MacPro31/hardware-configuration.nix new file mode 100644 index 0000000..120d1b1 --- /dev/null +++ b/system/machine/MacPro31/hardware-configuration.nix @@ -0,0 +1,33 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "ahci" "firewire_ohci" "usb_storage" "usbhid" "sd_mod" ]; + boot.initrd.kernelModules = [ "dm-snapshot" ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/mapper/MacPro-Root"; + fsType = "ext4"; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/0E9C-C099"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = + [ { device = "/dev/disk/by-uuid/dec138b4-320f-4b69-acbc-3014a1032cbd"; } + ]; + networking.useDHCP = lib.mkDefault true; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/system/machine/T400/README.md b/system/machine/T400/README.md new file mode 100644 index 0000000..6f2d1d7 --- /dev/null +++ b/system/machine/T400/README.md @@ -0,0 +1,48 @@ +# ThinkPad T400 — install notes + +Flake host: `lyrathorpe-t400`. Files: `configuration.nix`, the `boot-*.nix` +variants, and `hardware-configuration.nix`. + +## Hardware configuration + +`hardware-configuration.nix` here is a hand-written **placeholder**. On the real +machine, run `nixos-generate-config`, replace the file, and commit it. It assumes +by-label partitions — root `nixos` (ext4) and `swap` — so either label them at +install time or swap in the generated UUIDs. + +## Bootloader — import the module matching the flashed firmware + +`configuration.nix` imports exactly one boot module. Default is `boot-bios.nix`; +switch by commenting it out and uncommenting the relevant alternative. + +| Firmware | Module | Notes | +| --- | --- | --- | +| Stock Lenovo BIOS, or coreboot + **SeaBIOS** payload | `boot-bios.nix` | GRUB on the MBR. Set `device` to the real install disk (`/dev/sda` by default). MBR/legacy layout. | +| coreboot + **GRUB** payload | `boot-coreboot-grub.nix` | GRUB is config-only (`device = "nodev"`); NixOS does **not** write to a disk. Your coreboot `grub.cfg` (in the flash chip) must `search` for and `configfile` the on-disk `/boot/grub/grub.cfg`, or chainload the disk's GRUB. | +| coreboot + **Tianocore/edk2 (UEFI)** payload | `boot-coreboot-uefi.nix` | systemd-boot. `canTouchEfiVariables = true` (coreboot honours NVRAM writes). The module **declares its own ESP** (`/boot` vfat, label `ESP`) — when you regenerate `hardware-configuration.nix`, do **not** let it also define `/boot`. Create + label an `ESP` vfat partition (GPT). | + +## Graphics + +This unit has the optional **discrete ATI Mobility Radeon HD 3470 (RV620)**. The +open `radeon` KMS driver is loaded in the initrd for early modesetting; firmware +comes from `enableRedistributableFirmware`. + +The T400 has switchable graphics (discrete ATI + Intel GMA 4500MHD). Select +**Discrete** in the firmware's graphics setting so only the ATI is live. If you +run **Integrated** instead, the Intel `i915` driver takes over with no config +change and `radeon` stays idle. + +## Login + +Graphical login via a Wayland greeter — `greetd` running ReGreet inside the +`cage` kiosk compositor — configured centrally in `lyrathorpe/swaywm.nix` for +every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced +to the Dvorak layout to match the console and Sway session. Set the user +password (`passwd lyrathorpe`) after install, or the greeter cannot +authenticate. Requires working radeon/i915 KMS (see Graphics). + +## Apply + +```sh +sudo nixos-rebuild switch --flake .#lyrathorpe-t400 +``` diff --git a/system/machine/T400/boot-bios.nix b/system/machine/T400/boot-bios.nix new file mode 100644 index 0000000..01fcc8c --- /dev/null +++ b/system/machine/T400/boot-bios.nix @@ -0,0 +1,11 @@ +# Boot via legacy BIOS -- the stock Lenovo BIOS, or coreboot with the SeaBIOS +# payload (both present a legacy BIOS interface). GRUB is installed to the MBR of +# the boot disk. This is the default. +{ ... }: +{ + boot.loader.grub = { + enable = true; + # Must point at the actual install disk -- adjust if it is not /dev/sda. + device = "/dev/sda"; + }; +} diff --git a/system/machine/T400/boot-coreboot-grub.nix b/system/machine/T400/boot-coreboot-grub.nix new file mode 100644 index 0000000..ba5a6ab --- /dev/null +++ b/system/machine/T400/boot-coreboot-grub.nix @@ -0,0 +1,13 @@ +# Boot via coreboot's GRUB payload (e.g. libreboot default). The GRUB in the +# flash chip reads the grub.cfg that NixOS generates on disk, so GRUB here is +# config-only -- it is NOT installed to any disk MBR (`device = "nodev"`). +# +# Your coreboot grub.cfg must locate and load the on-disk config, e.g. search +# for and `configfile` /boot/grub/grub.cfg (or chainload the disk's GRUB). +{ ... }: +{ + boot.loader.grub = { + enable = true; + device = "nodev"; + }; +} diff --git a/system/machine/T400/boot-coreboot-uefi.nix b/system/machine/T400/boot-coreboot-uefi.nix new file mode 100644 index 0000000..2c65608 --- /dev/null +++ b/system/machine/T400/boot-coreboot-uefi.nix @@ -0,0 +1,17 @@ +# Boot via coreboot's Tianocore/edk2 (UEFI) payload. This turns the T400 into a +# real UEFI machine, so use systemd-boot. Unlike Apple's firmware, coreboot's +# UEFI honours EFI variable writes, so canTouchEfiVariables is on. +# +# Requires an EFI System Partition. It is declared here so it travels with this +# boot mode; the generated hardware-configuration.nix should NOT also define +# /boot. Label the ESP `ESP` at install, or replace with the generated UUID. +{ ... }: +{ + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + fileSystems."/boot" = { + device = "/dev/disk/by-label/ESP"; + fsType = "vfat"; + }; +} diff --git a/system/machine/T400/configuration.nix b/system/machine/T400/configuration.nix new file mode 100644 index 0000000..2ed5d13 --- /dev/null +++ b/system/machine/T400/configuration.nix @@ -0,0 +1,54 @@ +# ThinkPad T400 (NixOS). Shared laptop options live in ../../modules/laptop.nix; +# only host-specific settings are here. Install notes (boot variants, GPU, +# partitions): see ./README.md. +{ ... }: + +{ + imports = [ + ./hardware-configuration.nix + # Boot: import exactly ONE, matching the firmware currently flashed. + # Stock Lenovo BIOS and coreboot+SeaBIOS both use boot-bios.nix. + ./boot-bios.nix + # ./boot-coreboot-grub.nix # coreboot with the GRUB payload (config-only GRUB) + # ./boot-coreboot-uefi.nix # coreboot with the Tianocore/edk2 UEFI payload + # # (systemd-boot; carries its own ESP mount) + ]; + + networking.hostName = "T400-NixOS"; + + console.font = "Lat2-Terminus16"; + + services.pipewire = { + enable = true; + pulse.enable = true; + }; + + # This host accepts SSH, so open 22 (the firewall itself is enabled in + # laptop.nix with a default-deny policy). + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 ]; + + # The T400's fingerprint reader differs/may be absent; empty service still + # lets swaylock authenticate via password. + security.pam.services.swaylock = { }; + + # Intel Core 2 (Penryn) microcode + redistributable firmware. The latter also + # supplies the iwlwifi blobs (Intel WiFi Link 5100/5300) and the radeon + # firmware needed by the discrete GPU below. + hardware.cpu.intel.updateMicrocode = true; + hardware.enableRedistributableFirmware = true; + + # This T400 has the optional discrete GPU fitted: an ATI Mobility Radeon HD + # 3470 (RV620), driven by the open `radeon` KMS driver. Load it in the initrd + # for early modesetting (clean Sway/Wayland start); firmware comes from + # enableRedistributableFirmware above. + # + # The T400 has switchable graphics (this discrete GPU + the Intel GMA + # 4500MHD). Select "Discrete" in the firmware's graphics setting so only the + # ATI is live; if you instead run "Integrated", the Intel i915 driver takes + # over with no extra config and `radeon` simply stays idle. + boot.initrd.kernelModules = [ "radeon" ]; + + # See `man configuration.nix` / the stateVersion docs before changing. + system.stateVersion = "26.05"; +} diff --git a/system/machine/T400/hardware-configuration.nix b/system/machine/T400/hardware-configuration.nix new file mode 100644 index 0000000..a68a562 --- /dev/null +++ b/system/machine/T400/hardware-configuration.nix @@ -0,0 +1,44 @@ +# PLACEHOLDER -- hand-written, not machine-generated. Regenerate on the real +# T400 with `nixos-generate-config` and commit the result. The device labels +# below are guesses; replace them with the generated UUIDs (or label the +# partitions accordingly at install time). +{ + config, + lib, + modulesPath, + ... +}: + +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ + "ahci" + "ata_piix" + "ehci_pci" + "uhci_hcd" + "usb_storage" + "sd_mod" + "sr_mod" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + # Label your root partition `nixos` at install, or replace with the generated UUID. + fileSystems."/" = { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; + + swapDevices = [ + { device = "/dev/disk/by-label/swap"; } + ]; + + networking.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/system/machine/X1/configuration.nix b/system/machine/X1/configuration.nix deleted file mode 100644 index bb31107..0000000 --- a/system/machine/X1/configuration.nix +++ /dev/null @@ -1,33 +0,0 @@ -# ThinkPad X1 (NixOS). Shared laptop options live in ../../modules/laptop.nix; -# only host-specific settings are here. -{ ... }: - -{ - imports = [ - ./hardware-configuration.nix - ]; - - boot.loader.efi.canTouchEfiVariables = true; - - networking.hostName = "X1-NixOS"; - networking.domain = "client.cbg.emmaisvery.gay"; - - console.font = "Lat2-Terminus16"; - - services.pipewire = { - enable = true; - pulse.enable = true; - }; - - # This host accepts SSH, so open 22 (the firewall itself is enabled in - # laptop.nix with a default-deny policy). - services.openssh.enable = true; - networking.firewall.allowedTCPPorts = [ 22 ]; - - # Fingerprint reader: allow swaylock to authenticate via fprintd. - services.fprintd.enable = true; - security.pam.services.swaylock.fprintAuth = true; - - # See `man configuration.nix` / the stateVersion docs before changing. - system.stateVersion = "24.11"; -} diff --git a/system/machine/X1/hardware-configuration.nix b/system/machine/X1/hardware-configuration.nix deleted file mode 100644 index 888cd4a..0000000 --- a/system/machine/X1/hardware-configuration.nix +++ /dev/null @@ -1,42 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = - [ (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "sd_mod" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; - boot.extraModulePackages = [ ]; - - fileSystems."/" = - { device = "/dev/disk/by-uuid/a7145534-b122-4899-a75a-3d2e78474d6b"; - fsType = "ext4"; - }; - - fileSystems."/boot" = - { device = "/dev/disk/by-uuid/1338-3D4F"; - fsType = "vfat"; - options = [ "fmask=0077" "dmask=0077" ]; - }; - - swapDevices = - [ { device = "/dev/disk/by-uuid/e553c8dc-9d5a-48ec-87bc-9c86ce5932a4"; } - ]; - - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - networking.useDHCP = lib.mkDefault true; - # networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true; - # networking.interfaces.wlp0s20f3.useDHCP = lib.mkDefault true; - # networking.interfaces.wwan0.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; -} diff --git a/system/modules/workstation.nix b/system/modules/workstation.nix index dc7366f..96da265 100644 --- a/system/modules/workstation.nix +++ b/system/modules/workstation.nix @@ -1,10 +1,12 @@ # Form-factor-agnostic base for the physical graphical NixOS machines. Imported # by both ./laptop.nix and ./desktop.nix; those add only the bits that differ # between portable and desktop hosts (chiefly the networking backend). +# +# The bootloader is NOT set here -- it is firmware-specific, not form-factor: +# UEFI hosts (MBP, Mac Pro 3,1) use systemd-boot, the BIOS-only T400 uses GRUB. +# Each machine config declares its own. { ... }: { - boot.loader.systemd-boot.enable = true; - features.swayDesktop.enable = true; console.keyMap = "dvorak";