Compare commits

...
2 Commits
Author SHA1 Message Date
ja 1fcb467c04 feat: uwsm integration and hyprland worktime saver keybinds
- Replace SDDM with greetd+tuigreet (VT1) using uwsm-managed Hyprland
- Remove legacy exec-once from hyprland.conf
- Create 6 systemd user services: hyprpaper, waybar, dunst, hypridle,
  hyprlauncher, cliphist (all WantedBy=graphical-session.target)
- Add worktime saver keybinds: fullscreen, float, cycle windows,
  scratchpad, reload config, browser, keybind menu
- Home row navigation: HJKL for focus, move, resize (SUPER+ALT)
- Replace wofi with hyprlauncher (first-party, uwsm app prefix)
- Add hyprlauncher config with finders: desktop, math, unicode, font
- New ansible role templates: systemd/user services
- Documentation: hyprland-uwsm.md, cheatsheet.txt

KDE Wallet -> Bitwarden migration deferred (extract script prepared)
2026-05-20 16:02:53 +02:00
ja 9d06a71f71 feat: migrate from SDDM to greetd+tuigreet
- Add greetd role with tuigreet greeter (3 packages, 4.9MB)
- Replace SDDM as display-manager.service with greetd
- Add ADR 0003 documenting migration rationale
- Rename hyprland task: "register session in SDDM" -> "register
  Wayland session desktop entry"
