A custom Wayland desktop setup built around Hyprland, Quickshell, Lua-generated Hyprland config, Pywal theming, and native Quickshell UI modules.
The active desktop UI lives in:
~/.config/quickshell
The active Hyprland generator, generated config, and system scripts live in:
~/.config/hypr
- Overview
- Architecture
- Repository Layout
- Quickshell Entry Point
- Quickshell Modules
- Shared Components
- Services
- Hyprland Config
- Lua Generator
- Scripts
- IPC Commands
- Keybinds
- Data Flow
- Theme And Wallpaper Flow
- Validation
- Troubleshooting
- Maintenance Notes
This setup turns Hyprland into a full custom desktop environment:
- Hyprland manages windows, workspaces, input, keybinds, autostart, and session behavior.
- Quickshell renders the bar, popups, launcher, clipboard picker, notifications, OSDs, wallpaper picker, and overview.
- Lua source files generate Hyprland
.conffiles and Quickshell dashboard data. - Pywal generates colors from the selected wallpaper.
- Shell IPC connects Hyprland keybinds to Quickshell UI actions.
The current workflow avoids Rofi for core desktop actions. The launcher, clipboard picker, dashboard, power menu, notification center, wallpaper picker, and overview are native Quickshell modules.
Hyprland keybind
-> qs ipc call ...
-> Quickshell IpcHandler
-> ShellState singleton
-> popup/module opens, closes, or runs an action
For generated config:
~/.config/hypr/lua/*.lua
-> ~/.config/hypr/generate.lua
-> ~/.config/hypr/generated/*.conf
-> ~/.config/hypr/hyprpaper.conf
-> ~/.config/hypr/hypridle.conf
-> ~/.config/hypr/hyprlock.conf
-> ~/.config/quickshell/data/dashboard.json
For wallpaper/theme changes:
Quickshell wallpaper picker
-> ~/.config/hypr/scripts/wallpaper-picker.lua
-> hyprpaper wallpaper
-> wal -i
-> update-theme.lua
-> generate.lua
~/.config/quickshell
|-- shell.qml
|-- README.md
|-- components/
|-- data/
| `-- dashboard.json
|-- modules/
| |-- bar/
| |-- clipboard/
| |-- controlCenter/
| |-- dashboard/
| |-- launcher/
| |-- notifications/
| |-- osd/
| |-- overview/
| |-- powermenu/
| `-- wallpaperPicker/
|-- services/
|-- theme/
`-- widgets/
~/.config/hypr
|-- hyprland.conf
|-- hypridle.conf
|-- hyprlock.conf
|-- hyprpaper.conf
|-- generate.lua
|-- data/
| `-- dashboard.csv
|-- generated/
| |-- appearance.conf
| |-- autostart.conf
| |-- env.conf
| |-- input.conf
| |-- keybinds.conf
| |-- monitors.conf
| |-- variables.conf
| `-- windowrules.conf
|-- lua/
`-- scripts/
Main file:
~/.config/quickshell/shell.qml
It loads:
TopBarPowerMenuVolumeOSDBrightnessOSDLockOSDPowerProfileOSDNotificationPopupNotificationCenterWallpaperPickerAppLauncherClipboardPickerWorkspaceOverview
It also exposes shell-level IPC:
qs ipc call shell toggleOverview
qs ipc call shell openOverview
qs ipc call shell closeOverview
qs ipc call shell showPowerProfileOsdPath:
modules/bar/
The bar is split into:
LeftSection.qml: workspaces and tray.CenterSection.qml: clock and media controls.RightSection.qml: CPU, RAM, network, audio, battery, keyboard, power.TopBar.qml: panel window and dashboard attachment point.
Path:
modules/launcher/
Native app launcher backed by DesktopEntries.
IPC:
qs ipc call launcher toggle
qs ipc call launcher open
qs ipc call launcher closeImportant files:
AppLauncher.qml: panel UI and app launch behavior.LauncherState.qml: app loading, search, categories, selection.LauncherList.qml: result list.LauncherItem.qml: individual app row/card.
Path:
modules/clipboard/
Native clipboard picker backed by cliphist.
IPC:
qs ipc call clipboard toggle
qs ipc call clipboard open
qs ipc call clipboard close
qs ipc call clipboard reload
qs ipc call clipboard copyMode
qs ipc call clipboard deleteMode
qs ipc call clipboard deleteAllImportant files:
ClipboardPicker.qml: UI and keyboard navigation.ClipboardState.qml: list/copy/delete/wipe commands.ClipboardItem.qml: row display.
Path:
modules/dashboard/
Task, project, and exam tracker.
IPC:
qs ipc call dashboard toggle
qs ipc call dashboard open
qs ipc call dashboard close
qs ipc call dashboard reloadImportant files:
Dashboard.qml: popup window and orchestration.DashboardState.qml: active category and filtering.DashboardActions.qml: add/remove actions through Lua scripts.DashboardList.qml: grouped item display.DashboardAddPopup.qml: add-item form.UniCard.qml: dashboard item card.
Path:
modules/notifications/
Native notification popup and notification center.
IPC:
qs ipc call notificationCenter toggle
qs ipc call notificationCenter open
qs ipc call notificationCenter close
qs ipc call notificationCenter clearImportant files:
NotificationPopup.qml: transient toast stack.NotificationCenter.qml: grouped notification panel.NotificationGroupCard.qml: grouped notification UI.NotificationCard.qml: notification rendering.services/NotificationService.qml: notification server, snapshots, grouping, dismissal.
Path:
modules/wallpaperPicker/
Native wallpaper picker backed by:
~/.config/hypr/scripts/wallpaper-picker.lua
IPC:
qs ipc call wallpaperPicker toggle
qs ipc call wallpaperPicker open
qs ipc call wallpaperPicker close
qs ipc call wallpaperPicker restoreImportant files:
WallpaperPicker.qml: panel UI.WallpaperActions.qml: list/apply/restore processes.WallpaperList.qml: horizontal wallpaper list.WallpaperCard.qml: wallpaper preview card.
Path:
modules/overview/
Workspace overview with live window previews and drag-to-workspace behavior.
IPC:
qs ipc call shell toggleOverviewImportant files:
WorkspaceOverview.qml: fullscreen overview window.WorkspacePreview.qml: workspace tile.WindowPreview.qml: window preview and drag/focus behavior.OverviewConfig.qml: pinned special workspace labels.
Path:
modules/powermenu/
IPC:
qs ipc call powerMenu toggle
qs ipc call powerMenu open
qs ipc call powerMenu closeActions:
- Lock:
loginctl lock-session - Suspend:
systemctl suspend - Reboot:
systemctl reboot - Shutdown:
systemctl poweroff
Path:
modules/osd/
IPC:
qs ipc call volumeOsd show
qs ipc call volumeOsd raise
qs ipc call volumeOsd lower
qs ipc call volumeOsd toggleMute
qs ipc call brightnessOsd show
qs ipc call brightnessOsd raise
qs ipc call brightnessOsd lower
qs ipc call lockOsd caps
qs ipc call lockOsd num
qs ipc call powerProfileOsd show
qs ipc call powerProfileOsd refreshPath:
components/
Common UI building blocks:
ActionButton.qmlAnimatedPopupCard.qmlBadge.qmlBarActionPill.qmlBarInfoPill.qmlBarMediaPill.qmlBarPill.qmlCard.qmlDivider.qmlFormInput.qmlHeadingText.qmlIconButton.qmlMetaText.qmlPopupBackdrop.qmlSearchBox.qmlSlider.qmlTitleText.qml
Path:
services/
Singletons:
ShellState.qml: global popup, OSD, and overview state.NotificationService.qml: notification server and grouping.DashboardData.qml: reads generated dashboard JSON.
Module file:
services/qmldir
Main file:
~/.config/hypr/hyprland.conf
It sources generated files:
source = ~/.cache/wal/colors-hyprland.conf
source = ~/.config/hypr/generated/env.conf
source = ~/.config/hypr/generated/variables.conf
source = ~/.config/hypr/generated/monitors.conf
source = ~/.config/hypr/generated/autostart.conf
source = ~/.config/hypr/generated/windowrules.conf
source = ~/.config/hypr/generated/keybinds.conf
source = ~/.config/hypr/generated/appearance.conf
source = ~/.config/hypr/generated/input.conf
Do not manually edit generated files unless you are testing something temporarily. Make persistent changes in ~/.config/hypr/lua/.
Main generator:
lua ~/.config/hypr/generate.luaSource modules:
~/.config/hypr/lua/appearance.lua
~/.config/hypr/lua/autostart.lua
~/.config/hypr/lua/dashboard.lua
~/.config/hypr/lua/env.lua
~/.config/hypr/lua/hypridle.lua
~/.config/hypr/lua/hyprlock.lua
~/.config/hypr/lua/hyprpaper.lua
~/.config/hypr/lua/input.lua
~/.config/hypr/lua/keybinds.lua
~/.config/hypr/lua/monitors.lua
~/.config/hypr/lua/variables.lua
~/.config/hypr/lua/windowrules.lua
Generated outputs:
~/.config/hypr/generated/*.conf
~/.config/hypr/hyprpaper.conf
~/.config/hypr/hypridle.conf
~/.config/hypr/hyprlock.conf
~/.config/quickshell/data/dashboard.json
Path:
~/.config/hypr/scripts/
Active scripts:
dashboard-add.lua: appends dashboard item data and regenerates.dashboard-remove.lua: removes dashboard item data and regenerates.power-profile-toggle.lua: cycles power profile and shows Quickshell OSD.screenshot.lua: screenshot modes for full, region, copy, edit, and window.update-theme.lua: converts Pywal colors into Quickshell and GTK/Rofi theme files.wallpaper-picker.lua: applies wallpaper, runs Pywal, updates theme, regenerates Hypr config.
Common commands:
qs ipc call launcher toggle
qs ipc call clipboard toggle
qs ipc call clipboard deleteMode
qs ipc call dashboard toggle
qs ipc call powerMenu toggle
qs ipc call wallpaperPicker toggle
qs ipc call notificationCenter toggle
qs ipc call shell toggleOverviewOSD commands:
qs ipc call volumeOsd raise
qs ipc call volumeOsd lower
qs ipc call volumeOsd toggleMute
qs ipc call brightnessOsd raise
qs ipc call brightnessOsd lower
qs ipc call lockOsd caps
qs ipc call lockOsd num
qs ipc call shell showPowerProfileOsdSource:
~/.config/hypr/lua/keybinds.lua
Generated:
~/.config/hypr/generated/keybinds.conf
Current primary bindings:
| Key | Action |
|---|---|
SUPER + T |
Open terminal |
SUPER + B |
Open browser |
SUPER + D |
Toggle app launcher |
SUPER + TAB |
Toggle workspace overview |
SUPER + E |
Open file manager |
SUPER + L |
Lock session |
SUPER + ESCAPE |
Toggle power menu |
SUPER + Q |
Close active window |
SUPER + F |
Toggle fullscreen |
SUPER + X |
Toggle floating |
SUPER + M |
Exit Hyprland |
SUPER + 1..9 |
Switch workspace |
SUPER + SHIFT + 1..9 |
Move window to workspace |
SUPER + Left/Right |
Move to previous/next workspace |
Print |
Full screenshot |
SHIFT + Print |
Region screenshot |
CTRL + Print |
Region screenshot to clipboard |
ALT + Print |
Active window screenshot |
SUPER + Print |
Screenshot editor |
SUPER + V |
Clipboard picker |
SUPER + SHIFT + V |
Clipboard delete mode |
SUPER + ALT + V |
Clear clipboard history |
SUPER + N |
Notification center |
SUPER + A |
Dashboard |
SUPER + Space |
Switch keyboard layout |
SUPER + P |
Cycle power profile |
SUPER + SHIFT + W |
Wallpaper picker |
Media and hardware keys:
| Key | Action |
|---|---|
XF86AudioRaiseVolume |
Raise volume and show OSD |
XF86AudioLowerVolume |
Lower volume and show OSD |
XF86AudioMute |
Toggle mute and show OSD |
XF86MonBrightnessUp |
Raise brightness and show OSD |
XF86MonBrightnessDown |
Lower brightness and show OSD |
XF86AudioPlay |
Play/pause media |
XF86AudioNext |
Next track |
XF86AudioPrev |
Previous track |
Caps_Lock |
Show Caps Lock OSD |
Num_Lock |
Show Num Lock OSD |
Source data:
~/.config/hypr/data/dashboard.csv
Generated Quickshell data:
~/.config/quickshell/data/dashboard.json
Add/remove flow:
Dashboard UI
-> DashboardActions.qml
-> dashboard-add.lua or dashboard-remove.lua
-> dashboard.csv
-> generate.lua
-> dashboard.json
-> DashboardData.qml reload
NotificationServer
-> NotificationService.qml
-> NotificationPopup.qml
-> NotificationCenter.qml
Notifications are snapshotted before display so popups can outlive the raw notification object safely.
wl-paste --watch cliphist store
-> cliphist list
-> ClipboardState.qml
-> cliphist decode | wl-copy
Wallpaper picker module:
modules/wallpaperPicker/
Backend script:
~/.config/hypr/scripts/wallpaper-picker.lua
Theme updater:
~/.config/hypr/scripts/update-theme.lua
Generated Quickshell theme:
~/.config/quickshell/theme/WalTheme.qml
Wallpaper flow:
Select wallpaper
-> write ~/.cache/current-wallpaper
-> copy to ~/Pictures/wallpaper.png
-> apply through hyprpaper IPC
-> run wal -i
-> regenerate WalTheme.qml
-> regenerate Hyprland config
Check Quickshell loads:
timeout 4s quickshell --path ~/.config/quickshell/shell.qml --no-colorCheck running Quickshell instances:
qs listCheck Hyprland config errors:
hyprctl configerrorsRegenerate Hyprland and dashboard data:
lua ~/.config/hypr/generate.luaReload Hyprland:
hyprctl reloadCheck Lua syntax:
find ~/.config/hypr -path ~/.config/hypr/old-backup -prune -o -name '*.lua' -print -exec luac -p {} \;Check Markdown and whitespace in this repo:
git diff --checkCheck whether Quickshell is running:
qs listRestart Quickshell:
qs kill
quickshellRun:
hyprctl configerrorsIf the error is in generated/*.conf, edit the corresponding Lua file in ~/.config/hypr/lua/, then regenerate:
lua ~/.config/hypr/generate.lua
hyprctl reloadCheck:
pgrep -x hyprpaper
cat ~/.cache/current-wallpaper
hyprctl monitors -jRun the backend directly:
~/.config/hypr/scripts/wallpaper-picker.lua /path/to/wallpaper.pngLog file:
~/.cache/wallpaper-picker.log
Regenerate data:
lua ~/.config/hypr/generate.lua
qs ipc call dashboard reloadCheck source data:
~/.config/hypr/data/dashboard.csv
Check generated data:
~/.config/quickshell/data/dashboard.json
Check that cliphist watchers are running from Hyprland autostart:
pgrep -af 'wl-paste.*cliphist'
cliphist listCheck:
powerprofilesctl get
qs ipc call shell showPowerProfileOsdScript log:
~/.cache/power-profile-toggle.log
Required tools:
grim
slurp
wl-copy
jq
notify-send
swappy
Run:
~/.config/hypr/scripts/screenshot.lua full
~/.config/hypr/scripts/screenshot.lua region
~/.config/hypr/scripts/screenshot.lua copy
~/.config/hypr/scripts/screenshot.lua window
~/.config/hypr/scripts/screenshot.lua edit- Edit Quickshell UI in
~/.config/quickshell. - Edit Hyprland source config in
~/.config/hypr/lua. - Regenerate Hyprland config after Lua source changes.
- Avoid editing
~/.config/hypr/generated/*.confdirectly. - Keep active scripts in
~/.config/hypr/scripts. - Old backup files under
~/.config/hypr/old-backupare reference material, not active config. - The Quickshell repo may show deleted root-level Lua files; the active Lua scripts are in
~/.config/hypr/scripts.
Recent maintenance included:
- Centralized media player state in
widgets/MediaPlayerState.qml. - Fixed notification group expansion duplication.
- Rebuilt notification groups when tracked notification count changes.
- Allowed overview to show more than five normal workspaces.
- Made wallpaper picker paths use
$HOME. - Standardized
services/qmldir. - Improved Hypr Lua path handling.
- Added screenshot command success checks.
- Made wallpaper script detect the active monitor.
- Quoted the
qspath in the power profile script.
lua ~/.config/hypr/generate.lua
hyprctl configerrors
hyprctl reload
timeout 4s quickshell --path ~/.config/quickshell/shell.qml --no-color