dotfiles/config/hypr/hyprland.conf
Mads Fagerlund ddca4e1d72
hyprland: virtual desktops, and stop OOM kills from killing the session
Workspaces were pinned one-per-monitor, so switching only ever moved one
screen. The virtual-desktops plugin replaces them with vdesks that span
every monitor, and SUPER+1..5 now switches all screens together. Hyprland
plugins are ABI-locked to the compositor, so the input's nixpkgs follows
ours to compile against the hyprland we actually run. cycleworkspaces
stays off: upstream says it breaks above 2 monitors, and pc has 3.

The session had also been dying several times a day, which looked like a
Hyprland crash and was not one. There was no swap at all, and a
nixos-rebuild forks ~60 nix evaluators; stacked on a browser and a few
editor sessions that exhausted all 15G, leaving page cache squeezed to
40M while 13G sat in anon and nothing was reclaimable. The kernel then
OOM-killed 1Password (highest oom_score_adj), and because every GUI app
shares the wayland-wm@hyprland.service cgroup, systemd's default
OOMPolicy=stop tore down the whole unit -- compositor included, with
uwsm's Restart=no meaning nothing came back.

Two independent fixes: zram swap (50% of RAM, zstd) with vm.swappiness
raised to 180, so the kernel compresses anon pages instead of evicting
cache, which raises the ceiling; and OOMPolicy=continue, so that hitting
it costs one app instead of the desktop. zram rather than a swapfile
because root is compress=zstd, and a btrfs swapfile needs its own
nodatacow, uncompressed subvolume.

Also lands the pending work in this tree: hyprshot replaces grimblast for
screenshots (it reads HYPRSHOT_DIR, not XDG_SCREENSHOTS_DIR, so shots were
landing loose in ~/Pictures), hyprlock at boot so login lands on the lock
screen and unlocks the keyring, the battery popup moves to SUPER+R, and
SUPER+W/E open floating wifi and bluetooth panels.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-04 20:19:30 +02:00

176 lines
5.4 KiB
Text

