server-desktop-01/docs/window-manager/plan.md

15 KiB

Hyprland + DMS Migration Plan

Goal: Migrate from GNOME to Hyprland on Fedora 43, paired with DankMaterialShell (DMS), while keeping GNOME and Sway installable as fallbacks.

Architecture: Use Fedora's standard COPR-based install path for Hyprland (solopasha) and DMS (avengemedia). Run on the AMD iGPU (no proprietary NVIDIA driver needed). Store all user config under ~/.config/ and track it in ~/dotfiles (Forgejo). Adopt DMS's batteries-included shell so the bar/notifications/lock/launcher work out of the box, and use mylinuxforwork/hyprland-starter as a reference for a minimal, readable starter hyprland.conf.

Tech stack: Hyprland, Quickshell, DankMaterialShell, kitty, foot (backup), fuzzel, pipewire, hyprlock, hyprpaper, grim/slurp, wl-clipboard, cliphist, xdg-desktop-portal-hyprland, GDM.

Shell pick: DMS (over Noctalia). First-party Fedora COPR, dms setup generates sensible keybinds/autostart, replaces waybar+swaylock+swayidle+mako+fuzzel+polkit in one package. Revisit Noctalia later.

Hardware note: Machine has AMD Vega iGPU (primary) + NVIDIA RTX 3060 (discrete, unused for display). All steps assume AMD-path. Do not install akmod-nvidia as part of this plan.


Phase 0 — Preparation

Task 0.1: Bootstrap project docs and dotfiles

Files:

  • Create: ~/systems-admin/docs/window-manager/progress.md (done — append as we go)

  • Create: ~/systems-admin/docs/window-manager/cheatsheet.md (done — update as bindings change)

  • Verify: ~/dotfiles/ exists and is the Forgejo clone

  • Step 1: Confirm dotfiles repo exists at ~/.dotfiles (GNU Stow layout).

ls -ld ~/.dotfiles && git -C ~/.dotfiles remote -v

Expected: origin at git@forgejo.swansoncloud.com:jared/dotfiles.git.

  • Step 2: Record starting state in progress log.
{
  echo "## $(date -I) — Starting state"
  echo
  echo "- Fedora: $(cat /etc/fedora-release)"
  echo "- Kernel: $(uname -r)"
  echo "- Session: $XDG_SESSION_TYPE / $XDG_CURRENT_DESKTOP"
  echo "- Installed sessions: $(ls /usr/share/wayland-sessions/ | tr '\n' ' ')"
  echo "- GPU: $(lspci | grep -E 'VGA|3D' | sed 's/^/    /')"
  echo
} >> ~/systems-admin/docs/window-manager/progress.md
  • Step 3: Commit current idea.md and new docs to a git repo so decisions are preserved.
cd ~/systems-admin
git init -q 2>/dev/null || true
git add docs/window-manager/
git commit -m "docs(wm): initial idea, plan, progress log"

Task 0.2: Enable COPRs and RPM Fusion (free only)

Why: Hyprland and DMS are not in Fedora's default repos yet. RPM Fusion free gives us codecs + a few utilities; we are not enabling the nonfree NVIDIA path.

  • Step 1: Enable RPM Fusion free (safe even if already enabled).
sudo dnf install -y \
  https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
  • Step 2: Enable the solopasha Hyprland COPR.
sudo dnf copr enable -y solopasha/hyprland
  • Step 3: Enable the DMS COPR.
sudo dnf copr enable -y avengemedia/dms
  • Step 4: Refresh metadata.
sudo dnf -y makecache
  • Step 5: Verify both COPRs are listed.
sudo dnf copr list | grep -E 'solopasha|avengemedia'

Expected: both visible and enabled.

  • Step 6: Append to progress log.
echo "- $(date -Is): Enabled COPRs solopasha/hyprland, avengemedia/dms" \
  >> ~/systems-admin/docs/window-manager/progress.md

Phase 1 — Install Hyprland and essentials

Task 1.1: Install Hyprland core + session utilities

Files: none yet — package install only.

  • Step 1: Install the core set in one transaction.
sudo dnf install -y \
  hyprland hyprland-contrib hyprpaper hyprlock hypridle \
  xdg-desktop-portal-hyprland xdg-desktop-portal-gtk \
  kitty foot fuzzel \
  grim slurp hyprshot \
  wl-clipboard cliphist \
  pipewire pipewire-pulseaudio wireplumber \
  brightnessctl playerctl network-manager-applet \
  qt6-qtwayland qt5-qtwayland \
  lxpolkit
  • Step 2: Verify Hyprland runs and shows version.
