From 1e648b6c494c917a1563fd61135ab40ca17ff9ef Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Wed, 15 Jul 2026 09:45:32 +0100 Subject: [PATCH] feat(terminal): themed Kitty when OpenGL < 4.3 (#95) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ghostty needs OpenGL 4.3; older GPUs (Intel HD 4000) top out at 4.2. Installer probes glxinfo and writes settings.terminal=kitty into theme-state when below the floor. modules/home/kitty.nix enables Kitty with the same palette, mono font, and opacity as Ghostty so the fallback still looks like Nomarchy. Default machines stay Ghostty-only (no kitty in the HM closure). Doctor and calendar still invoke Ghostty by class — V3 on Acer for SUPER+Return. Verified: V0 flake check; installer-safety; HM with terminal=kitty builds themed kitty.conf + TERMINAL=kitty; default template has no kitty bin. --- README.md | 3 +- agent/BACKLOG.md | 73 +++++------------------ agent/HARDWARE-QUEUE.md | 12 ++-- agent/JOURNAL.md | 13 ++++ docs/REQUIREMENTS.md | 21 +++++-- docs/ROADMAP.md | 6 ++ modules/home/default.nix | 1 + modules/home/kitty.nix | 63 +++++++++++++++++++ modules/home/options.nix | 10 +++- pkgs/nomarchy-install/default.nix | 2 + pkgs/nomarchy-install/nomarchy-install.sh | 41 +++++++++++++ templates/downstream/home.nix | 5 +- 12 files changed, 179 insertions(+), 71 deletions(-) create mode 100644 modules/home/kitty.nix diff --git a/README.md b/README.md index 6597487..b3af69d 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,8 @@ two tables below are split along exactly that line. | Option | Default | Purpose | |---|---|---| | `nomarchy.stateFile` | — (required) | Path to your theme-state.json | -| `nomarchy.terminal` | `"ghostty"` | Terminal for keybinds and `$TERMINAL` | +| `nomarchy.terminal` | `"ghostty"` (or `"kitty"`) | Terminal for keybinds and `$TERMINAL`; installer sets `settings.terminal = "kitty"` when OpenGL < 4.3 | +| `nomarchy.kitty.enable` | `false` | Themed Kitty (also auto-on when terminal is kitty) | | `nomarchy.keyboard.layout` | `"us"` | XKB layout for the Hyprland session (installer writes the matching `services.xserver.xkb` in system.nix; the console + LUKS prompt follow via the distro default) | | `nomarchy.keyboard.variant` | `""` | XKB variant for the session | | `nomarchy.keyboard.devices` | `{}` | Per-device layout overrides (Hyprland `device` blocks keyed by `hyprctl devices` name) — e.g. an external keyboard with its own layout/variant | diff --git a/agent/BACKLOG.md b/agent/BACKLOG.md index 6206905..21b5235 100644 --- a/agent/BACKLOG.md +++ b/agent/BACKLOG.md @@ -144,66 +144,25 @@ template source. Pass = Chromium launches after installation and HTTP/HTTPS mime defaults resolve to `chromium-browser.desktop` (and the intended live-ISO browser posture is explicit). -### 95. Live ISO/install: Ghostty does not open on Acer (old GL + two log shapes) +### 95. Live ISO/install: Ghostty does not open on Acer (old GL) — **install fallback shipped** -**Machine:** Acer Aspire M5-481T only. **Dell XPS 9350 starts Ghostty fine** -(Bernardo clarified 2026-07-15 — do not treat XPS as a Ghostty failure). +**Machine:** Acer Aspire M5-481T only (Intel HD 4000, OpenGL **4.2**). XPS +starts Ghostty fine. Generation ceiling documented in `docs/REQUIREMENTS.md`. -**Why this hardware fails:** the Aspire is Ivy Bridge–class (**Intel HD -4000**, ~2012). Mesa’s max for that GPU is **OpenGL 4.2**; Ghostty requires -**4.3**. That is a **generation ceiling**, not a missing package — no -Nomarchy or Mesa tweak can make HD 4000 report 4.3 honestly. Documented in -`docs/REQUIREMENTS.md`. The product response is a fallback terminal (or -probe), not “fix Intel drivers.” +**Shipped (this iteration):** installer probes `glxinfo`; if OpenGL < 4.3, +writes `settings.terminal = "kitty"` into theme-state. `modules/home/kitty.nix` +installs and **themes** Kitty from the same palette/font/opacity as Ghostty. +Default HM keeps Ghostty only (no kitty package). Manual override: +`settings.terminal = "kitty"` or `nomarchy.terminal = "kitty"`. -On the Acer, Bernardo installed **kitty** by hand (worked) and launched -Ghostty from that shell twice — **same box, two lifecycle points, two log -shapes**: - -**1 — Before Home Manager was properly built** (partial first boot after -#123 bake failure). Explicit GPU floor: - -``` -info(opengl): loaded OpenGL 4.2 -warning(opengl): OpenGL version is too old. Ghostty requires OpenGL 4.3 -warning(gtk_ghostty_surface): failed to initialize surface - err=error.OpenGLOutdated -warning(gtk_ghostty_surface): surface failed to initialize - err=error.SurfaceError -``` - -**2 — After home was built** (full HM generation). Silent-ish exit: - -``` -warning(gtk_ghostty_application): setting GDK_DEBUG= -warning(gtk_ghostty_application): setting GDK_DISABLE=gles-api,vulkan -~ 9s ) -``` - -No `info(opengl):` line, no `OpenGLOutdated`, no `SurfaceError` — process -ends after ~9s with only the two `GDK_*` warnings. Same Ghostty 1.3.1, same -machine; the difference is **session/config state after HM**, not a second -GPU. Likely still the GL 4.2 floor (or the same surface path) with different -verbosity once the full Ghostty/GTK/Stylix config is in place — confirm -before inventing a second root cause. Re-run post-HM with extra logging -(`GDK_DEBUG`, Ghostty verbosity, `WAYLAND_DEBUG=1`) if the quiet path is -still opaque. - -**Product impact:** default terminal is Ghostty (`nomarchy.terminal`), so on -this class of GPU SUPER+Return and the launcher entry are dead. Kitty as a -manual workaround proved the session/Wayland path is fine. - -**Fix direction:** treat the **GL ≥ 4.3 requirement** as the primary Acer -blocker (soft-fallback, alternate default terminal when GL < 4.3, or -installer/hardware probe that swaps the default). Do not leave SUPER+Return -bound to a terminal that cannot map a surface. Optionally make the post-HM -failure mode print the OpenGL reason again so the quiet log is not a -support trap. - -**Pass:** on the Acer (or a GL 4.2 / softGL stand-in), SUPER+Return opens a -usable terminal without hand-editing `home.packages`; modern-GL machines -(including the XPS) still get themed Ghostty by default. V2 softGL may cover -the floor; V3 on the Acer is the real check. +**Still open:** +- **V3** on the Acer: fresh install (or hand-set terminal + `nomarchy-home`) + → SUPER+Return opens themed Kitty; Ghostty may still fail if launched by + name (expected). +- Doctor / calendar classed windows still hardcode `ghostty` — may fail on + the same GL floor (separate small follow-up if they matter on old HW). +- Live ISO session on HD 4000 still defaults to Ghostty until install (or a + live-side probe). ### 98. Boreal: button text renders black and is difficult to read diff --git a/agent/HARDWARE-QUEUE.md b/agent/HARDWARE-QUEUE.md index 36ebf4d..42be802 100644 --- a/agent/HARDWARE-QUEUE.md +++ b/agent/HARDWARE-QUEUE.md @@ -436,12 +436,12 @@ the **T14s** (webcam case). **without** running `home-manager switch` by hand. **Fail** = still need the recovery one-liner, or GTK/nm-applet still wrong until a manual switch (then the bake is still incomplete). -- [ ] **#95 Ghostty on Acer M5-481T only** — XPS is fine (clarified - 2026-07-15). On the Acer after the fix: SUPER+Return opens a usable - terminal without hand-adding kitty. Today both attempts fail: (1) - pre-HM → explicit `OpenGL 4.2` / `OpenGLOutdated`; (2) post-HM → - quiet exit after `GDK_DISABLE=gles-api,vulkan`. Confirm post-fix - both lifecycle points open a window. Modern-GL default remains Ghostty. +- [ ] **#95 themed Kitty fallback on Acer M5-481T** — after install from an + ISO that probes GL < 4.3 (or set `settings.terminal = "kitty"` + + `nomarchy-home`): SUPER+Return opens **Kitty** with Boreal (or current + theme) colors/font — not unthemed stock. `theme-state.json` should + show `"terminal": "kitty"`. Ghostty may still refuse if launched by + name (expected on HD 4000). XPS / modern-GL machines keep Ghostty. - [ ] **#124 flake pin after main-ISO install** — on a machine installed from a **main-built** ISO (post-fix): `~/.nomarchy/flake.nix` nomarchy input must not re-resolve to a lagging `v1` that lacks diff --git a/agent/JOURNAL.md b/agent/JOURNAL.md index f6512b1..b1a5588 100644 --- a/agent/JOURNAL.md +++ b/agent/JOURNAL.md @@ -19,6 +19,19 @@ Template: --- +## 2026-07-15 — #95: themed Kitty when OpenGL < 4.3 (this commit) +- **Task:** Bernardo: when GL < 4.3 automatically add kitty — and theme it. +- **Did:** `modules/home/kitty.nix` themes Kitty from theme-state (same + palette/font/opacity as Ghostty); enables when `nomarchy.terminal` is + kitty or `nomarchy.kitty.enable`. Installer runs `glxinfo` (mesa-demos on + PATH) and, if OpenGL < 4.3, writes `settings.terminal = "kitty"` (+ + `terminalReason`) into theme-state. Docs/REQUIREMENTS + README rows. +- **Verified:** V0 flake check; HM gen with `settings.terminal=kitty` builds + — kitty.conf has Boreal colors, `TERMINAL=kitty`, Hyprland `$terminal=kitty`; + default template HM has ghostty and **no** kitty package. +- **Pending:** V3 Acer install; doctor/calendar still hardcode ghostty. +- **Next suggestion:** #123 (HM pre-activate dconf) — still highest install pain. + ## 2026-07-15 — docs/REQUIREMENTS.md (OpenGL floor + disk planning) - **Task:** Bernardo: why Acer lacks OpenGL 4.3; start documenting minimum system requirements including storage (Nix store + snapshots). diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index b2c1e41..a1de08d 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -37,13 +37,24 @@ that generation top out at OpenGL 4.2. No package upgrade on the distro can turn Ivy Bridge into a 4.3 device. Ghostty is choosing a modern GL baseline on purpose. -**Until a fallback ships** (BACKLOG **#95** — alternate terminal or probe when -GL < 4.3): machines below OpenGL 4.3 can still **install and run the -desktop**, but SUPER+Return / the default terminal entry will fail until the -user installs another terminal (e.g. kitty) or we ship an automatic fallback. +**Install-time fallback (shipped):** when `nomarchy-install` can probe OpenGL +(via `glxinfo` on the live session) and the version is **< 4.3**, it writes +`settings.terminal = "kitty"` into the machine’s `theme-state.json`. Kitty is +enabled and **themed from the same JSON** as Ghostty (palette, mono font, +opacity). SUPER+Return then launches Kitty. Ghostty stays installed (doctor / +calendar classed windows still call it — those helpers may still fail on old +GL until a later follow-up). + +If the probe fails (no display / headless VM), Ghostty remains the default — +do not guess. Existing installs can set the same by hand: + +```json +"settings": { "terminal": "kitty" } +``` + +then `nomarchy-home` (or `home-manager switch`). Other Wayland clients vary; Hyprland itself is usually fine on these GPUs. -The Ghostty floor is the one that bit real hardware first. --- diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 90f988e..df0a301 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -404,6 +404,12 @@ Design/decision records and a running log of shipped work (items marked decision rather than a drive-by. Proved to fail: dropping snapshot makes it name the missing entry. **V3 pending** — that the apps *launch* needs real hardware (HARDWARE-QUEUE, Acer M5-481T). +- ✓ **Kitty fallback when OpenGL < 4.3 (#95 partial):** Ghostty needs GL 4.3; + Ivy Bridge HD 4000 tops at 4.2 (Acer M5-481T). Installer probes `glxinfo` + and writes `settings.terminal = "kitty"` when below the floor; `kitty.nix` + themes Kitty from the same theme-state as Ghostty (ANSI, mono font, + opacity). Default machines stay Ghostty-only in the HM closure. V3 on Acer + still needed; doctor/calendar still invoke Ghostty by class. - ✓ **Mime defaults must name a present .desktop (#119):** same bug class as #94, one key over. `text/plain` pointed at `code.desktop` (vscode), which the live ISO never ships — GIO skips the missing entry and leaves no diff --git a/modules/home/default.nix b/modules/home/default.nix index e5c086f..915f686 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -11,6 +11,7 @@ ./hyprland.nix ./waybar.nix ./ghostty.nix + ./kitty.nix # themed fallback when Ghostty cannot run (OpenGL < 4.3) ./btop.nix ./rofi.nix # launcher theming + the nomarchy-menu dispatcher ./swaync.nix # notification daemon, themed from the same JSON diff --git a/modules/home/kitty.nix b/modules/home/kitty.nix new file mode 100644 index 0000000..423e749 --- /dev/null +++ b/modules/home/kitty.nix @@ -0,0 +1,63 @@ +# Kitty — fallback terminal when Ghostty cannot run (OpenGL < 4.3 on +# older GPUs, e.g. Intel HD 4000). Themed from the same theme-state.json +# as Ghostty so a machine that needs the fallback still looks like +# Nomarchy. Enabled when nomarchy.terminal is "kitty" (installer sets +# that after a GL probe) or when nomarchy.kitty.enable is forced on. +{ config, lib, ... }: + +let + t = config.nomarchy.theme; + c = t.colors; + # Default terminal may be a bare name ("kitty") or a path; match the + # basename so settings.terminal = "kitty" and nomarchy.terminal = + # "${pkgs.kitty}/bin/kitty" both light this module. + terminalIsKitty = + let + term = config.nomarchy.terminal; + base = lib.last (lib.splitString "/" term); + in + base == "kitty" || lib.hasPrefix "kitty " term; + + enabled = config.nomarchy.kitty.enable || terminalIsKitty; + + # Kitty colorN = ANSI 0–15 from the theme palette. + colorSettings = lib.listToAttrs ( + lib.imap0 (i: color: { + name = "color${toString i}"; + value = color; + }) t.ansi + ); +in +{ + config = lib.mkIf enabled { + programs.kitty = { + enable = true; + shellIntegration.enableBashIntegration = true; + shellIntegration.enableZshIntegration = true; + + font = { + name = t.fonts.mono; + size = t.fonts.size; + }; + + settings = { + background = c.base; + foreground = c.text; + cursor = c.accent; + cursor_text_color = c.base; + selection_background = c.overlay; + selection_foreground = c.text; + url_color = c.accent; + active_border_color = c.accent; + inactive_border_color = c.surface; + # Match Ghostty's padding / opacity so the two terminals feel + # like the same product when a machine falls back. + background_opacity = t.ui.terminalOpacity; + window_padding_width = lib.mkDefault 12; + confirm_os_window_close = lib.mkDefault 0; + enable_audio_bell = lib.mkDefault false; + wayland_titlebar_color = lib.mkDefault "background"; + } // colorSettings; + }; + }; +} diff --git a/modules/home/options.nix b/modules/home/options.nix index d2f9367..4071fd2 100644 --- a/modules/home/options.nix +++ b/modules/home/options.nix @@ -101,9 +101,17 @@ in terminal = lib.mkOption { type = lib.types.str; default = config.nomarchy.settings.terminal or "ghostty"; - description = "Terminal emulator command, used by keybinds and $TERMINAL."; + example = "kitty"; + description = '' + Terminal emulator command, used by keybinds and `$TERMINAL`. + Default is Ghostty. On GPUs below OpenGL 4.3 the installer writes + `settings.terminal = "kitty"` so SUPER+Return still works (Ghostty + needs 4.3; see docs/REQUIREMENTS.md). Set explicitly to override. + ''; }; + kitty.enable = lib.mkEnableOption "Kitty with Nomarchy theming (palette/font/opacity from theme-state); also auto-on when nomarchy.terminal is kitty (OpenGL < 4.3 install fallback)"; + keyboard.layout = lib.mkOption { type = lib.types.str; default = config.nomarchy.settings.keyboard.layout or "us"; diff --git a/pkgs/nomarchy-install/default.nix b/pkgs/nomarchy-install/default.nix index 07a0f30..126a9fc 100644 --- a/pkgs/nomarchy-install/default.nix +++ b/pkgs/nomarchy-install/default.nix @@ -15,6 +15,7 @@ , pciutils # lspci , usbutils # lsusb (fingerprint-reader VID probe; sysfs is the fallback) , btrfs-progs # inspect-internal map-swapfile (hibernation offset) +, mesa-demos # glxinfo — OpenGL probe for Ghostty floor (needs 4.3) , xkeyboard_config # human-readable installed layout/variant catalog # Baked metadata — what this installer installs and where it came from. , templateDir # templates/downstream (home.nix, theme-state.json) @@ -64,6 +65,7 @@ stdenvNoCC.mkDerivation { --prefix PATH : ${lib.makeBinPath [ bash gum disko whois git python3 util-linux gptfdisk parted cryptsetup lvm2 pciutils usbutils btrfs-progs + mesa-demos ]} \ --set NOMARCHY_INSTALL_SHARE "$share" \ --set NOMARCHY_XKB_RULES ${xkeyboard_config}/share/X11/xkb/rules/base.lst \ diff --git a/pkgs/nomarchy-install/nomarchy-install.sh b/pkgs/nomarchy-install/nomarchy-install.sh index d10467f..434c080 100644 --- a/pkgs/nomarchy-install/nomarchy-install.sh +++ b/pkgs/nomarchy-install/nomarchy-install.sh @@ -625,6 +625,47 @@ python3 "$SHARE/patch-template.py" "$FLAKE_DIR" </dev/null 2>&1; then + gl_ver=$( + { DISPLAY="${DISPLAY:-:0}" WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-}" \ + glxinfo -B 2>/dev/null || glxinfo -B 2>/dev/null || true; } \ + | sed -n 's/.*OpenGL version string: \([0-9][0-9]*\.[0-9][0-9]*\).*/\1/p' \ + | head -1 + ) +fi +if [[ -n "$gl_ver" ]]; then + gl_major=${gl_ver%%.*} + gl_minor=${gl_ver#*.} + gl_minor=${gl_minor%%.*} + if (( gl_major < 4 || (gl_major == 4 && gl_minor < 3) )); then + info "OpenGL $gl_ver < 4.3 — default terminal → kitty (Ghostty needs 4.3)" + python3 - "$FLAKE_DIR/theme-state.json" <<'PY' +import json, sys +path = sys.argv[1] +with open(path, encoding="utf-8") as f: + state = json.load(f) +settings = state.setdefault("settings", {}) +# Do not clobber an explicit user/template choice. +if not settings.get("terminal"): + settings["terminal"] = "kitty" + settings["terminalReason"] = "opengl-below-4.3" +with open(path, "w", encoding="utf-8") as f: + json.dump(state, f, indent=2) + f.write("\n") +PY + else + info "OpenGL $gl_ver ≥ 4.3 — keeping Ghostty as the default terminal" + fi +else + info "OpenGL version not probed (glxinfo unavailable or no display) — Ghostty stays default" +fi + # The flake.lock: composed offline — nomarchy is path-locked to the very # source the ISO carries (original stays the forge URL, so a later # `nix flake update` on the installed machine re-resolves normally). diff --git a/templates/downstream/home.nix b/templates/downstream/home.nix index 3aad767..b6d91f3 100644 --- a/templates/downstream/home.nix +++ b/templates/downstream/home.nix @@ -14,7 +14,10 @@ nomarchy.keyboard.variant = ""; # ── Overrides (the desktop is on by default; tweak or opt out) ────── - # nomarchy.terminal = "kitty"; # swap the default terminal + # nomarchy.terminal = "kitty"; # swap the default terminal (installer + # # sets this via settings.terminal when + # # OpenGL < 4.3 — Ghostty needs 4.3) + # nomarchy.kitty.enable = true; # themed Kitty without making it default # nomarchy.waybar.enable = false; # bring your own bar # nomarchy.stylix.enable = false; # opt out of GTK/Qt theming # nomarchy.dockAudio.enable = false; # keep audio put when docking