diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-29 10:05:08 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-29 10:05:08 +0200 |
| commit | 266cb5655217293f733e22d677169d93e497e400 (patch) | |
| tree | 9cb0ad1aceab4684c00147166a9de471cbf5d09f /docs/superpowers/specs/2026-09-29-grub-theme-udt-design.md | |
| download | grub-theme-udt-266cb5655217293f733e22d677169d93e497e400.tar.gz grub-theme-udt-266cb5655217293f733e22d677169d93e497e400.zip | |
Add design spec, GPLv2 license
Design for a udt-themed GRUB theme: wallpaper snapshot background,
left panel echoing sddm-theme-udt, theme.txt.in rendered by
udt-palette, and a stdlib build script that assembles the
installable theme.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/superpowers/specs/2026-09-29-grub-theme-udt-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-09-29-grub-theme-udt-design.md | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-09-29-grub-theme-udt-design.md b/docs/superpowers/specs/2026-09-29-grub-theme-udt-design.md new file mode 100644 index 0000000..a9c522d --- /dev/null +++ b/docs/superpowers/specs/2026-09-29-grub-theme-udt-design.md @@ -0,0 +1,132 @@ +# grub-theme-udt design + +A GRUB 2 theme for the unified-desktop-theme (udt) family. Colours come from +the active udt scheme; the background is a snapshot of the current wallpaper; +the layout echoes sddm-theme-udt's left panel. + +## Constraints + +- GRUB reads `/boot` before anything else is mounted, so the installed theme is + a **copy**, never a symlink into `/home`. +- Installing is root's job. Nothing in this repo or in udt writes under `/boot` + or `/etc`; udt's `install.sh` only prints the root commands. +- Nothing at boot can read the wallpaper-tracking accent, so the theme uses the + scheme's fixed `accent` role. +- `theme.txt` has no include mechanism, so it is a template rendered whole. +- Target: GRUB 2.14, x86_64-efi. 2.14 supports `desktop-image-scale-method`. +- GRUB's image readers are limited: no progressive JPEG, no interlaced PNG, no + webp. Anything GRUB loads is written by `build` in a format it can read. +- Minimal dependencies: Python stdlib, `grub-mkfont`, `fc-match`, `magick`, + all already installed. + +## Layout + +- **Background:** `desktop-image: "background.jpg"` with + `desktop-image-scale-method: "crop"`, so any mode (including 2560x1080) is + filled without distortion. `desktop-color` is `@BG@`, shown if the image fails + to load. +- **Panel:** a `+ image` component at `left = 0`, `top = 0`, `width = 25%`, + `height = 100%`, file `panel.png`: a small RGBA PNG of `bg_alt` at about 85% + alpha. GRUB scales the image to the component and alpha-blends it over the + wallpaper. Square corners, as in SDDM. +- **Inside the panel,** top to bottom: + - title `+ label`: the `NAME` from `/etc/os-release`, colour `@FG@`, + Noto Sans Bold; + - `+ boot_menu`: `item_color = @FG_DIM@`, `selected_item_color = @FG@`, + Noto Sans Regular, no icons (`icon_width = 0`); + - `+ label` with `id = "__timeout__"`, colour `@FG_FAINT@`; + - `+ progress_bar` with `id = "__timeout__"`, `fg_color = @ACCENT@`, + `bg_color = @SURFACE@`, a few pixels high. +- **Selected item:** `selected_item_pixmap_style = "select_*.png"`. The `w` + slice is a 4px `accent` bar, the `c` and `e` slices are solid `surface`. +- **Console:** `terminal-box: "terminal_box_*.png"`, all nine slices solid + `bg_deep`; `terminal-font` is Inconsolata Nerd Font Mono. + +Menu entry icons are out of scope. GRUB would look for `icons/<class>.png` per +entry class; adding them later is additive. + +## Files + +| Path | Tracked | Role | +|---|---|---| +| `theme.txt.in` | yes | the template, `@ROLE@` placeholders | +| `theme.txt` | no | rendered by udt-palette | +| `build` | yes | stdlib Python script, assembles `build/udt/` | +| `build/udt/` | no | the complete theme, what root copies | +| `README.md`, `LICENSE` | yes | GPLv2 only | +| `.gitignore` | yes | `theme.txt`, `build/` | + +## Placeholders + +udt-palette substitutes these in `theme.txt.in`, each as `#rrggbb`: + +`@BG@ @BG_ALT@ @BG_DEEP@ @SURFACE@ @FG@ @FG_DIM@ @FG_FAINT@ @ACCENT@` + +plus `@SCHEME@`, the scheme name, used in a header comment. + +The template carries one machine-readable comment line that `build` parses to +get the colours it needs for the PNGs, so there is no second template: + + # udt: bg_alt=@BG_ALT@ bg_deep=@BG_DEEP@ surface=@SURFACE@ accent=@ACCENT@ + +GRUB ignores comment lines. + +## Data flow + +1. udt's `install.sh` runs udt-palette, which renders `theme.txt.in` to + `theme.txt` in this repo (the gen function and TARGETS line are owned by + the udt session). +2. udt's `install.sh` runs `./build` here (skipped if the repo is not cloned). +3. `build`: + - reads `theme.txt`, fails if any `@...@` remains or the `# udt:` line is + missing; + - substitutes the title from `/etc/os-release` into `build/udt/theme.txt`; + - writes `panel.png`, `select_{w,c,e}.png`, `terminal_box_{nw,n,ne,w,c,e,sw,s,se}.png` + with an in-script PNG encoder (`zlib` + `struct`, 8-bit RGBA, not + interlaced); + - builds the `.pf2` fonts with `grub-mkfont`, resolving the TTF paths with + `fc-match -f '%{file}'`; rebuilt only when missing, since fonts do not + change with the scheme; + - converts `~/.cache/udt/wpaper` (following the symlink) with `magick` to a + baseline, non-interlaced `background.jpg`, capped at 2560px on the long + side, metadata stripped. +4. udt's `install.sh` prints the root commands: + + rm -rf /boot/grub/themes/udt && cp -r <repo>/build/udt /boot/grub/themes/ + # once: set GRUB_THEME="/boot/grub/themes/udt/theme.txt" in /etc/default/grub + grub-mkconfig -o /boot/grub/grub.cfg + + The copy must be rerun after a scheme or wallpaper change. `grub-mkconfig` + is needed only when `GRUB_THEME` changes. + +## Fonts + +| Use | Source | Size | +|---|---|---| +| title | Noto Sans Bold | 32 | +| menu items, countdown | Noto Sans Regular | 24 | +| console | Inconsolata Nerd Font Mono | 18 | + +Font names in `theme.txt.in` must match what `grub-mkfont` records (family, +style, size), e.g. `"Noto Sans Regular 24"`; `build` checks the generated +names against the template. + +## Errors + +`build` exits non-zero with a one-line message when: `theme.txt` is missing +or unrendered, the wallpaper link is missing, `magick`/`grub-mkfont`/`fc-match` +is absent, or `fc-match` resolves to a fallback font rather than the requested +family. Nothing is written to `build/udt/` until all inputs are valid, so a +failed build never leaves a half theme for root to copy. + +## Testing + +- `./build --selftest`: encodes PNGs, decodes them back with `zlib`, checks + pixel values against the input colours; checks placeholder detection. +- Visual check before install: `grub-emu` is not packaged here, so the check + is a real boot. The previous `GRUB_THEME` line is the rollback. + +## Notes for the user + +`/etc/default/grub` has `GRUB_GFXMODE=2560x1080×32` with a Unicode `×`, so +GRUB likely rejects that mode and falls back to 1920x1080. Replace with `x`. |