hyprctl version

Expected: version string, no errors.

  • Step 3: Confirm Hyprland's session file is present so GDM will offer it.
ls /usr/share/wayland-sessions/hyprland.desktop

Expected: file exists. If missing, reinstall hyprland and check again before continuing.

  • Step 4: Log the installed Hyprland version.
echo "- $(date -Is): Installed hyprland $(hyprctl version | head -n1)" \
  >> ~/systems-admin/docs/window-manager/progress.md

Task 1.2: Install DankMaterialShell

  • Step 1: Install DMS plus commonly-needed optional deps.
sudo dnf install -y dms cava matugen
  • Step 2: Confirm dms binary is on PATH.
command -v dms && dms --version

Expected: path printed, version string.

  • Step 3: Log DMS install.
echo "- $(date -Is): Installed DMS $(dms --version 2>/dev/null | head -n1)" \
  >> ~/systems-admin/docs/window-manager/progress.md

Phase 2 — Minimal config

Approach: write one small hyprland.conf we can read top-to-bottom, not a mega-config. DMS provides bar/launcher/lock/notifications/polkit, so our hyprland.conf just handles: monitors, input, keybinds, and launching DMS.

Task 2.1: Create the base Hyprland config

Files:

  • Create: ~/.config/hypr/hyprland.conf

  • Step 1: Ensure the directory exists.

mkdir -p ~/.config/hypr
  • Step 2: Write the config.
cat > ~/.config/hypr/hyprland.conf <<'EOF'
# ----- Monitors -----
# Use Hyprland's auto-detect. Override later with specific resolution/refresh.
monitor = , preferred, auto, 1

# ----- Autostart -----
exec-once = lxpolkit
exec-once = /usr/libexec/xdg-desktop-portal-hyprland
exec-once = wl-paste --watch cliphist store
exec-once = dms run

# ----- Environment -----
env = XCURSOR_SIZE,24
env = QT_QPA_PLATFORMTHEME,qt6ct
env = MOZ_ENABLE_WAYLAND,1

# ----- Input -----
input {
    kb_layout = us
    follow_mouse = 1
    touchpad {
        natural_scroll = true
        tap-to-click = true
    }
    sensitivity = 0
}

# ----- Look -----
general {
    gaps_in = 4
    gaps_out = 8
    border_size = 2
    layout = dwindle
}

decoration {
    rounding = 8
    blur {
        enabled = true
        size = 4
        passes = 2
    }
}

animations {
    enabled = true
}

dwindle {
    pseudotile = true
    preserve_split = true
}

# ----- Keybindings -----
$mod = SUPER

# Launch
bind = $mod, Return, exec, kitty
bind = $mod, D, exec, fuzzel          # fallback launcher; DMS spotlight works too
bind = $mod, E, exec, kitty -e yazi    # file manager in terminal

# Window control
bind = $mod, Q, killactive,
bind = $mod SHIFT, Q, exit,
bind = $mod, F, fullscreen,
bind = $mod, V, togglefloating,
bind = $mod, P, pseudo,
bind = $mod, J, togglesplit,

# Focus (arrows + hjkl both work)
bind = $mod, left,  movefocus, l
bind = $mod, right, movefocus, r
bind = $mod, up,    movefocus, u
bind = $mod, down,  movefocus, d
bind = $mod, h,     movefocus, l
bind = $mod, l,     movefocus, r
bind = $mod, k,     movefocus, u
bind = $mod, j,     movefocus, d

# Move window
bind = $mod SHIFT, left,  movewindow, l
bind = $mod SHIFT, right, movewindow, r
bind = $mod SHIFT, up,    movewindow, u
bind = $mod SHIFT, down,  movewindow, d

# Workspaces
bind = $mod, 1, workspace, 1
bind = $mod, 2, workspace, 2
bind = $mod, 3, workspace, 3
bind = $mod, 4, workspace, 4
bind = $mod, 5, workspace, 5
bind = $mod, 6, workspace, 6
bind = $mod, 7, workspace, 7
bind = $mod, 8, workspace, 8
bind = $mod, 9, workspace, 9
bind = $mod, 0, workspace, 10

