Files
fedora-niri-dms/fedora-niri-dms-setup.sh
T

350 lines
14 KiB
Bash

#!/usr/bin/env bash
#
# fedora-niri-dms-setup.sh
# Turns a fresh "Fedora Custom Operating System" (terminal-only) install
# into a niri + DankMaterialShell desktop with the DMS greeter.
#
# Usage (as your normal user, NOT root):
# chmod +x fedora-niri-dms-setup.sh
# ./fedora-niri-dms-setup.sh
#
# Safe to re-run: every step checks before changing things.
set -euo pipefail
# ---------- helpers ----------------------------------------------------------
BOLD=$'\e[1m'; GREEN=$'\e[32m'; YELLOW=$'\e[33m'; RED=$'\e[31m'; RESET=$'\e[0m'
step() { echo; echo "${BOLD}${GREEN}==> $*${RESET}"; }
info() { echo " $*"; }
warn() { echo "${YELLOW} ! $*${RESET}"; }
die() { echo "${RED}ERROR: $*${RESET}" >&2; exit 1; }
ask() {
# ask "Question?" -> returns 0 for yes, 1 for no (default no)
local reply
read -r -p " $1 [y/N] " reply
[[ "${reply,,}" == y || "${reply,,}" == yes ]]
}
dnf_install() { sudo dnf install -y "$@"; }
# ---------- pre-flight -------------------------------------------------------
[[ $EUID -eq 0 ]] && die "Run this as your normal user (it uses sudo when needed), not as root."
[[ -r /etc/fedora-release ]] || die "This script is for Fedora only."
command -v sudo >/dev/null || die "sudo is not installed."
FEDORA_VER=$(rpm -E %fedora)
VIRT=$(systemd-detect-virt 2>/dev/null || true)
echo "${BOLD}Fedora ${FEDORA_VER} -> niri + DankMaterialShell${RESET}"
[[ -n "$VIRT" && "$VIRT" != "none" ]] && warn "Running in a VM ($VIRT). niri needs 3D acceleration enabled in the VM settings, or you'll get a black screen."
# Ask optional questions up front so the rest can run unattended.
echo
echo "${BOLD}Optional extras${RESET} (answer now, then the script runs on its own):"
WANT_STEAM=false; ask "Install Steam + gamescope?" && WANT_STEAM=true
WANT_OPENVPN=false; ask "Install OpenVPN support for NetworkManager (for PIA .ovpn files)?" && WANT_OPENVPN=true
WANT_FPRINT=false; ask "Set up the fingerprint reader (Framework laptops)?" && WANT_FPRINT=true
WANT_EASYFX=false; ask "Install EasyEffects (Framework speaker profile)?" && WANT_EASYFX=true
sudo -v # cache sudo credentials early
# keep sudo alive for the duration of the script
while true; do sudo -n true; sleep 50; kill -0 "$$" || exit; done 2>/dev/null &
# ---------- 1. dnf tuning + updates -----------------------------------------
step "Tuning dnf and updating the system"
if ! grep -q '^max_parallel_downloads' /etc/dnf/dnf.conf 2>/dev/null; then
echo 'max_parallel_downloads=10' | sudo tee -a /etc/dnf/dnf.conf >/dev/null
info "Enabled parallel downloads"
fi
# dnf5-plugins provides 'copr' and 'config-manager'; not included in a minimal install
dnf_install dnf5-plugins pciutils
sudo dnf upgrade --refresh -y
# ---------- 2. RPM Fusion + OpenH264 -----------------------------------------
step "Enabling RPM Fusion (free + non-free) and OpenH264"
if ! rpm -q rpmfusion-free-release >/dev/null 2>&1; then
dnf_install \
"https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-${FEDORA_VER}.noarch.rpm" \
"https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-${FEDORA_VER}.noarch.rpm"
else
info "RPM Fusion already enabled"
fi
sudo dnf config-manager setopt fedora-cisco-openh264.enabled=1 || warn "Could not enable OpenH264 repo"
# ---------- 3. Graphics ------------------------------------------------------
step "Installing graphics drivers"
dnf_install mesa-dri-drivers mesa-vulkan-drivers vulkan-loader
GPU_INFO=$(lspci -nn | grep -Ei 'vga|3d|display' || true)
info "Detected: ${GPU_INFO:-unknown}"
NVIDIA=false
if grep -qi 'amd\|ati' <<<"$GPU_INFO"; then
info "AMD GPU -> freeworld VA-API drivers"
if rpm -q mesa-va-drivers >/dev/null 2>&1; then
sudo dnf swap -y mesa-va-drivers mesa-va-drivers-freeworld
else
dnf_install mesa-va-drivers-freeworld
fi
fi
if grep -qi 'intel' <<<"$GPU_INFO"; then
info "Intel GPU -> intel-media-driver"
dnf_install intel-media-driver
fi
if grep -qi 'nvidia' <<<"$GPU_INFO"; then
info "NVIDIA GPU -> akmod-nvidia"
dnf_install akmod-nvidia xorg-x11-drv-nvidia-cuda
NVIDIA=true
fi
# ---------- 4. Codecs --------------------------------------------------------
step "Installing full multimedia codecs"
if rpm -q ffmpeg-free >/dev/null 2>&1; then
sudo dnf swap -y ffmpeg-free ffmpeg --allowerasing
else
dnf_install ffmpeg --allowerasing
fi
sudo dnf group install -y multimedia --allowerasing || warn "Multimedia group had conflicts; core codecs are still installed"
# ---------- 5. Audio, Bluetooth, power, firmware -----------------------------
step "Installing PipeWire audio"
dnf_install pipewire pipewire-pulseaudio pipewire-alsa \
pipewire-jack-audio-connection-kit wireplumber alsa-sof-firmware
step "Installing Bluetooth"
dnf_install bluez
sudo systemctl enable --now bluetooth
step "Installing power profiles and firmware updater"
dnf_install tuned-ppd fwupd
sudo systemctl enable --now tuned tuned-ppd
# ---------- 6. niri + DankMaterialShell --------------------------------------
step "Installing niri and DankMaterialShell"
sudo dnf copr enable -y avengemedia/dms
# niri "recommends" these, but DMS replaces all of them. Waybar in particular
# gives you a second bar on top of the DMS one, and mako fights DMS over
# notifications. Keep them out (and remove them on a re-run).
DMS_REPLACES=(waybar mako fuzzel swaybg swayidle swaylock alacritty)
EXCLUDE=$(IFS=,; echo "${DMS_REPLACES[*]}")
if ! dnf_install niri dms xwayland-satellite --exclude="$EXCLUDE"; then
warn "Install with exclusions failed; retrying without them"
dnf_install niri dms xwayland-satellite
fi
REMOVE=()
for pkg in "${DMS_REPLACES[@]}"; do
rpm -q "$pkg" >/dev/null 2>&1 || continue
# only remove it if nothing hard-depends on it
if rpm -q --whatrequires "$pkg" >/dev/null 2>&1; then
warn "Keeping $pkg (another package requires it)"
else
REMOVE+=("$pkg")
fi
done
if (( ${#REMOVE[@]} )); then
info "Removing tools DMS replaces: ${REMOVE[*]}"
sudo dnf remove -y "${REMOVE[@]}"
fi
info "Starting DMS with niri"
systemctl --user add-wants niri.service dms
# ---------- 6a. xwayland-satellite menu fix -----------------------------------
# xwayland-satellite <= 0.8.2 makes X11 popup menus (e.g. Steam's) flicker and
# vanish under niri. The fix is upstream commit add2795 but not yet in a Fedora
# release. Build that commit into /usr/local/bin (which comes before /usr/bin in
# PATH), and install a boot-time check that deletes it once Fedora ships a newer
# version, so Fedora's package takes over again automatically.
XWLS_BROKEN_MAX=0.8.2
XWLS_FIX_REV=add2795134593faafce60e404a0a75df68e9ee0c
XWLS_FEDORA=$(rpm -q --qf '%{VERSION}' xwayland-satellite 2>/dev/null || echo 0)
# true if $1 <= $2 (version compare)
ver_le() { [[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -1)" == "$1" ]]; }
if ver_le "$XWLS_FEDORA" "$XWLS_BROKEN_MAX"; then
step "Building fixed xwayland-satellite (Fedora has $XWLS_FEDORA, menus are broken up to $XWLS_BROKEN_MAX)"
if [[ -x /usr/local/bin/xwayland-satellite ]]; then
info "Already built, skipping"
else
dnf_install cargo clang git pkgconf libxcb-devel xcb-util-cursor-devel
XWLS_BUILD=$(mktemp -d)
if cargo install --git https://github.com/Supreeeme/xwayland-satellite \
--rev "$XWLS_FIX_REV" --locked --root "$XWLS_BUILD"; then
sudo install -m 755 "$XWLS_BUILD/bin/xwayland-satellite" /usr/local/bin/xwayland-satellite
info "Installed to /usr/local/bin/xwayland-satellite"
else
warn "Build failed; keeping Fedora's xwayland-satellite (Steam menus may misbehave)"
fi
rm -rf "$XWLS_BUILD"
fi
if [[ -x /usr/local/bin/xwayland-satellite ]]; then
info "Installing the auto-cleanup check"
sudo mkdir -p /usr/local/libexec
sudo tee /usr/local/libexec/xwls-git-check >/dev/null <<EOF
#!/bin/bash
# Removes the custom xwayland-satellite build once Fedora ships a version
# newer than $XWLS_BROKEN_MAX, then removes itself.
cur=\$(rpm -q --qf '%{VERSION}' xwayland-satellite 2>/dev/null) || exit 0
newest=\$(printf '%s\n%s\n' "$XWLS_BROKEN_MAX" "\$cur" | sort -V | tail -1)
if [[ "\$newest" != "$XWLS_BROKEN_MAX" ]]; then
rm -f /usr/local/bin/xwayland-satellite
systemctl disable xwls-git-check.service
rm -f /etc/systemd/system/xwls-git-check.service /usr/local/libexec/xwls-git-check
systemctl daemon-reload
echo "Fedora xwayland-satellite \$cur installed; removed custom build"
fi
EOF
sudo chmod 755 /usr/local/libexec/xwls-git-check
sudo tee /etc/systemd/system/xwls-git-check.service >/dev/null <<'EOF'
[Unit]
Description=Remove custom xwayland-satellite build once Fedora's is newer
Before=display-manager.service greetd.service
[Service]
Type=oneshot
ExecStart=/usr/local/libexec/xwls-git-check
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable xwls-git-check.service
fi
else
info "Fedora's xwayland-satellite $XWLS_FEDORA already includes the menu fix"
fi
# ---------- 6b. niri config ---------------------------------------------------
step "Configuring niri for DMS and Ptyxis"
NIRI_CFG="$HOME/.config/niri/config.kdl"
mkdir -p "$(dirname "$NIRI_CFG")"
if [[ ! -f "$NIRI_CFG" ]]; then
# niri writes its default config on first launch; create it now instead so
# it can be adjusted before you ever log in.
DEFAULT_CFG=$(rpm -ql niri 2>/dev/null | grep -m1 'default-config.kdl' || true)
if [[ -n "$DEFAULT_CFG" && -f "$DEFAULT_CFG" ]]; then
cp "$DEFAULT_CFG" "$NIRI_CFG"
info "Copied niri's default config from $DEFAULT_CFG"
else
NIRI_VER=$(rpm -q --qf '%{VERSION}' niri)
if curl -fsSL "https://raw.githubusercontent.com/niri-wm/niri/v${NIRI_VER}/resources/default-config.kdl" -o "$NIRI_CFG"; then
info "Downloaded niri ${NIRI_VER} default config"
else
rm -f "$NIRI_CFG"
warn "Couldn't get niri's default config. Log into niri once, then re-run this script to apply the tweaks."
fi
fi
fi
if [[ -f "$NIRI_CFG" ]]; then
[[ -f "$NIRI_CFG.orig" ]] || cp "$NIRI_CFG" "$NIRI_CFG.orig"
sed -i -E \
-e 's|^([[:space:]]*)spawn-at-startup "waybar"|\1// spawn-at-startup "waybar" // DMS provides the bar|' \
-e 's|spawn "alacritty"|spawn "ptyxis"|g' \
-e 's|spawn "fuzzel"|spawn "dms" "ipc" "call" "spotlight" "toggle"|g' \
-e 's|spawn "swaylock"|spawn "dms" "ipc" "call" "lock" "lock"|g' \
-e 's|Open a Terminal: alacritty|Open a Terminal: Ptyxis|' \
-e 's|Run an Application: fuzzel|Run an Application: DMS launcher|' \
-e 's|Lock the Screen: swaylock|Lock the Screen: DMS|' \
"$NIRI_CFG"
info "Waybar disabled, Super+T opens Ptyxis, launcher and lock keys use DMS"
info "Original config saved as $NIRI_CFG.orig"
if command -v niri >/dev/null && ! niri validate -c "$NIRI_CFG" >/dev/null 2>&1; then
warn "niri reports a config problem; restoring the original"
cp "$NIRI_CFG.orig" "$NIRI_CFG"
fi
fi
# ---------- 7. Desktop essentials --------------------------------------------
step "Installing desktop essentials"
dnf_install \
ptyxis nautilus firefox \
xdg-desktop-portal-gnome xdg-desktop-portal-gtk \
gnome-keyring gnome-keyring-pam \
google-noto-sans-fonts google-noto-color-emoji-fonts \
xdg-user-dirs flatpak
info "Auto-unlocking the keyring at login"
sudo authselect enable-feature with-pam-gnome-keyring || warn "authselect failed; keyring may ask for a password at login"
info "Creating home folders"
xdg-user-dirs-update
info "Adding Flathub"
flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo
sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
# ---------- 8. Login screen --------------------------------------------------
step "Installing the DMS greeter"
if ! dnf_install dms-greeter; then
info "Not found in the dms COPR, trying avengemedia/danklinux"
sudo dnf copr enable -y avengemedia/danklinux
dnf_install dms-greeter
fi
dms greeter enable
dms greeter sync || warn "Greeter theme sync failed; re-run 'dms greeter sync' after first login"
dms greeter status || true
sudo systemctl set-default graphical.target
# ---------- 9. Optional extras -----------------------------------------------
if $WANT_STEAM; then
step "Installing Steam and gamescope"
dnf_install steam gamescope
fi
if $WANT_OPENVPN; then
step "Installing NetworkManager OpenVPN support"
dnf_install NetworkManager-openvpn
info "Import a PIA config later with:"
info " nmcli connection import type openvpn file ~/Downloads/<region>.ovpn"
fi
if $WANT_FPRINT; then
step "Setting up the fingerprint reader"
dnf_install fprintd fprintd-pam
sudo authselect enable-feature with-fingerprint || warn "Could not enable fingerprint auth"
info "Enrol a finger now (press Ctrl+C to skip and run 'fprintd-enroll' later)"
fprintd-enroll || warn "Enrolment skipped or failed; run 'fprintd-enroll' later"
fi
if $WANT_EASYFX; then
step "Installing EasyEffects"
flatpak install -y --user flathub com.github.wwmm.easyeffects
info "Load Framework's speaker profile from their knowledge base after first login"
fi
# ---------- done -------------------------------------------------------------
step "All done"
info "Next boot goes to the DMS login screen. Pick the 'niri' session."
info "Press Super+Shift+/ after logging in to see all keybinds."
$NVIDIA && warn "NVIDIA: wait ~5 minutes for the driver module to finish building before rebooting (check with: modinfo -F version nvidia)."
[[ -n "$VIRT" && "$VIRT" != "none" ]] && warn "VM: make sure 3D acceleration is on before rebooting."
info "If the screen stays black: Ctrl+Alt+F2, log in, and run 'niri-session' or 'dms greeter status'."
echo
if ask "Reboot now?"; then
sudo reboot
fi