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

515 lines
15 KiB
Markdown

# 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).
```bash
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.
```bash
{
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.
```bash
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).
```bash
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.
```bash
sudo dnf copr enable -y solopasha/hyprland
```
- [ ] **Step 3:** Enable the DMS COPR.
```bash
sudo dnf copr enable -y avengemedia/dms
```
- [ ] **Step 4:** Refresh metadata.
```bash
sudo dnf -y makecache
```
- [ ] **Step 5:** Verify both COPRs are listed.
```bash
sudo dnf copr list | grep -E 'solopasha|avengemedia'
```
Expected: both visible and enabled.
- [ ] **Step 6:** Append to progress log.
```bash
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.
```bash
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.
```bash
hyprctl version
```
Expected: version string, no errors.
- [ ] **Step 3:** Confirm Hyprland's session file is present so GDM will offer it.
```bash
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.
```bash
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.
```bash
sudo dnf install -y dms cava matugen
```
- [ ] **Step 2:** Confirm `dms` binary is on PATH.
```bash
command -v dms && dms --version
```
Expected: path printed, version string.
- [ ] **Step 3:** Log DMS install.
```bash
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.
```bash
mkdir -p ~/.config/hypr
```
- [ ] **Step 2:** Write the config.
```bash
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.
```bash
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).
```bash
dms setup
```
Expected: walks through theme picker, writes config to `~/.config/DankMaterialShell/` or `~/.config/quickshell/dms/`.
- [ ] **Step 2:** Verify DMS config directory exists.
```bash
ls ~/.config/DankMaterialShell/ 2>/dev/null || ls ~/.config/quickshell/ 2>/dev/null
```
Expected: at least one directory present.
- [ ] **Step 3:** Log.
```bash
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:
```bash
mkdir -p ~/Pictures/wallpapers
# copy or download a wallpaper into ~/Pictures/wallpapers/default.jpg
ls ~/Pictures/wallpapers/
```
- [ ] **Step 2:** Write hyprpaper config.
```bash
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 by `stow`)
- [ ] **Step 1:** Confirm `stow` is installed.
```bash
command -v stow || sudo dnf install -y stow
```
- [ ] **Step 2:** Move the config into a new `hypr` stow package.
```bash
mkdir -p ~/.dotfiles/hypr/.config
mv ~/.config/hypr ~/.dotfiles/hypr/.config/hypr
```
- [ ] **Step 3:** Stow it to recreate the symlink.
```bash
cd ~/.dotfiles
stow --target="$HOME" hypr
```
- [ ] **Step 4:** Verify the symlink resolves.
```bash
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).
```bash
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:
```bash
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.
```bash
$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.