Files
homelab/hosts/terra/home/hyprland.nix

303 lines
12 KiB
Nix

{ lib, pkgs, config, inputs, ... }:
let
lua = lib.generators.mkLuaInline;
# Cursor theme+size live in home.pointerCursor (theme.nix) so the name is
# in one place; hyprland.lua is what actually gets them into the graphical
# session's environment (hm-session-vars.sh is only sourced by login shells).
cursorName = config.home.pointerCursor.name;
cursorSize = toString config.home.pointerCursor.size;
# Wallpapers aren't checked into this repo (binaries) — pulled from the
# Wallhaven library on /mnt/hdd_01. Picked once here since hyprpaper has
# no built-in "random" mode.
wallpaper = "/mnt/hdd_01/data/Pictures/Wallhaven/wallhaven-mlwz78.png";
# Dispatchers → the new hl.dsp.* API (signatures verified against hyprland
# 0.55's src/config/lua/bindings/LuaBindingsDispatchers.cpp).
dsp = {
exec = cmd: lua ''hl.dsp.exec_cmd("${cmd}")'';
global = name: lua ''hl.dsp.global("${name}")'';
exit = lua "hl.dsp.exit()";
killactive = lua "hl.dsp.window.close()";
togglefloating = lua ''hl.dsp.window.float({ action = "toggle" })'';
fullscreen = lua "hl.dsp.window.fullscreen()"; # mode 0
maximize = lua ''hl.dsp.window.fullscreen({ mode = "maximized" })''; # mode 1
togglegroup = lua "hl.dsp.group.toggle()";
changegroupactive = lua "hl.dsp.group.next()";
drag = lua "hl.dsp.window.drag()";
resizemouse = lua "hl.dsp.window.resize()";
movefocus = dir: lua ''hl.dsp.focus({ direction = "${dir}" })'';
movewindow = dir: lua ''hl.dsp.window.move({ direction = "${dir}" })'';
resizeactive = x: y:
lua ''hl.dsp.window.resize({ x = ${toString x}, y = ${toString y}, relative = true })'';
workspace = n: lua ''hl.dsp.focus({ workspace = ${toString n} })'';
workspaceRef = s: lua ''hl.dsp.focus({ workspace = "${s}" })'';
movetoworkspace = n: lua ''hl.dsp.window.move({ workspace = ${toString n} })'';
togglespecial = name: lua ''hl.dsp.workspace.toggle_special("${name}")'';
};
bind = keys: dispatcher: { _args = [ keys dispatcher ]; };
bindo = keys: dispatcher: opts: { _args = [ keys dispatcher opts ]; };
# SUPER + 1..9 → workspaces 1..9, SUPER + 0 → workspace 10.
wsKeys = [
{ k = "1"; n = 1; } { k = "2"; n = 2; } { k = "3"; n = 3; }
{ k = "4"; n = 4; } { k = "5"; n = 5; } { k = "6"; n = 6; }
{ k = "7"; n = 7; } { k = "8"; n = 8; } { k = "9"; n = 9; }
{ k = "0"; n = 10; }
];
in
{
services.hyprpaper = {
enable = true;
settings = {
ipc = "on";
splash = false;
preload = [ wallpaper ];
wallpaper = [
{ monitor = "DP-2"; path = wallpaper; }
{ monitor = "HDMI-A-1"; path = wallpaper; }
];
};
};
wayland.windowManager.hyprland = {
enable = true;
# Unloaded for now; re-add together with the plugin.hyprchrome settings below.
# plugins = [ inputs.hypr-chrome.packages.${pkgs.stdenv.hostPlatform.system}.default ];
settings = {
# ---- colours (from colors.conf) ----
fg_color = { _var = "rgba(eeeeeeff)"; };
fg_accent = { _var = "rgba(e8722aff)"; };
fg_accent_alt = { _var = "rgba(ff9d42ff)"; };
bg_color = { _var = "rgba(0f1012ff)"; };
bg_accent = { _var = "rgba(963c38ff)"; };
# ---- monitors ----
monitor = [
{ output = "DP-2"; mode = "2560x1440@144"; position = "1920x0"; scale = 1; }
{ output = "HDMI-A-1"; mode = "1920x1080@60"; position = "0x360"; scale = 1; }
];
# ---- variables (general/decoration/input/misc/...) ----
config = {
input = {
kb_layout = "de";
numlock_by_default = true;
follow_mouse = 1;
sensitivity = 0;
};
debug.disable_logs = false;
general = {
border_size = 1;
col = {
inactive_border = lua "bg_accent";
active_border = {
colors = [ (lua "fg_accent") (lua "fg_accent_alt") ];
angle = 45;
};
};
layout = "dwindle";
gaps_in = 4;
gaps_out = 8;
};
decoration = {
dim_special = 0.3;
rounding = 25;
rounding_power = 1.0;
blur = {
enabled = true;
special = true; # blur behind the special workspace
size = 6;
passes = 2;
ignore_opacity = true;
};
shadow.enabled = false;
active_opacity = 0.9;
inactive_opacity = 0.9;
};
animations.enabled = true;
misc = {
close_special_on_empty = true;
disable_hyprland_logo = true;
disable_splash_rendering = true;
};
binds = {
hide_special_on_workspace_change = true;
workspace_back_and_forth = true;
allow_workspace_cycles = true;
};
# Unloaded for now — Hyprland rejects plugin config for a plugin that is
# not loaded, so this stays commented until it goes back in `plugins` above.
# plugin.hyprchrome = {
# enabled = true;
# glow_size = 12;
# glow_strength = 0.85;
# shadow_size = 24;
# shadow_color = lua "bg_color";
# shadow_offset = lua "{ 4, 8 }";
# outline_size = lua "2";
# outline_color = lua "fg_color";
# };
};
# ---- animations ----
# specialWorkspace, enabled, speed 8, default curve, slidefadevert -50%
animation = [
{ leaf = "specialWorkspace"; enabled = true; speed = 8; bezier = "default"; style = "slidefadevert -50%"; }
];
# ---- environment (environment.conf) ----
env = [
{ _args = [ "HYPRCURSOR_THEME" cursorName ]; }
{ _args = [ "HYPRCURSOR_SIZE" cursorSize ]; }
{ _args = [ "XCURSOR_THEME" cursorName ]; }
{ _args = [ "XCURSOR_SIZE" cursorSize ]; }
{ _args = [ "GDK_BACKEND" "wayland,x11" ]; }
{ _args = [ "SDL_VIDEODRIVER" "wayland" ]; }
{ _args = [ "CLUTTER_BACKEND" "wayland" ]; }
{ _args = [ "XDG_CURRENT_DESKTOP" "Hyprland" ]; }
{ _args = [ "XDG_SESSION_DESKTOP" "Hyprland" ]; }
{ _args = [ "XDG_SESSION_TYPE" "wayland" ]; }
{ _args = [ "QT_QPA_PLATFORMTHEME" "qt6ct" ]; }
];
# ---- keybinds (keybinds.conf) ----
bind = [
(bindo "SUPER + SUPER_L" (dsp.exec "bash $HOME/.config/quickshell/open_launcher.sh") { release = true; })
(bind "SUPER + Return" (dsp.exec "alacritty"))
# quickshell app-launcher variants (evaluating — pick one)
]
++ (map (n: bind "SUPER + CTRL + ${toString n}" (dsp.global "quickshell:launcher${toString n}")) (lib.range 1 9))
++ [
# variant 10 (CTRL+0 is already the quickshell restart binding below)
(bind "SUPER + CTRL + SHIFT + 0" (dsp.global "quickshell:launcher10"))
# variant 11 cyber dock
(bind "SUPER + CTRL + SHIFT + D" (dsp.global "quickshell:launcher11"))
# restart quickshell (also starts it if not running)
(bind "SUPER + CTRL + 0" (dsp.exec "qs kill; sleep 0.3; qs"))
# toggle the Slant sidebar
(bind "SUPER + CTRL + S" (dsp.global "quickshell:sidebar"))
# toggle the host vitals HUD
(bind "SUPER + CTRL + V" (dsp.global "quickshell:vitals"))
# toggle the debug widget stage (centred on the secondary monitor)
(bind "SUPER + CTRL + D" (dsp.global "quickshell:debug"))
# expand / collapse the hyprchrome bar as a whole
(bind "SUPER + A" (dsp.global "quickshell:chrome"))
(bind "SUPER + B" (dsp.exec "vivaldi"))
(bind "SUPER + E" (dsp.exec "cosmic-files"))
(bind "SUPER + SHIFT + G" (dsp.workspaceRef "game"))
(bind "SUPER + S" (dsp.exec "grim -o DP-2 ~/screenshot.png"))
(bind "SUPER + SHIFT + S" (dsp.exec "grimblast copy area --freeze"))
(bind "Print" (dsp.exec "rishot"))
(bind "SUPER + L" (dsp.exec "hyprlock"))
(bind "SUPER + W" (dsp.exec "waypaper --random"))
# window management
(bind "SUPER + Q" dsp.killactive)
(bind "SUPER + SHIFT + Q" dsp.exit)
(bind "SUPER + F" dsp.maximize)
(bind "SUPER + SHIFT + F" dsp.fullscreen)
(bind "SUPER + Space" dsp.togglefloating)
# focus
(bind "SUPER + left" (dsp.movefocus "left"))
(bind "SUPER + right" (dsp.movefocus "right"))
(bind "SUPER + up" (dsp.movefocus "up"))
(bind "SUPER + down" (dsp.movefocus "down"))
# move
(bind "SUPER + SHIFT + left" (dsp.movewindow "left"))
(bind "SUPER + SHIFT + right" (dsp.movewindow "right"))
(bind "SUPER + SHIFT + up" (dsp.movewindow "up"))
(bind "SUPER + SHIFT + down" (dsp.movewindow "down"))
# resize
(bind "SUPER + CTRL + left" (dsp.resizeactive (-20) 0))
(bind "SUPER + CTRL + right" (dsp.resizeactive 20 0))
(bind "SUPER + CTRL + up" (dsp.resizeactive 0 (-20)))
(bind "SUPER + CTRL + down" (dsp.resizeactive 0 20))
# tabbed / group
(bind "SUPER + g" dsp.togglegroup)
(bind "SUPER + tab" dsp.changegroupactive)
# special workspaces
(bind "SUPER + T" (dsp.togglespecial "terminal"))
(bind "SUPER + C" (dsp.togglespecial "communications"))
(bind "SUPER + M" (dsp.togglespecial "music"))
(bind "SUPER + V" (dsp.togglespecial "version_control"))
# cycle workspaces
(bind "SUPER + ALT + up" (dsp.workspaceRef "e+1"))
(bind "SUPER + ALT + down" (dsp.workspaceRef "e-1"))
# mouse move / resize
(bindo "SUPER + mouse:272" dsp.drag { mouse = true; })
(bindo "SUPER + mouse:273" dsp.resizemouse { mouse = true; })
# multimedia (pipewire)
(bindo "XF86AudioRaiseVolume" (dsp.exec "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+") { repeating = true; })
(bindo "XF86AudioLowerVolume" (dsp.exec "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-") { repeating = true; })
(bind "XF86AudioMute" (dsp.exec "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"))
(bind "XF86AudioPlay" (dsp.exec "playerctl play-pause"))
(bind "XF86AudioPause" (dsp.exec "playerctl play-pause"))
(bind "XF86AudioNext" (dsp.exec "playerctl next"))
(bind "XF86AudioPrev" (dsp.exec "playerctl previous"))
]
++ (map (w: bind "SUPER + ${w.k}" (dsp.workspace w.n)) wsKeys)
++ (map (w: bind "SUPER + SHIFT + ${w.k}" (dsp.movetoworkspace w.n)) wsKeys);
# ---- window rules (windowrules.conf) ----
window_rule = [
{ name = "games"; match.class = "gamescope"; workspace = "name:game"; opacity = "1 1 override"; }
{ name = "vivaldi"; match.title = "(.*)(- YouTube - Vivaldi)"; opacity = "1 1 override"; }
{ name = "jetbrains"; match.class = "(jetbrains-)(.*)"; no_initial_focus = true; float = true; center = true; }
{ name = "jetbrains-toolbox"; match.class = "jetbrains-toolbox"; move = "1933 21"; }
{ name = "comms"; match.class = "discord|org.telegram.desktop"; workspace = "special:communications"; }
{ name = "music"; match.class = "qbz"; workspace = "special:music"; }
{ name = "vicinae"; match.class = "vicinae"; stay_focused = true; }
{ name = "gitkraken"; match.class = "gitkraken"; workspace = "special:version_control"; }
];
# ---- workspace rules (workspacerules.conf) ----
workspace_rule = [
{ workspace = "name:game"; monitor = "DP-2"; decorate = false; }
{ workspace = "special:terminal"; on_created_empty = "alacritty"; gaps_out = 256; }
{ workspace = "special:communications"; on_created_empty = "${config.home.homeDirectory}/.config/scripts/start-communications.sh"; gaps_out = 128; }
{ workspace = "special:music"; on_created_empty = "com.blitzfc.qbz"; gaps_out = 128; }
{ workspace = "special:version_control"; on_created_empty = "com.axosoft.GitKraken"; gaps_out = 128; }
];
# ---- autostart (autostart.conf) ----
on = {
_args = [
"hyprland.start"
(lua ''
function()
-- No polkit agent started here: quickshell registers its own
-- (HyprChrome/Widgets/Polkit), and a session admits only one.
hl.exec_cmd("cosmic-settings-daemon")
hl.exec_cmd("quickshell")
hl.exec_cmd("alacritty", { workspace = "special:terminal silent" })
hl.exec_cmd("kbuildsycoca6 --noincremental")
end'')
];
};
};
};
xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-cosmic ];
}