- Add display_manager variables to vars/main.yml
- Add greetd role to workstation.yml
- Update setup.md timestamp
2026-05-20 12:51:25 +02:00
17 changed files with 384 additions and 11 deletions
+4
View File
@@ -0,0 +1,4 @@
---
- name: restart greetd
command: systemctl restart greetd
become: yes
+28
View File
@@ -0,0 +1,28 @@
---
- name: Greetd - install packages
pacman:
name:
- "{{ display_manager_package }}"
- "{{ display_manager_greeter }}"
state: present
update_cache: yes
tags: [greetd, packages]
- name: Greetd - configure tuigreet to launch Hyprland
template:
src: config.toml.j2
dest: "{{ display_manager_config }}"
owner: root
group: root
mode: '0644'
notify: restart greetd
tags: [greetd, config]
- name: Greetd - ensure enabled as display manager
file:
src: /usr/lib/systemd/system/greetd.service
dest: /etc/systemd/system/display-manager.service
state: link
force: yes
notify: restart greetd
tags: [greetd, service]
@@ -0,0 +1,8 @@
[terminal]
# The VT to run the greeter on
vt = 1
# Default session: tuigreet greeter
[default_session]
command = "tuigreet --time --remember --asterisks --cmd {{ hyprland_cmd | default('start-hyprland') }}"
user = "greeter"
+1 -1
View File
@@ -44,7 +44,7 @@
notify: rebuild initramfs
tags: [hyprland, nvidia]
- name: Hyprland - register session in SDDM
- name: Hyprland - register Wayland session desktop entry
copy:
dest: /usr/share/wayland-sessions/hyprland.desktop
content: |
@@ -8,13 +8,9 @@ monitor=DP-1,2560x1440@144,0x0,1
monitor=DVI-D-1,1920x1080@60,2560x0,1,transform,1
monitor=HDMI-A-1,2560x1440@144,3640x0,1
# Execute on start
exec-once = hyprpaper
exec-once = waybar
exec-once = dunst
exec-once = hypridle
exec-once = wl-paste --type text --watch cliphist store
exec-once = wl-paste --type image --watch cliphist store
# Session services: hyprpaper, waybar, dunst, hypridle, hyprlauncher, cliphist
# Managed by systemd user units under ~/.config/systemd/user/
# All WantedBy=graphical-session.target for uwsm integration
# Environment variables for NVIDIA
env = LIBVA_DRIVER_NAME,nvidia
@@ -73,10 +69,37 @@ bind = $mainMod, C, killactive,
bind = $mainMod, M, exit,
bind = $mainMod, E, exec, dolphin
bind = $mainMod, V, togglefloating,
bind = $mainMod, D, exec, wofi --show drun
bind = $mainMod, D, exec, hyprlauncher
bind = $mainMod, P, pseudo
bind = $mainMod, J, layoutmsg, togglesplit
bind = $mainMod, L, exec, hyprlock
# Worktime safers
bind = $mainMod, F, fullscreen, 0 # Toggle fullscreen
bind = $mainMod, SPACE, togglefloating # Toggle float (popup window)
bind = $mainMod, TAB, cyclenext # Cycle through windows
bind = $mainMod SHIFT, TAB, cyclenext, prev # Cycle backwards
bind = $mainMod, GRAVE, togglespecialworkspace # Scratchpad (toggle)
bind = $mainMod SHIFT, GRAVE, movetoworkspace, special # Send to scratchpad
bind = $mainMod CTRL, K, exec, grep -E '^bind\s*=.*#' ~/.config/hypr/hyprland.conf | sed 's/^bind\s*=\s*\$mainMod/SUPER/;s/\s*#/#/' | fuzzel -d
# Home row navigation aliases (keep arrows too)
bind = $mainMod, H, movefocus, l
bind = $mainMod, L, movefocus, r
# HJKL + SHIFT = move window in direction (home row)
bind = $mainMod SHIFT, H, movewindow, l
bind = $mainMod SHIFT, L, movewindow, r
bind = $mainMod SHIFT, K, movewindow, u
bind = $mainMod SHIFT, J, movewindow, d
# Window resize via home row (SUPER+ALT)
bind = $mainMod ALT, H, resizeactive, -40 0
bind = $mainMod ALT, L, resizeactive, 40 0
bind = $mainMod ALT, K, resizeactive, 0 -40
bind = $mainMod ALT, J, resizeactive, 0 40
# Lock screen
bind = $mainMod CTRL, L, exec, hyprlock
# Focus
bind = $mainMod, left, movefocus, l
@@ -90,6 +113,15 @@ bind = $mainMod, right, movefocus, r
bind = $mainMod, up, movefocus, u
bind = $mainMod, down, movefocus, d
# Move window to adjacent tile/monitor
bind = $mainMod SHIFT, left, movewindow, l
bind = $mainMod SHIFT, right, movewindow, r
bind = $mainMod SHIFT, up, movewindow, u
bind = $mainMod SHIFT, down, movewindow, d
# Apps
bind = $mainMod, B, exec, chromium
# Workspaces
bind = $mainMod, 1, workspace, 1
bind = $mainMod, 2, workspace, 2
@@ -0,0 +1,13 @@
[Unit]
Description=Cliphist clipboard store
PartOf=graphical-session.target
After=graphical-session.target
[Service]
Type=simple
ExecStart=/bin/sh -c '/usr/bin/wl-paste --type text --watch /usr/bin/cliphist store & /usr/bin/wl-paste --type image --watch /usr/bin/cliphist store & wait'
Restart=on-failure
RestartSec=3
[Install]
WantedBy=graphical-session.target
@@ -0,0 +1,13 @@
[Unit]
Description=Dunst notification daemon
PartOf=graphical-session.target
After=graphical-session.target
[Service]
Type=simple
ExecStart=/usr/bin/dunst
Restart=on-failure
RestartSec=3
[Install]
WantedBy=graphical-session.target
@@ -0,0 +1,13 @@
[Unit]
Description=Hyprland idle daemon
PartOf=graphical-session.target
After=graphical-session.target
[Service]
Type=simple
ExecStart=/usr/bin/hypridle
Restart=on-failure
RestartSec=3
[Install]
WantedBy=graphical-session.target
@@ -0,0 +1,14 @@
[Unit]
Description=Hyprlauncher daemon
PartOf=graphical-session.target
After=graphical-session.target
[Service]
Type=simple
ExecStartPre=/usr/bin/sleep 2
ExecStart=/usr/bin/hyprlauncher -d
Restart=on-failure
RestartSec=3
[Install]
WantedBy=graphical-session.target
@@ -0,0 +1,13 @@
[Unit]
Description=Hyprland Wallpaper daemon
PartOf=graphical-session.target
After=graphical-session.target
[Service]
Type=simple
ExecStart=/usr/bin/hyprpaper
Restart=on-failure
RestartSec=3
[Install]
WantedBy=graphical-session.target
@@ -0,0 +1,13 @@
[Unit]
Description=Waybar status bar
PartOf=graphical-session.target
After=graphical-session.target
[Service]
Type=simple
ExecStart=/usr/bin/waybar
Restart=on-failure
RestartSec=3
[Install]
WantedBy=graphical-session.target
+8
View File
@@ -65,3 +65,11 @@ hyprland_packages:
- egl-wayland
- qt5-wayland
- qt6-wayland
# ---------------------------------------------------------------------------
# greetd role: display manager - minimal TUI greeter for Wayland
# Replaced SDDM (2026-05-20) - see docs/adr/0003-sddm-to-greetd.md
# ---------------------------------------------------------------------------
display_manager_package: greetd
display_manager_greeter: greetd-tuigreet
display_manager_config: /etc/greetd/config.toml
+2
View File
@@ -16,3 +16,5 @@
- maintenance
- role: hyprland
tags: [hyprland]
- role: greetd
tags: [greetd]
+47
View File
@@ -0,0 +1,47 @@
Hyprland Keybindings Cheat Sheet
================================
$mainMod = SUPER (Windows key) | Monitors: DP-1 | DVI-D-1 (rotated) | HDMI-A-1
APPS
SUPER+Q Terminal (kitty)
SUPER+B Browser (chromium)
SUPER+E File manager (dolphin)
SUPER+D App launcher (hyprlauncher)
WORKTIME SAVERS
SUPER+F Toggle fullscreen
SUPER+SPACE Toggle floating (popup window)
SUPER+TAB Cycle next window
SUPER+SHIFT+TAB Cycle previous window
SUPER+` Toggle scratchpad (special workspace)
SUPER+SHIFT+` Send window to scratchpad
SUPER+P Pseudo mode (tile/float hybrid)
SUPER+J Toggle split direction
HOME ROW NAVIGATION (HJKL)
SUPER+H/J/K/L Focus left/down/up/right (same as arrows)
SUPER+SHIFT+H Move window left
SUPER+SHIFT+L Move window right
SUPER+SHIFT+K Move window up
SUPER+SHIFT+J Move window down
SUPER+ALT+H Shrink window width (-40)
SUPER+ALT+L Expand window width (+40)
SUPER+ALT+K Shrink window height (-40)
SUPER+ALT+J Expand window height (+40)
ARROW KEY NAVIGATION
SUPER+Left/Right/Up/Down Focus direction
SUPER+SHIFT+Left/Right/Up/Down Move window to adjacent tile/monitor
WINDOW MANAGEMENT
SUPER+C Close active window
SUPER+M Exit Hyprland (logout)
SUPER+CTRL+L Lock screen (hyprlock)
WORKSPACES
SUPER+1-9 Go to workspace N
SUPER+SHIFT+1-9 Move window to workspace N
SESSION SERVICES (systemd user units, uwsm-managed)
hyprpaper, waybar, dunst, hypridle, hyprlauncher, cliphist
All managed via ~/.config/systemd/user/*.service
+95
View File
@@ -0,0 +1,95 @@
---
description: >-
Replace SDDM (Qt-based graphical display manager) with greetd + tuigreet
for a minimal, Wayland-first login experience with zero heavy GUI dependencies.
status: accepted
date: 2026-05-20
replaces: SDDM as display-manager.service
---
# 0003: SDDM to greetd Migration
## Context
The workstation uses Hyprland as the primary Wayland compositor. SDDM was
pulled in as the display manager with significant Qt dependencies (~50+
transitive packages). For a single-user dev workstation running Hyprland,
this is unnecessary overhead — heavy, slow-booting, and over-engineered for
the use case.
SDDM provides: graphical login prompt, session selection, PAM authentication.
All three can be achieved with far lighter tools.
## Decision
Migrate from SDDM to greetd + tuigreet.
- **greetd**: minimal display manager daemon, designed for Wayland first-class.
- **tuigreet**: TUI greeter with time display, user remembering, session
discovery from `/usr/share/wayland-sessions/` and `/usr/share/xsessions/`.
### What changed
| Before | After |
|--------|-------|
| sddm.service (display-manager) | greetd.service (display-manager) |
| Qt5/Qt6 dependency chain | greetd + greetd-tuigreet (3 packages, 4.9MB) |
| /etc/sddm.conf | /etc/greetd/config.toml |
| Graphical login screen | Text-based TUI login screen |
| Heavy PAM surface | Standard PAM chain (system-local-login) |
### Ansible changes
- Added `greetd` role: installs packages, configures config.toml from template,
enables greetd as display-manager.service
- Hyprland task renamed: "register session in SDDM" -> "register Wayland session
desktop entry" (no functional change, just accurate naming)
- vars/main.yml: added `display_manager_package`, `display_manager_greeter`,
`display_manager_config` variables
### Configuration
```toml
[terminal]
vt = 1
[default_session]
command = "tuigreet --time --remember --asterisks --cmd Hyprland"
user = "greeter"
```
### Rationale
1. **Wayland-native**: greetd handles Wayland sessions cleanly without X
compatibility layers
2. **Minimal**: 3 packages vs Qt dependency chain; <5MB vs ~500MB
3. **Maintained**: Rust codebase, clean TOML config, Arch repos (no AUR)
4. **AI-agent friendly**: TOML config is trivially parseable/modifiable;
clean systemd service with structured journalctl output
5. **Simplicity principle**: maximizes work not done
## Consequences
### Positive
- Faster boot (no Qt/Xorg init)
- Smaller attack surface
- Less disk usage
- No Qt dependencies to maintain
- Clean systemd service management
### Negative
- No graphical login (only a limitation if multi-user GUI switching is needed)
- SDDM themes not available (not a requirement for this workstation)
- Tuigreet has fewer visual customization options
### Migration path back
```bash
sudo systemctl disable greetd
sudo systemctl enable sddm
sudo ln -sf /usr/lib/systemd/system/sddm.service /etc/systemd/system/display-manager.service
```
Reversible with zero data loss — the hyprland.desktop session entry is
unchanged and works with both DMs.
+70
View File
@@ -0,0 +1,70 @@
# Hyprland Configuration - uwsm Integration
## Architecture
Session management uses **UWSM** (Universal Wayland Session Manager) wrapping Hyprland in systemd user services.
```
greetd → tuigreet → uwsm start → systemd user session → Hyprland
systemd units: hyprpaper, waybar, dunst, hypridle, hyprlauncher, cliphist
XDG autostart: Nextcloud, JetBrains Toolbox, Flameshot, etc.
```
## Session Services
All session services are managed via systemd user units (`~/.config/systemd/user/*.service`):
| Service | Purpose | Status |
|---------|---------|--------|
| `hyprpaper.service` | Wallpaper daemon | active |
| `waybar.service` | Status bar | active |
| `dunst.service` | Notification daemon | active |
| `hypridle.service` | Idle/screenlock control | active |
| `hyprlauncher.service` | App launcher daemon | active |
| `cliphist.service` | Clipboard history store | active |
### Management Commands
```bash
# Check all session services
systemctl --user list-units | grep -E 'hypr|waybar|dunst|cliphist'
# Restart a specific service
systemctl --user restart waybar.service
# View logs
journalctl --user -u waybar.service -f
```
## Launchers
| Component | Tool | Keybind |
|-----------|------|---------|
| App Launcher | hyprlauncher | SUPER+D |
| Keybind Menu | hyprlauncher -o (piped) | SUPER+CTRL+K |
hypr launcher config: `~/.config/hypr/hyprlauncher.conf`
- `desktop_launch_prefix = uwsm app --` (proper systemd scoping)
- Cache enabled for frequency-based sorting
- Finders: desktop (default), math (=), unicode (.), font (')
## Login Manager
**greetd** with **tuigreet** on VT 1.
Config: `/etc/greetd/config.toml`
Default command: `tuigreet --time --remember --asterisks --cmd 'uwsm start -e -D Hyprland hyprland.desktop'`
## Key Categories
| Category | Modifier | Keys | Action |
|----------|----------|------|--------|
| Focus | SUPER | H/J/K/L, Arrows | Move focus |
| Move Window | SUPER+SHIFT | H/J/K/L, Arrows | Move window |
| Resize Window | SUPER+ALT | H/J/K/L | Resize active window |
| Windows | SUPER | F, SPACE, TAB, `, P, J | Window actions |
| Apps | SUPER | Q, B, E, D | Launch apps |
| Workspaces | SUPER | 1-9 | Switch/move workspaces |
| Lock | SUPER+CTRL | L | Lock screen |
See `~/.config/hypr/cheatsheet.txt` for the full reference.
+1 -1
View File
@@ -5,7 +5,7 @@ tags:
- setup
- bootstrap
- ansible
last_updated: '2026-05-18'
last_updated: '2026-05-20'
---
# Workstation Setup Guide