#!/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 </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/.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