bind = $mod SHIFT, 1, movetoworkspace, 1
bind = $mod SHIFT, 2, movetoworkspace, 2
bind = $mod SHIFT, 3, movetoworkspace, 3
bind = $mod SHIFT, 4, movetoworkspace, 4
bind = $mod SHIFT, 5, movetoworkspace, 5
bind = $mod SHIFT, 6, movetoworkspace, 6
bind = $mod SHIFT, 7, movetoworkspace, 7
bind = $mod SHIFT, 8, movetoworkspace, 8
bind = $mod SHIFT, 9, movetoworkspace, 9
bind = $mod SHIFT, 0, movetoworkspace, 10

# Scroll through workspaces
bind = $mod, mouse_down, workspace, e+1
bind = $mod, mouse_up,   workspace, e-1

# Scratchpad
bind = $mod,       S, togglespecialworkspace, magic
bind = $mod SHIFT, S, movetoworkspace, special:magic

# Mouse drag
bindm = $mod, mouse:272, movewindow
bindm = $mod, mouse:273, resizewindow

# Screenshots (saved to ~/Pictures/Screenshots/)
bind = ,       Print, exec, hyprshot -m output
bind = SHIFT,  Print, exec, hyprshot -m region
bind = CTRL,   Print, exec, hyprshot -m window

# Media + brightness
bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindl  = , XF86AudioMute,        exec, wpctl set-mute   @DEFAULT_AUDIO_SINK@ toggle
bindl  = , XF86AudioMicMute,     exec, wpctl set-mute   @DEFAULT_AUDIO_SOURCE@ toggle
bindel = , XF86MonBrightnessUp,   exec, brightnessctl s 10%+
bindel = , XF86MonBrightnessDown, exec, brightnessctl s 10%-
bindl  = , XF86AudioPlay, exec, playerctl play-pause
bindl  = , XF86AudioNext, exec, playerctl next
bindl  = , XF86AudioPrev, exec, playerctl previous
EOF
  • Step 3: Sanity-check the config parses.
hyprland --verify-config -c ~/.config/hypr/hyprland.conf 2>&1 | tail -20 || \
  echo "(Older Hyprland versions may not have --verify-config; safe to skip)"

Expected: no fatal errors. Warnings are OK.


Task 2.2: Run DMS's setup to generate its own baseline

Why: dms setup writes its autostart/keybind snippets in a way DMS expects. Our hyprland.conf already launches dms run, so setup here is just to generate any DMS-side state files (theme, shell config).

  • Step 1: Run setup (answer prompts; accept defaults).
dms setup

Expected: walks through theme picker, writes config to ~/.config/DankMaterialShell/ or ~/.config/quickshell/dms/.

  • Step 2: Verify DMS config directory exists.
ls ~/.config/DankMaterialShell/ 2>/dev/null || ls ~/.config/quickshell/ 2>/dev/null

Expected: at least one directory present.

  • Step 3: Log.
echo "- $(date -Is): Ran 'dms setup'; baseline shell config written" \
  >> ~/systems-admin/docs/window-manager/progress.md

Task 2.3: Pick a wallpaper and wire hyprpaper

Files:

  • Create: ~/.config/hypr/hyprpaper.conf

  • Step 1: Pick any image and note its absolute path. Example:

mkdir -p ~/Pictures/wallpapers
# copy or download a wallpaper into ~/Pictures/wallpapers/default.jpg
ls ~/Pictures/wallpapers/
  • Step 2: Write hyprpaper config.
cat > ~/.config/hypr/hyprpaper.conf <<EOF
preload = $HOME/Pictures/wallpapers/default.jpg
wallpaper = ,$HOME/Pictures/wallpapers/default.jpg
EOF
  • Step 3: Add hyprpaper to autostart. Edit ~/.config/hypr/hyprland.conf, in the # ----- Autostart ----- block, append:
exec-once = hyprpaper

Note: If DMS manages your wallpaper (it often does via matugen theming), skip this task and let DMS handle it. Revisit later if DMS's wallpaper tool isn't working for you.


Task 3.1: Add a hypr stow package

Files:

  • Create: ~/.dotfiles/hypr/.config/hypr/hyprland.conf (moved from ~/.config/hypr/hyprland.conf)

  • Symlink: ~/.config/hypr/hyprland.conf~/.dotfiles/hypr/.config/hypr/hyprland.conf (created by stow)

  • Step 1: Confirm stow is installed.