# -- Monitors & input defaults --
# Monitor layout is host-specific; written by the flake per host.
source = ~/.config/hypr/monitors.conf
# Virtual desktops plugin. A vdesk spans every monitor, so SUPER+1..5 switches
# all screens together instead of workspaces being pinned per monitor.
source = ~/.config/hypr/plugins.conf
plugin {
virtual-desktops {
# The startup toast would fire on every login now that mako exists.
notifyinit = 0
rememberlayout = size
# Deliberately NOT enabling cycleworkspaces: upstream states it does
# not work with more than 2 monitors, and pc has 3.
}
}
device {
name = apple-spi-keyboard
kb_options = caps:ctrl_modifier,grp:win_space_toggle
}
# TrackPoint (Corne nipple): hold middle button + move to scroll, joystick-style
device {
name = corne-tp
scroll_method = on_button_down
scroll_button = 274 # BTN_MIDDLE
scroll_button_lock = false # set true to toggle scroll on/off with a tap instead of holding
}
input {
kb_layout = us,dk
kb_options = grp:win_space_toggle
follow_mouse = 1
repeat_rate = 50
repeat_delay = 300
accel_profile = custom
scroll_points = 0.5 1.0 1.5 2.0 2.5
touchpad {
natural_scroll = true
scroll_factor = 0.9
tap-to-click = false
clickfinger_behavior = true # 2-finger click = right-click, 3-finger = middle (Mac-like)
disable_while_typing = true
}
}
general {
gaps_in = 0
gaps_out = 0
border_size = 0
layout = dwindle
}
animations {
enabled = false
}
dwindle {
preserve_split = true
force_split = 2
}
misc {
disable_hyprland_logo = true
disable_splash_rendering = true
focus_on_activate = true
}
ecosystem {
no_update_news = true
no_donation_nag = true
}
# -- Environment --
env = QT_QPA_PLATFORMTHEME,qt6ct
env = QT_STYLE_OVERRIDE,Adwaita-Dark
env = XDG_SCREENSHOTS_DIR,$HOME/Pictures/Screenshots
# hyprshot ignores XDG_SCREENSHOTS_DIR: it reads HYPRSHOT_DIR, else XDG_PICTURES_DIR,
# else $HOME. Without this, screenshots land loose in ~/Pictures.
env = HYPRSHOT_DIR,$HOME/Pictures/Screenshots
# -- Autostart --
exec-once = uwsm finalize
# Boot lands on the lock screen rather than an unlocked desktop. Unlocking it
# also unlocks the keyring (see security.pam.services.hyprlock in flake.nix).
exec-once = hyprlock
exec-once = swaybg -c '#16181a'
exec-once = GDK_BACKEND=wayland eww daemon
exec-once = systemctl --user start hyprpolkitagent
# -- Core --
bind = SUPER, Return, exec, kitty
bind = SUPER, D, exec, fuzzel
bind = SUPER, R, exec, eww open battery-popup
bindr = SUPER, R, exec, eww close battery-popup
bind = SUPER, Q, killactive
bind = SUPER, F, fullscreen, 1
bind = SUPER SHIFT, Space, togglefloating
bind = SUPER, Escape, exec, hyprlock
# Amphetamine-style stay-awake toggle: idle + sleep + lid. See flake.nix.
bind = SUPER, A, exec, caffeine
# -- Floating panels --
# Each gets its own app_id so the rule below floats only these and not every
# terminal. Quit with the TUI's own `q`; no toggle wrapper.
#
# The float/size rule is in monitors.conf, generated per host: Hyprland 0.56
# `size` takes ONLY absolute pixels -- percentages are silently ignored (they
# parse fine and then do nothing), so the value depends on the resolution.
bind = SUPER, W, exec, kitty --class panel-wifi impala
bind = SUPER, E, exec, kitty --class panel-bt bluetui
# -- Focus --
bind = SUPER, h, movefocus, l
bind = SUPER, j, movefocus, d
bind = SUPER, k, movefocus, u
bind = SUPER, l, movefocus, r
# -- Move windows --
bind = SUPER SHIFT, h, movewindow, l
bind = SUPER SHIFT, j, movewindow, d
bind = SUPER SHIFT, k, movewindow, u
bind = SUPER SHIFT, l, movewindow, r
# -- Virtual desktops (plugin dispatchers, not Hyprland workspaces) --
bind = SUPER, 1, vdesk, 1
bind = SUPER, 2, vdesk, 2
bind = SUPER, 3, vdesk, 3
bind = SUPER, 4, vdesk, 4
bind = SUPER, 5, vdesk, 5
bind = SUPER SHIFT, 1, movetodesk, 1
bind = SUPER SHIFT, 2, movetodesk, 2
bind = SUPER SHIFT, 3, movetodesk, 3
bind = SUPER SHIFT, 4, movetodesk, 4
bind = SUPER SHIFT, 5, movetodesk, 5
# -- Move current workspace to another monitor --
bind = SUPER ALT, h, movecurrentworkspacetomonitor, l
bind = SUPER ALT, l, movecurrentworkspacetomonitor, r
# -- Resize (hold for repeat) --
binde = SUPER CTRL, h, resizeactive, -40 0
binde = SUPER CTRL, j, resizeactive, 0 40
binde = SUPER CTRL, k, resizeactive, 0 -40
binde = SUPER CTRL, l, resizeactive, 40 0
# -- Mouse --
bindm = SUPER, mouse:272, movewindow
bindm = SUPER, mouse:273, resizewindow
# -- Audio (bindel: locked + repeat; bindl: locked, no repeat) --
bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.0 @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
# -- Brightness --
bindel = , XF86MonBrightnessUp, exec, brightnessctl set 5%+
bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%-
# -- Media --
bindl = , XF86AudioPlay, exec, playerctl play-pause
bindl = , XF86AudioNext, exec, playerctl next
bindl = , XF86AudioPrev, exec, playerctl previous
# -- Screenshots --
# Super+Ctrl+S: focused monitor -> file + clipboard
bind = SUPER CTRL, S, exec, hyprshot -m output
# Super+Shift+S: region (screen frozen while selecting) -> file + clipboard
bind = SUPER SHIFT, S, exec, hyprshot -m region -z