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.mdand 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
dmsbinary 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.
Phase 3 — Dotfiles symlinks
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 bystow) -
Step 1: Confirm
stowis installed.
command -v stow || sudo dnf install -y stow
- Step 2: Move the config into a new
hyprstow 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
hyprpackage — leave existing uncommitted changes inkitty/,nvim/, etc. alone).
cd ~/.dotfiles
git add hypr
git commit -m "hypr: add baseline Hyprland + DMS config"
git push
- Step 6: (Optional) Add a
dmsstow package once you've confirmed what filedms setupwrote 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+Returnopens kitty.SUPER+Dopens fuzzel launcher.SUPER+1..9switches 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+Returnlaunches kitty -
SUPER+Dlaunches fuzzel; typing + Enter launches the app -
Bar shows time, battery/system info
-
SUPER+Qcloses the focused window -
SUPER+2switches to workspace 2; opening a new app there puts it on 2 -
SUPER+SHIFT+3moves the current window to workspace 3 -
Volume keys work; a notification appears on change
-
Printtakes a screenshot (check~/Pictures/Screenshots/) -
wl-copy hello && wl-pasteround-trips clipboard -
DMS notification appears when running
notify-send "hi" "it works" -
Browser (Firefox) opens on Wayland (
echo $WAYLAND_DISPLAYinside 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-runfor NVIDIA dGPU usage - Replace
fuzzelkeybind with DMS spotlight if you prefer it - Move to
footas 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.confthe 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/hyprpolkitagentare split packages or bundled inhyprland-contrib. Ifdnf installcomplains about a missing name,dnf search <name>and adjust.