command -v stow || sudo dnf install -y stow
  • Step 2: Move the config into a new hypr stow package.
mkdir -p ~/.dotfiles/hypr/.config
mv ~/.config/hypr ~/.dotfiles/hypr/.config/hypr
  • Step 3: Stow it to recreate the symlink.
cd ~/.dotfiles
stow --target="$HOME" hypr
  • Step 4: Verify the symlink resolves.
ls -l ~/.config/hypr/hyprland.conf
readlink -f ~/.config/hypr/hyprland.conf

Expected: points into ~/.dotfiles/hypr/.config/hypr/hyprland.conf.

  • Step 5: Commit (only the new hypr package — leave existing uncommitted changes in kitty/, nvim/, etc. alone).
cd ~/.dotfiles
git add hypr
git commit -m "hypr: add baseline Hyprland + DMS config"
git push
  • Step 6: (Optional) Add a dms stow package once you've confirmed what file dms setup wrote and that it's stable. Don't move it yet if DMS rewrites its config on upgrades.

Phase 4 — First login and smoke test

Task 4.1: Log out and pick Hyprland from GDM

  • Step 1: Save any open work. Log out of GNOME.

  • Step 2: At GDM, click the gear/session picker and choose Hyprland.

  • Step 3: Log in. Expected:

    • Blank desktop briefly, then DMS bar appears at top.
    • SUPER+Return opens kitty.
    • SUPER+D opens fuzzel launcher.
    • SUPER+1..9 switches workspaces.
  • Step 4: If things break: switch to TTY (Ctrl+Alt+F3), log in, and inspect:

journalctl --user -b -u graphical-session.target -n 100
cat ~/.cache/hyprland/hyprland.log | tail -100

Log out via loginctl terminate-user $USER if stuck, then pick GNOME or Sway from GDM as fallback.


Task 4.2: Smoke-test checklist

Run through each and tick off:

  • SUPER+Return launches kitty

  • SUPER+D launches fuzzel; typing + Enter launches the app

  • Bar shows time, battery/system info

  • SUPER+Q closes the focused window

  • SUPER+2 switches to workspace 2; opening a new app there puts it on 2

  • SUPER+SHIFT+3 moves the current window to workspace 3

  • Volume keys work; a notification appears on change

  • Print takes a screenshot (check ~/Pictures/Screenshots/)

  • wl-copy hello && wl-paste round-trips clipboard

  • DMS notification appears when running notify-send "hi" "it works"

  • Browser (Firefox) opens on Wayland (echo $WAYLAND_DISPLAY inside the browser's about:support shows Wayland)

  • Lock screen works: run hyprlock — can unlock with password

  • Step 1: Log results.

$EDITOR ~/systems-admin/docs/window-manager/progress.md
# Append a dated "First login" section with ✓/✗ per item.

Phase 5 — Knobs to turn later (not in this plan)

These are intentionally deferred. Write them in progress.md as "future" items as you encounter them:

  • Monitor resolution/scaling overrides (monitor = HDMI-A-1, 2560x1440@144, 0x0, 1)
  • Keyboard layout tweaks (caps→esc, etc.)
  • Per-app window rules (windowrule = float, class:^(pavucontrol)$)
  • Matugen wallpaper theming wired to DMS
  • Gaming / prime-run for NVIDIA dGPU usage
  • Replace fuzzel keybind with DMS spotlight if you prefer it
  • Move to foot as default terminal if kitty feels heavy

Self-review check

  • Spec coverage: idea.md asked for Hyprland + shell (DMS or Noctalia) + stability. Plan installs Hyprland, picks DMS with reasoning, keeps Sway/GNOME as fallbacks. ✓
  • GPU path: Plan explicitly avoids proprietary NVIDIA driver based on detected hardware (AMD iGPU primary). ✓
  • Backup goal: Configs live in ~/.config/ and are migrated into ~/dotfiles (Forgejo) in Phase 3. ✓
  • Learning goal: Minimal hand-authored hyprland.conf the user can read top-to-bottom; cheat sheet provided separately. ✓
  • Known gaps to verify at install time: exact COPR package names (solopasha occasionally renames), whether hyprshot/hypridle/hyprpolkitagent are split packages or bundled in hyprland-contrib. If dnf install complains about a missing name, dnf search <name> and adjust.