feat(greeter): switch HyprLua to LightDM, fix Plymouth splash, extract ly module

Plymouth: bg-skull.svg is a 1920x1080 transparent composition but was rendered
at 300px and treated as a small centred logo, so the skull all but vanished and
the spinner (positioned off the logo height) landed on the monitor artwork. Now
render at native 1920px, scale to fill the screen, and pin the spinner at a
fixed 0.93 of screen height. Also fix DOTFILES_DIR (was off by one level, so the
bundled SVG never resolved and it fell back to a transparent placeholder) and
the dead ../plymouth.sh paths in plymouth-custom.sh and install-modules.sh.

Greeter: HyprLua now uses LightDM + lightdm-gtk-greeter, themed with the
existing cyberqueer GTK theme and a rendered skull background, via a new
self-contained lightdm module. ly is extracted from the DE scripts into its own
module so it stays installable; niri and legacy hyprland keep their inline ly.
Both greeters are registered in modules.conf as mutually exclusive, and the
generated installer/answerfile/docs regions were rebuilt from it. install-modules.sh
guards the 'ly' token with a padded match so it can't substring-hit 'p(ly)mouth'.

Add setup/tools/migrate-to-lightdm.sh to move an existing install (ly / greetd /
GDM / SDDM / LXDM) onto the Plymouth + LightDM setup: it disables the old DM for
the next boot only (never stops the running session) and delegates install +
theming to the module scripts. Supports --dry-run / --no-plymouth / --yes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/astal-menu
Amir Alexander Abdelbaki 2026-07-04 01:53:37 +02:00
parent 2ffedd076e
commit d1e5f765aa
12 changed files with 398 additions and 33 deletions

View File

@ -135,6 +135,8 @@ bash ~/Dotfiles/setup/install-modules.sh
| `wprs` | Wayland proxy for remote sessions (wprs-git, AUR) |
| `plymouth` | boot splash — bundled skull logo and spinner |
| `plymouth-custom` | boot splash with a user-supplied image |
| `lightdm` | LightDM GTK greeter, cyberqueer-themed (HyprLua default) |
| `ly` | ly TUI display manager, cyberqueer-themed |
<!-- END GENERATED MODULES: system -->
### Gaming

View File

@ -0,0 +1,17 @@
# lightdm-gtk-greeter.conf — cyberqueer-themed LightDM greeter
#
# Deployed to /etc/lightdm/lightdm-gtk-greeter.conf by
# setup/modules/optional-Modules/apps/lightdm.sh.
#
# Colours are inherited from the cyberqueer GTK theme (installed to
# /usr/share/themes/cyberqueer), which is an apply-theme.sh SYS_FILES target —
# so there are no hardcoded palette hex values to keep in sync here.
[greeter]
theme-name = cyberqueer
icon-theme-name = Papirus-Dark
background = /usr/share/backgrounds/cyberqueer-skull.png
font-name = JetBrains Mono 11
xft-antialias = true
xft-hintstyle = hintfull
indicators = ~host;~spacer;~clock;~spacer;~session;~power
clock-format = %H:%M

View File

