#!/bin/bash # plymouth.sh — Plymouth boot splash installer # # Installs the M-Archy Plymouth theme: the magenta skull-monitor logo centred # on a dark background, with the latest 3 boot-log lines polled underneath it # as plain text (no spinner). # # SVG NOTE: Plymouth's image loader (ply-image) is PNG-only — it links against # libpng and has no SVG or gdk-pixbuf dependency. The ply-image.h header is # even commented "png file loader". bg-skull.svg must be converted to PNG with # rsvg-convert (higher fidelity than ImageMagick for SVG) before deployment. # # Logo resolution order: # 1. $PLYMOUTH_LOGO_SRC env var — caller-supplied custom image (PNG or SVG) # 2. $DOTFILES_DIR/resources/bg-skull.svg — repo copy, always present # 3. /root/installer/resources/bg-skull.svg — archiso embedded copy # # PNG inputs are used directly; SVG inputs are converted via rsvg-convert. # # Steps: # 1. Install plymouth (extra repo) # 2. Install librsvg (rsvg-convert) if logo is SVG # 3. Produce logo.png (600 px wide) from the resolved source # 4. Write the m-archy theme (.plymouth descriptor + .script animation) # 5. Register with plymouth-set-default-theme # 6. Inject plymouth / sd-plymouth hook into /etc/mkinitcpio.conf # 7. Add 'quiet splash' to GRUB_CMDLINE_LINUX_DEFAULT # 8. Regenerate GRUB config and initramfs set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/../../lib/logging.sh" THEME_DIR="/usr/share/plymouth/themes/m-archy" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # apps/ → optional-Modules → modules → setup → repo root (four levels up). DOTFILES_DIR="$(cd "$SCRIPT_DIR/../../../.." && pwd)" # Resolve logo source — env var takes priority, then bundled SVG LOGO_SRC="${PLYMOUTH_LOGO_SRC:-}" if [[ -z "$LOGO_SRC" ]]; then for _candidate in \ "$DOTFILES_DIR/resources/bg-skull.svg" \ "/root/installer/resources/bg-skull.svg" do if [[ -f "$_candidate" ]]; then LOGO_SRC="$_candidate" break fi done fi # ── Install Plymouth ────────────────────────────────────────────────────────── log "Installing Plymouth..." sudo pacman -S --noconfirm --needed plymouth # ── Produce logo.png ────────────────────────────────────────────────────────── TMP_LOGO="$(mktemp /tmp/plymouth-logo.XXXXXX.png)" trap 'rm -f "$TMP_LOGO"' EXIT if [[ -z "$LOGO_SRC" ]]; then warn "No logo source found — using transparent placeholder." if ! command -v convert &>/dev/null; then log "Installing imagemagick (placeholder generation)..." sudo pacman -S --noconfirm --needed imagemagick fi convert -size 600x600 xc:transparent "$TMP_LOGO" elif [[ "${LOGO_SRC,,}" == *.png ]]; then log "Using PNG directly: $LOGO_SRC" cp "$LOGO_SRC" "$TMP_LOGO" else # SVG (or unknown) — convert via rsvg-convert for best fidelity if ! command -v rsvg-convert &>/dev/null; then log "Installing librsvg (rsvg-convert) for SVG→PNG conversion..." sudo pacman -S --noconfirm --needed librsvg fi # Render at 600 px wide — the skull is centred (not full-screen), and the # Plymouth script scales it down to a fraction of the display height, so we # only need enough resolution to stay crisp on high-DPI panels. log "Converting $LOGO_SRC → PNG (600 px wide)..." rsvg-convert -w 600 "$LOGO_SRC" -o "$TMP_LOGO" fi # ── Install theme files ─────────────────────────────────────────────────────── log "Installing M-Archy Plymouth theme..." sudo mkdir -p "$THEME_DIR" sudo cp "$TMP_LOGO" "$THEME_DIR/logo.png" sudo tee "$THEME_DIR/m-archy.plymouth" > /dev/null <<'EOF' [Plymouth Theme] Name=M-Archy Description=M-Archy boot splash — centred skull logo with a boot-log tail ModuleName=script [script] ImageDir=/usr/share/plymouth/themes/m-archy ScriptFile=/usr/share/plymouth/themes/m-archy/m-archy.script EOF sudo tee "$THEME_DIR/m-archy.script" > /dev/null <<'EOF' # M-Archy Plymouth splash — centred skull logo + latest 3 boot-log lines. Window.SetBackgroundTopColor (0.10, 0.10, 0.10); Window.SetBackgroundBottomColor (0.07, 0.07, 0.07); screen_width = Window.GetWidth (); screen_height = Window.GetHeight (); # ── Skull logo, centred ─────────────────────────────────────────────────────── # logo.png is the tight skull-monitor artwork (roughly square). Scale it to a # fraction of the screen height, preserving aspect ratio, then centre it and # nudge it up a little so the log lines below have room to breathe. logo.image = Image ("logo.png"); target_h = screen_height * 0.42; scale = target_h / logo.image.GetHeight (); scaled = logo.image.Scale (logo.image.GetWidth () * scale, target_h); logo.sprite = Sprite (scaled); logo.sprite.SetX ((screen_width - scaled.GetWidth ()) / 2); logo.sprite.SetY ((screen_height - scaled.GetHeight ()) / 2 - screen_height * 0.08); logo.sprite.SetZ (10); # ── Boot-log tail — latest 3 lines ──────────────────────────────────────────── # A rolling buffer of the three most recent status/message strings Plymouth # hands us, drawn as centred text beneath the logo. The newest line is the # brightest; older lines fade back. num_lines = 3; line_gap = 26; # px between successive baselines text_top = Math.Int (screen_height * 0.72); # y of the oldest (top) line for (i = 0; i < num_lines; i++) { log_line[i] = ""; log_sprite[i] = Sprite (); log_sprite[i].SetZ (20); } fun draw_lines () { for (i = 0; i < num_lines; i++) { # Empty slots (before the buffer fills) stay hidden — Image.Text ("") # is unreliable across Plymouth versions, so never render it. if (log_line[i] == "") { log_sprite[i].SetOpacity (0); } else { shade = 0.40 + 0.25 * i; # 0.40, 0.65, 0.90 top→bottom image = Image.Text (log_line[i], shade, shade, shade); log_sprite[i].SetImage (image); log_sprite[i].SetX ((screen_width - image.GetWidth ()) / 2); log_sprite[i].SetY (text_top + i * line_gap); log_sprite[i].SetOpacity (1); } } } fun push_line (text) { # Scroll the buffer up by one line and append the newcomer at the bottom. for (i = 0; i < num_lines - 1; i++) { log_line[i] = log_line[i + 1]; } log_line[num_lines - 1] = text; draw_lines (); } # Both callbacks feed the same tail: systemd unit progress arrives via the # status function, explicit `plymouth display-message` strings via the message # function. Plymouth.SetUpdateStatusFunction (fun (status) { push_line (status); }); Plymouth.SetDisplayMessageFunction (fun (text) { push_line (text); }); EOF # ── Register theme ──────────────────────────────────────────────────────────── log "Registering m-archy as default Plymouth theme..." sudo plymouth-set-default-theme m-archy # ── mkinitcpio: inject Plymouth hook ───────────────────────────────────────── log "Adding Plymouth hook to /etc/mkinitcpio.conf..." if grep -q '\bplymouth\b\|sd-plymouth' /etc/mkinitcpio.conf; then skip "Plymouth hook already present in mkinitcpio.conf" else # systemd hook → sd-plymouth goes after systemd # traditional udev hook → plymouth goes after udev if grep -qE 'HOOKS=\([^)]*\bsystemd\b' /etc/mkinitcpio.conf; then sudo sed -Ei 's/(\bsystemd\b)( |\))/\1 sd-plymouth\2/' /etc/mkinitcpio.conf log "Injected sd-plymouth hook after systemd" else sudo sed -Ei 's/(\budev\b)( |\))/\1 plymouth\2/' /etc/mkinitcpio.conf log "Injected plymouth hook after udev" fi if ! grep -q '\bplymouth\b\|sd-plymouth' /etc/mkinitcpio.conf; then warn "Could not auto-inject Plymouth hook." warn "Manually add 'plymouth' after 'udev' in /etc/mkinitcpio.conf." fi fi # ── GRUB: add quiet splash ──────────────────────────────────────────────────── GRUB_CONF="/etc/default/grub" if [[ -f "$GRUB_CONF" ]]; then if grep -q '\bsplash\b' "$GRUB_CONF"; then skip "'splash' already present in $GRUB_CONF" else log "Adding 'quiet splash' to GRUB_CMDLINE_LINUX_DEFAULT..." sudo sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"/GRUB_CMDLINE_LINUX_DEFAULT="\1 quiet splash"/' "$GRUB_CONF" sudo sed -i 's/quiet quiet/quiet/g' "$GRUB_CONF" fi log "Regenerating GRUB config..." sudo grub-mkconfig -o /boot/grub/grub.cfg else warn "/etc/default/grub not found." warn "If using systemd-boot, add 'quiet splash' to your loader entry manually." fi # ── Rebuild initramfs ───────────────────────────────────────────────────────── log "Rebuilding initramfs (this bakes the theme into the initrd)..." sudo mkinitcpio -P log "Plymouth m-archy theme installed. Reboot to see the splash screen."