@ -113,6 +113,9 @@ count_steps() {
[[ "$sel" == *"butter"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$sel" == *"plymouth"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$sel" == *"plymouth-custom"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$sel" == *"lightdm"* ]] && TOTAL=$(( TOTAL + 1 ))
# 'ly' is padded/quote-stripped so it doesn't substring-match 'p(ly)mouth'.
[[ " ${sel//\"/} " == *" ly "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$sel" == *"tlp"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$sel" == *"steam"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$sel" == *"vesktop"* ]] && TOTAL=$(( TOTAL + 1 ))
@ -211,6 +214,8 @@ SELECTED=$(dialog --backtitle "$BACKTITLE" \
"butter" "butter btrfs snapshot backup (AUR)" off \
"plymouth" "Plymouth boot splash — skull logo + spinner" off \
"plymouth-custom" "Plymouth (custom) boot splash — supply your own image" off \
"lightdm" "LightDM GTK greeter, cyberqueer-themed" off \
"ly" "ly TUI display manager, cyberqueer" off \
"tlp" "TLP laptop power management" off \
\
"steam" "Steam gaming platform" off \
@ -304,6 +309,8 @@ SUMMARY=""
[[ "$SELECTED" == *"localtunnel"* ]] && SUMMARY+=" ✦ LocalTunnel\n"
[[ "$SELECTED" == *"butter"* ]] && SUMMARY+=" ✦ butter (btrfs backup)\n"
[[ "$SELECTED" == *"plymouth"* ]] && SUMMARY+=" ✦ Plymouth boot splash\n"
[[ "$SELECTED" == *"lightdm"* ]] && SUMMARY+=" ✦ LightDM greeter\n"
[[ " ${SELECTED//\"/} " == *" ly "* ]] && SUMMARY+=" ✦ ly greeter\n"
if [[ "$SELECTED" == *"plymouth-custom"* ]]; then
if [[ -n "$PLYMOUTH_LOGO_SRC" ]]; then
SUMMARY+=" ✦ Plymouth (custom logo: $PLYMOUTH_LOGO_SRC)\n"
@ -384,11 +391,14 @@ DE_DIR="$MODULES/Desktop-Environments"
[[ "$SELECTED" == *"ffmpeg"* ]] && run_module "FFmpeg extras" "$APPS/ffmpeg.sh"
[[ "$SELECTED" == *"localtunnel"* ]] && run_module "LocalTunnel" "$APPS/localtunnel.sh"
[[ "$SELECTED" == *"butter"* ]] && run_module "butter" "$APPS/butter.sh"
[[ "$SELECTED" == *"plymouth"* ]] && run_module "Plymouth" "$MODULES/optional-Modules/plymouth.sh"
[[ "$SELECTED" == *"plymouth"* ]] && run_module "Plymouth" "$APPS/plymouth.sh"
if [[ "$SELECTED" == *"plymouth-custom"* ]]; then
export PLYMOUTH_LOGO_SRC
run_module "Plymouth (custom)" "$APPS/plymouth-custom.sh"
fi
[[ "$SELECTED" == *"lightdm"* ]] && run_module "LightDM greeter" "$APPS/lightdm.sh"
# 'ly' is padded/quote-stripped so it doesn't substring-match 'p(ly)mouth'.
[[ " ${SELECTED//\"/} " == *" ly "* ]] && run_module "ly greeter" "$APPS/ly.sh"
[[ "$SELECTED" == *"tlp"* ]] && run_module "TLP" "$APPS/tlp.sh"
[[ "$SELECTED" == *"steam"* ]] && run_module "Steam" "$APPS/steam.sh"
[[ "$SELECTED" == *"vesktop"* ]] && run_module "Vesktop" "$APPS/vesktop.sh"

View File

@ -51,6 +51,8 @@ zfs|system|zfs-dkms kernel module|off|
wprs|system|Wayland proxy for remote sessions (wprs-git, AUR)|off|
plymouth|system|boot splash — bundled skull logo and spinner|on|plymouth-custom
plymouth-custom|system|boot splash with a user-supplied image|off|plymouth
lightdm|system|LightDM GTK greeter, cyberqueer-themed (HyprLua default)|off|ly
ly|system|ly TUI display manager, cyberqueer-themed|off|lightdm
# ── Gaming ────────────────────────────────────────────────────────────────────
steam|gaming|Steam gaming platform|off|

View File

@ -25,7 +25,7 @@
# 3. systemd service enablement
# 4. AUR packages via yay
# 5. EWW bar (form-factor selection + Rust compilation)
# 6. Themes, ly config, terminal symlink, SSH askpass, dark-mode preference
# 6. Themes, terminal symlink, SSH askpass, dark-mode preference
# 7. Nordzy left-hand cursor theme
# 8. Bluetooth / iwd services
# 9. Config file deployment (hyprlua source tree)
@ -133,7 +133,6 @@ HYPRLUA_PACKAGES=(
hyprpicker # colour picker outputting hex/rgb/hsl values
pcmanfm-qt # Qt file manager (kept as fallback / alternative)
udisks2 # D-Bus daemon for querying and managing storage devices
ly # minimal TUI display manager (takes over tty1 from getty)
kew # terminal-based music player
hyprpolkitagent # Hyprland-native Polkit authentication agent
@ -155,18 +154,13 @@ log "Enabling essential services..."
# NetworkManager: manages all network connections (wired, Wi-Fi, VPN).
enable_service NetworkManager.service
# Disable the default getty login prompt on tty1 so ly can own that TTY.
# '|| true' prevents abort if the unit is already disabled.
sudo systemctl disable getty@tty1.service || true
# Disable greetd, which core.sh enables by default for every install. hyprlua
# uses ly as its greeter, so leaving greetd enabled means two display managers
# both race to claim tty1 — and the stale greetd/tuigreet greeter can win,
# showing the old text login instead of ly. Disable it so ly is the sole greeter.
disable_service greetd.service
# ly: TUI display manager that presents the login screen on tty1.
enable_service ly@tty1.service
# Greeter: HyprLua uses LightDM with the cyberqueer GTK greeter. The lightdm
# module installs LightDM, deploys its themed config + skull background, disables
# the competing greeters (greetd from core.sh, ly, and the tty1 getty) and
# enables lightdm.service — so exactly one display manager owns the login screen.
# ly remains available as a standalone module (optional-Modules/apps/ly.sh) for
# anyone who prefers it.
bash "$(dirname "${BASH_SOURCE[0]}")/../optional-Modules/apps/lightdm.sh"
# udisks2: D-Bus block-device service required by udiskie for auto-mounting.
enable_service udisks2.service
@ -273,8 +267,8 @@ sudo cp -r ~/Dotfiles/gtk-themes/cyberqueer /usr/share/themes
# Install the matching btop resource-monitor colour theme.
sudo cp ~/Dotfiles/desktopenvs/hyprlua/btop/themes/cyberqueer.theme /usr/share/btop/themes
# Deploy the ly display-manager configuration (login screen appearance).
sudo cp -f ~/Dotfiles/etc-ly-config.ini /etc/ly/config.ini
# (The login greeter — LightDM + cyberqueer GTK greeter — is set up in step 3
# via the lightdm module, which also deploys the greeter theme and background.)
# Register kitty as the system terminal for xdg-terminal-exec so file managers
# and scripts that open "a terminal here" consistently launch kitty.

View File

@ -0,0 +1,71 @@
#!/bin/bash
# lightdm.sh — LightDM + GTK greeter (cyberqueer-themed)
#
# The default greeter for HyprLua. LightDM's GTK greeter reuses the cyberqueer
# GTK theme shipped in this repo (gtk-themes/cyberqueer), so it matches the rest
# of the desktop, and it is driven by a single trivial INI file
# (/etc/lightdm/lightdm-gtk-greeter.conf) — the simplest config of the
# LightDM/LXDM/GDM options.
#
# This module is self-contained (installs the theme + skull background itself)
# so it can also be selected on its own from modules.conf. It is mutually
# exclusive with the ly greeter module. Enabling LightDM here disables the other
# greeters (greetd, ly) and the tty1 getty so exactly one display manager runs.
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/../../lib/logging.sh"
# apps/ → optional-Modules → modules → setup → repo root (four levels up).
DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
log "Installing LightDM + GTK greeter..."
sudo pacman -S --noconfirm --needed lightdm lightdm-gtk-greeter
# ── Ensure the cyberqueer GTK theme is present system-wide ────────────────────
# The greeter runs as the 'lightdm' user and reads themes from /usr/share/themes.
# DE modules already install this, but copy it here too so LightDM can be chosen
# standalone. (Papirus-Dark icons are pulled in by the desktop modules.)
GTK_THEME_SRC="$DOTFILES_DIR/gtk-themes/cyberqueer"
if [[ -d "$GTK_THEME_SRC" ]]; then
log "Installing cyberqueer GTK theme to /usr/share/themes..."
sudo cp -r "$GTK_THEME_SRC" /usr/share/themes/
else
warn "cyberqueer GTK theme not found at $GTK_THEME_SRC — greeter will use the default GTK theme."
fi
# ── Render the skull background from the bundled SVG ───────────────────────────
# lightdm-gtk-greeter needs a raster background; convert the 1920x1080 SVG.
SKULL_SRC="$DOTFILES_DIR/resources/bg-skull.svg"
SKULL_DST="/usr/share/backgrounds/cyberqueer-skull.png"
if [[ -f "$SKULL_SRC" ]]; then
if ! command -v rsvg-convert &>/dev/null; then
log "Installing librsvg (rsvg-convert) for SVG→PNG conversion..."
sudo pacman -S --noconfirm --needed librsvg
fi
log "Rendering skull background → $SKULL_DST..."
TMP_BG="$(mktemp /tmp/lightdm-skull.XXXXXX.png)"
trap 'rm -f "$TMP_BG"' EXIT
rsvg-convert -w 1920 "$SKULL_SRC" -o "$TMP_BG"
sudo install -Dm644 "$TMP_BG" "$SKULL_DST"
else
warn "Skull SVG not found at $SKULL_SRC — greeter will fall back to a solid background."
fi
# ── Deploy the greeter configuration ──────────────────────────────────────────
GREETER_CONF_SRC="$DOTFILES_DIR/etc-lightdm/lightdm-gtk-greeter.conf"
if [[ -f "$GREETER_CONF_SRC" ]]; then
log "Deploying lightdm-gtk-greeter.conf..."
sudo install -Dm644 "$GREETER_CONF_SRC" /etc/lightdm/lightdm-gtk-greeter.conf
else
warn "Greeter config not found at $GREETER_CONF_SRC — LightDM will use greeter defaults."
fi
# ── Make LightDM the sole display manager ─────────────────────────────────────
# LightDM manages its own VT; free tty1 and disable the competing greeters so
# nothing else races it.
sudo systemctl disable getty@tty1.service || true
disable_service greetd.service
disable_service ly@tty1.service
enable_service lightdm.service
log "LightDM installed and enabled. The cyberqueer GTK greeter starts on next boot."

View File

@ -0,0 +1,43 @@
#!/bin/bash
# ly.sh — ly TUI display manager (cyberqueer-themed)
#
# ly is a minimal, animated TUI login manager that takes over tty1 from getty.
# Its cyberqueer theming (colormix / doom animation colours) lives in the repo's
# etc-ly-config.ini, which is also an apply-theme.sh SYS_FILES target so the
# palette stays in sync.
#
# This module was extracted from the Desktop-Environment scripts so ly can be
# installed independently of any particular DE. It is mutually exclusive with
# the lightdm greeter module (see modules.conf). Enabling ly here disables the
# other greeters (greetd, lightdm) and the tty1 getty so exactly one display
# manager owns tty1.
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/../../lib/logging.sh"
# apps/ → optional-Modules → modules → setup → repo root (four levels up).
DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
log "Installing ly display manager..."
sudo pacman -S --noconfirm --needed ly
# ── Deploy the cyberqueer ly configuration ────────────────────────────────────
LY_CONF_SRC="$DOTFILES_DIR/etc-ly-config.ini"
if [[ -f "$LY_CONF_SRC" ]]; then
log "Deploying ly config to /etc/ly/config.ini..."
sudo mkdir -p /etc/ly
sudo cp -f "$LY_CONF_SRC" /etc/ly/config.ini
else
warn "ly config not found at $LY_CONF_SRC — leaving ly defaults in place."
fi
# ── Make ly the sole greeter on tty1 ──────────────────────────────────────────
# Free tty1 from the getty login prompt so ly can own it.
sudo systemctl disable getty@tty1.service || true
# Turn off the other display managers so two greeters don't race for tty1.
disable_service greetd.service
disable_service lightdm.service
# ly presents the login screen on tty1.
enable_service ly@tty1.service
log "ly display manager installed and enabled. It takes over tty1 on next boot."

View File

@ -19,4 +19,4 @@ fi
log "Custom Plymouth logo: $PLYMOUTH_LOGO_SRC"
export PLYMOUTH_LOGO_SRC
exec bash "$(dirname "${BASH_SOURCE[0]}")/../plymouth.sh"
exec bash "$(dirname "${BASH_SOURCE[0]}")/plymouth.sh"

View File

@ -1,8 +1,9 @@
#!/bin/bash
# plymouth.sh — Plymouth boot splash installer
#
# Installs the M-Archy Plymouth theme: skull logo centred on a dark background
# with a magenta spinning-dot animation below it.
# Installs the M-Archy Plymouth theme: the transparent skull/CRT artwork scaled
# to fill the screen over a dark background, with a magenta spinning-dot
# animation near the bottom.
#
# 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
@ -19,7 +20,7 @@
# Steps:
# 1. Install plymouth (extra repo)
# 2. Install librsvg (rsvg-convert) if logo is SVG; imagemagick for dot
# 3. Produce logo.png (300 px wide) from the resolved source
# 3. Produce logo.png (native 1920 px wide) from the resolved source
# 4. Generate a 10×10 magenta dot.png for the spinner
# 5. Write the m-archy theme (.plymouth descriptor + .script animation)
# 6. Register with plymouth-set-default-theme
@ -32,7 +33,8 @@ source "$(dirname "${BASH_SOURCE[0]}")/../../lib/logging.sh"
THEME_DIR="/usr/share/plymouth/themes/m-archy"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOTFILES_DIR="$(cd "$SCRIPT_DIR/../../.." && 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:-}"
@ -67,7 +69,7 @@ trap 'rm -f "$TMP_LOGO" "$TMP_DOT"' EXIT
if [[ -z "$LOGO_SRC" ]]; then
warn "No logo source found — using transparent placeholder."
convert -size 300x300 xc:transparent "$TMP_LOGO"
convert -size 1920x1080 xc:transparent "$TMP_LOGO"
elif [[ "${LOGO_SRC,,}" == *.png ]]; then
log "Using PNG directly: $LOGO_SRC"
cp "$LOGO_SRC" "$TMP_LOGO"
@ -77,8 +79,10 @@ else
log "Installing librsvg (rsvg-convert) for SVG→PNG conversion..."
sudo pacman -S --noconfirm --needed librsvg
fi
log "Converting $LOGO_SRC → PNG (300 px wide)..."
rsvg-convert -w 300 "$LOGO_SRC" -o "$TMP_LOGO"
# Render at native width so the full-screen splash keeps detail when the
# Plymouth script scales it to the display resolution.
log "Converting $LOGO_SRC → PNG (1920 px wide)..."
rsvg-convert -w 1920 "$LOGO_SRC" -o "$TMP_LOGO"
fi
# ── Generate spinner dot ──────────────────────────────────────────────────────
@ -106,7 +110,7 @@ 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 — skull logo + magenta spinner
# M-Archy Plymouth splash — full-screen skull background + magenta spinner
Window.SetBackgroundTopColor (0.10, 0.10, 0.10);
Window.SetBackgroundBottomColor (0.07, 0.07, 0.07);
@ -114,19 +118,24 @@ Window.SetBackgroundBottomColor (0.07, 0.07, 0.07);
screen_width = Window.GetWidth ();
screen_height = Window.GetHeight ();
# Centred skull logo
# Full-screen skull background. bg-skull.svg is a 1920x1080 composition with a
# transparent field, so we scale it to fill the display and pin it at the
# origin; the transparent areas reveal the dark background gradient above.
logo.image = Image ("logo.png");
logo.sprite = Sprite (logo.image);
logo.sprite.SetX (Math.Int (screen_width / 2 - logo.image.GetWidth () / 2));
logo.sprite.SetY (Math.Int (screen_height / 2 - logo.image.GetHeight () / 2));
scaled = logo.image.Scale (screen_width, screen_height);
logo.sprite = Sprite (scaled);
logo.sprite.SetX (0);
logo.sprite.SetY (0);
logo.sprite.SetZ (10);
# 12-dot spinner below the logo
# 12-dot spinner near the bottom of the screen. The centre is a fixed fraction
# of the screen height (not derived from the logo size) so it stays put
# regardless of the source image's dimensions.
num_dots = 12;
dot_r = 5;
orbit = 35;
cx = screen_width / 2;
cy = screen_height / 2 + logo.image.GetHeight () / 2 + 55;
cy = Math.Int (screen_height * 0.93);
dot.image = Image ("dot.png");
for (i = 0; i < num_dots; i++) {

View File

@ -239,7 +239,7 @@ if [[ "$AF_RUN_TUI" == "true" ]]; then
# BEGIN GENERATED MODULES: module-checklist
AF_APPS=$(dialog --backtitle "$BACKTITLE" \
--title " Applications " \
--checklist "Optional applications — installed after base components:" 40 76 80 \
--checklist "Optional applications — installed after base components:" 40 76 82 \
"ollama" "ollama local LLM runner and API server" off \
"llama-cpp" "llama-cpp standalone LLM inference CLI and server" off \
"open-webui" "open-webui browser UI for Ollama and LLM backends" off \
@ -274,6 +274,8 @@ if [[ "$AF_RUN_TUI" == "true" ]]; then
"wprs" "wprs Wayland proxy for remote sessions (wprs-git, AUR)" off \
"plymouth" "plymouth boot splash — bundled skull logo and spinner" on \
"plymouth-custom" "plymouth-custom boot splash with a user-supplied image" off \
"lightdm" "lightdm LightDM GTK greeter, cyberqueer-themed (HyprLua default)" off \
"ly" "ly ly TUI display manager, cyberqueer-themed" off \
"steam" "steam Steam gaming platform" off \
"vesktop" "vesktop Discord client with Vencord theme" off \
"spotify" "spotify Spotify launcher with Spicetify theming" off \

193
setup/tools/migrate-to-lightdm.sh Executable file
View File

@ -0,0 +1,193 @@
#!/bin/bash
# =============================================================================
# migrate-to-lightdm.sh — switch an existing install to Plymouth + LightDM
# =============================================================================
# Migrates a machine that currently boots ly / greetd / GDM / SDDM / LXDM (or any
# other display manager) to this repo's boot experience:
#
# Plymouth skull splash → LightDM + lightdm-gtk-greeter (cyberqueer theme)
#
# What it does:
# 1. Detects the currently-configured display manager(s).
# 2. Disables every known greeter EXCEPT lightdm — for the NEXT boot only. It
# does NOT stop your running session, so you stay logged in until you
# reboot yourself.
# 3. Runs the lightdm module (installs LightDM + lightdm-gtk-greeter, deploys
# the cyberqueer greeter config + skull background, enables lightdm.service).
# 4. Runs the plymouth module (skull splash, initramfs + GRUB cmdline) unless
# --no-plymouth is given.
#
# It reuses the real module scripts under optional-Modules/apps/ so there is no
# duplicated install logic — this tool only handles detection, teardown of the
# OLD display manager, and orchestration.
#
# Safe to re-run (idempotent). Reboot to complete the switch.
#
# Usage:
# migrate-to-lightdm.sh [--yes] [--no-plymouth] [--dry-run] [--help]
# -y, --yes Skip the confirmation prompt (required for non-TTY runs)
# --no-plymouth Migrate the display manager only; leave Plymouth alone
# -n, --dry-run Print what would happen; change nothing
# -h, --help Show this help and exit
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SETUP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
APPS="$SETUP_DIR/modules/optional-Modules/apps"
# Shared helpers: log / skip / warn / err, and best-effort disable_service.
source "$SETUP_DIR/modules/lib/logging.sh"
# ── Argument parsing ──────────────────────────────────────────────────────────
ASSUME_YES=0
DO_PLYMOUTH=1
DRY_RUN=0
usage() {
cat <<'EOF'
migrate-to-lightdm.sh — switch this machine to Plymouth + LightDM (cyberqueer)
Disables the current display manager (ly / greetd / GDM / SDDM / LXDM / …) for
the next boot, installs and themes LightDM, and sets up the Plymouth splash. Your
running session is left untouched; reboot to complete the switch.
Usage:
migrate-to-lightdm.sh [--yes] [--no-plymouth] [--dry-run] [--help]
-y, --yes Skip the confirmation prompt (required for non-TTY runs)
--no-plymouth Migrate the display manager only; leave Plymouth alone
-n, --dry-run Print what would happen; change nothing
-h, --help Show this help and exit
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-y|--yes) ASSUME_YES=1 ;;
--no-plymouth) DO_PLYMOUTH=0 ;;
-n|--dry-run) DRY_RUN=1 ;;
-h|--help) usage; exit 0 ;;
*) err "Unknown argument: $1"; echo "Try --help." >&2; exit 2 ;;
esac
shift
done
# In dry-run, describe an action instead of doing it. In real mode, run it.
run() {
if (( DRY_RUN )); then
printf '[dry-run] would run: %s\n' "$*"
else
"$@"
fi
}
# ── Known display managers ────────────────────────────────────────────────────
# Every greeter/DM unit this tool knows how to turn off. lightdm is the TARGET,
# so it never appears here. ly is a template unit (ly@tty1) but some setups use
# the plain ly.service alias, so both are listed.
OTHER_DMS=(
"ly@tty1.service" "ly.service"
"greetd.service"
"gdm.service" "gdm3.service"
"sddm.service"
"lxdm.service"
"xdm.service"
"slim.service"
"lemurs.service"
"emptty.service"
)
# ── 1. Detect what is configured now ──────────────────────────────────────────
log "Detecting the current display manager configuration..."
# The canonical "which DM boots" pointer is the display-manager.service symlink.
# Use plain readlink to get the intended target name even if the link is broken;
# a self-referential/absent target means no real DM is configured there.
primary_dm=""
if dm_link="$(readlink /etc/systemd/system/display-manager.service 2>/dev/null)"; then
dm_link="$(basename "$dm_link")"
[[ "$dm_link" != "display-manager.service" ]] && primary_dm="$dm_link"
fi
# Also probe every known unit directly — ly-based installs often have no
# display-manager.service symlink, they just enable ly@tty1 on tty1.
enabled_dms=()
for unit in "${OTHER_DMS[@]}"; do
state="$(systemctl is-enabled "$unit" 2>/dev/null || true)"
[[ "$state" == "enabled" || "$state" == "alias" || "$state" == "enabled-runtime" ]] \
&& enabled_dms+=("$unit")
done
# `is-enabled` on an absent unit both prints "not-found" and exits non-zero;
# capture with `|| true` (never `|| echo`, which would concatenate two lines)
# and normalise the "not installed" cases to one label.
lightdm_state="$(systemctl is-enabled lightdm.service 2>/dev/null || true)"
[[ -z "$lightdm_state" || "$lightdm_state" == "not-found" ]] && lightdm_state="not-installed"
echo
echo " Current display-manager.service : ${primary_dm:-<none>}"
if (( ${#enabled_dms[@]} )); then
echo " Enabled greeter units : ${enabled_dms[*]}"
else
echo " Enabled greeter units : <none detected>"
fi
echo " lightdm.service : $lightdm_state"
echo " Plymouth migration : $( ((DO_PLYMOUTH)) && echo 'yes' || echo 'skipped (--no-plymouth)')"
echo
if (( ${#enabled_dms[@]} == 0 )) && [[ "$lightdm_state" == "enabled" ]]; then
log "LightDM already appears to be the active greeter — re-running to refresh config/theme."
fi
# ── 2. Confirm ────────────────────────────────────────────────────────────────
# This changes what greets you at the NEXT boot. Require an explicit yes unless
# --yes was given; on a non-interactive stdin, --yes is mandatory.
if (( ! ASSUME_YES )); then
if [[ ! -t 0 ]]; then
err "Non-interactive shell and --yes not given — refusing to change the display manager."
exit 1
fi
read -r -p "Switch this machine to Plymouth + LightDM as described above? [y/N] " reply
case "$reply" in
y|Y|yes|YES) ;;
*) warn "Aborted — nothing changed."; exit 0 ;;
esac
fi
# ── 3. Disable the OLD display managers (next boot only) ──────────────────────
# We intentionally do NOT `systemctl stop` the running greeter: that would kill
# your current graphical session. Disabling only removes it from the next boot;
# lightdm.service (enabled below) takes over then.
if (( ${#enabled_dms[@]} )); then
log "Disabling old display manager(s) for the next boot: ${enabled_dms[*]}"
for unit in "${enabled_dms[@]}"; do
run disable_service "$unit"
done
else
skip "No competing display managers enabled — nothing to disable."
fi
# ── 4. Install + theme + enable LightDM (delegated to the module) ─────────────
# lightdm.sh installs LightDM + the GTK greeter, deploys the cyberqueer greeter
# config + skull background, disables greetd/ly@tty1/getty@tty1, and enables
# lightdm.service. Running it here keeps a single source of truth.
log "Setting up LightDM via the lightdm module..."
run bash "$APPS/lightdm.sh"
# ── 5. Plymouth splash (delegated to the module) ──────────────────────────────
if (( DO_PLYMOUTH )); then
log "Setting up the Plymouth splash via the plymouth module..."
run bash "$APPS/plymouth.sh"
else
skip "Skipping Plymouth setup (--no-plymouth)."
fi
# ── Done ──────────────────────────────────────────────────────────────────────
echo
if (( DRY_RUN )); then
log "Dry run complete — no changes were made."
else
log "Migration complete. Your current session is untouched."
log "Reboot to boot into the Plymouth splash and the LightDM cyberqueer greeter."
fi

View File

@ -367,6 +367,8 @@ count_steps() {
[[ " $a " == *" wprs "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ " $a " == *" plymouth "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ " $a " == *" plymouth-custom "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ " $a " == *" lightdm "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ " $a " == *" ly "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ " $a " == *" steam "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ " $a " == *" vesktop "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ " $a " == *" spotify "* ]] && TOTAL=$(( TOTAL + 1 ))
@ -589,6 +591,8 @@ else
"wprs" "wprs Wayland proxy for remote sessions (wprs-git, AUR)" off \
"plymouth" "plymouth boot splash — bundled skull logo and spinner" on \
"plymouth-custom" "plymouth-custom boot splash with a user-supplied image" off \
"lightdm" "lightdm LightDM GTK greeter, cyberqueer-themed (HyprLua default)" off \
"ly" "ly ly TUI display manager, cyberqueer-themed" off \
"steam" "steam Steam gaming platform" off \
"vesktop" "vesktop Discord client with Vencord theme" off \
"spotify" "spotify Spotify launcher with Spicetify theming" off \
@ -700,6 +704,8 @@ if ! $ANSWERFILE_MODE; then
[[ " $SELECTED_APPS " == *" wprs "* ]] && SUMMARY+=" ✦ wprs\n"
[[ " $SELECTED_APPS " == *" plymouth "* ]] && SUMMARY+=" ✦ plymouth\n"
[[ " $SELECTED_APPS " == *" plymouth-custom "* ]] && SUMMARY+=" ✦ plymouth-custom\n"
[[ " $SELECTED_APPS " == *" lightdm "* ]] && SUMMARY+=" ✦ lightdm\n"
[[ " $SELECTED_APPS " == *" ly "* ]] && SUMMARY+=" ✦ ly\n"
[[ " $SELECTED_APPS " == *" steam "* ]] && SUMMARY+=" ✦ steam\n"
[[ " $SELECTED_APPS " == *" vesktop "* ]] && SUMMARY+=" ✦ vesktop\n"
[[ " $SELECTED_APPS " == *" spotify "* ]] && SUMMARY+=" ✦ spotify\n"
@ -804,6 +810,20 @@ if [[ " $SELECTED_APPS " == *" plymouth-custom "* && " $SELECTED_APPS " == *" pl
SELECTED_APPS="${SELECTED_APPS# }"
SELECTED_APPS="${SELECTED_APPS% }"
fi
if [[ " $SELECTED_APPS " == *" lightdm "* && " $SELECTED_APPS " == *" ly "* ]]; then
warn "'lightdm' and 'ly' are mutually exclusive — deselecting 'ly'"
SELECTED_APPS=" $SELECTED_APPS "
SELECTED_APPS="${SELECTED_APPS/ ly / }"
SELECTED_APPS="${SELECTED_APPS# }"
SELECTED_APPS="${SELECTED_APPS% }"
fi
if [[ " $SELECTED_APPS " == *" ly "* && " $SELECTED_APPS " == *" lightdm "* ]]; then
warn "'ly' and 'lightdm' are mutually exclusive — deselecting 'lightdm'"
SELECTED_APPS=" $SELECTED_APPS "
SELECTED_APPS="${SELECTED_APPS/ lightdm / }"
SELECTED_APPS="${SELECTED_APPS# }"
SELECTED_APPS="${SELECTED_APPS% }"
fi
# END GENERATED MODULES: module-conflicts
# BEGIN GENERATED MODULES: module-dispatch
[[ " $SELECTED_APPS " == *" ollama "* ]] && run_module "ollama" "$APPS/ollama.sh"
@ -840,6 +860,8 @@ fi
[[ " $SELECTED_APPS " == *" wprs "* ]] && run_module "wprs" "$APPS/wprs.sh"
[[ " $SELECTED_APPS " == *" plymouth "* ]] && run_module "plymouth" "$APPS/plymouth.sh"
[[ " $SELECTED_APPS " == *" plymouth-custom "* ]] && run_module "plymouth-custom" "$APPS/plymouth-custom.sh"
[[ " $SELECTED_APPS " == *" lightdm "* ]] && run_module "lightdm" "$APPS/lightdm.sh"
[[ " $SELECTED_APPS " == *" ly "* ]] && run_module "ly" "$APPS/ly.sh"
[[ " $SELECTED_APPS " == *" steam "* ]] && run_module "steam" "$APPS/steam.sh"
[[ " $SELECTED_APPS " == *" vesktop "* ]] && run_module "vesktop" "$APPS/vesktop.sh"
[[ " $SELECTED_APPS " == *" spotify "* ]] && run_module "spotify" "$APPS/spotify.sh"