508 lines
18 KiB
Nix
508 lines
18 KiB
Nix
{ modules, ... }:
|
|
{
|
|
modules.hyprland = {
|
|
provides = {
|
|
doloro-settings = {
|
|
includes = [
|
|
modules.dunst
|
|
];
|
|
homeManager =
|
|
{
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
tofiCfg = pkgs.writeTextFile {
|
|
name = "hyprland-tofi-config";
|
|
text = ''
|
|
width = 100%
|
|
height = 100%
|
|
border-width = 0
|
|
outline-width = 0
|
|
padding-left = 35%
|
|
padding-top = 35%
|
|
result-spacing = 25
|
|
num-results = 5
|
|
font = monospace
|
|
background-color = #000A
|
|
'';
|
|
};
|
|
meowScript = pkgs.writeShellScript "meow" ''
|
|
fish -c 'grim -g $(slurp) -t png - &| wl-copy -t image/png; killall wayfreeze'
|
|
'';
|
|
mreowScript = pkgs.writeShellScript "mreow" ''
|
|
exec $(tofi-drun -c ${tofiCfg})
|
|
'';
|
|
|
|
mainMod = "SUPER";
|
|
|
|
# Each `settings.<fn>` list entry renders as `hl.<fn>(<table>)`.
|
|
# `_args` renders a multi-argument call instead of one Lua table,
|
|
# and mkLuaInline passes raw Lua through for dispatchers.
|
|
dsp = lib.generators.mkLuaInline;
|
|
bind = key: dispatcher: {
|
|
_args = [
|
|
key
|
|
dispatcher
|
|
];
|
|
};
|
|
bezier = name: points: {
|
|
_args = [
|
|
name
|
|
{
|
|
type = "bezier";
|
|
inherit points;
|
|
}
|
|
];
|
|
};
|
|
wsBinds = lib.concatLists (
|
|
builtins.genList (
|
|
i:
|
|
let
|
|
ws = toString (i + 1);
|
|
in
|
|
[
|
|
(bind "${mainMod} + ${ws}" (dsp "hl.dsp.focus({ workspace = ${ws} })"))
|
|
(bind "${mainMod} + SHIFT + ${ws}" (dsp "hl.dsp.window.move({ workspace = ${ws} })"))
|
|
]
|
|
) 9
|
|
);
|
|
in
|
|
{
|
|
home.packages = [
|
|
pkgs.hyprcursor
|
|
pkgs.hyprshutdown
|
|
pkgs.tofi
|
|
pkgs.killall
|
|
pkgs.grim
|
|
pkgs.slurp
|
|
pkgs.wayclip
|
|
];
|
|
# wlogout menu. Default layout, except Shutdown hands off to
|
|
# hyprshutdown so apps close gracefully before poweroff.
|
|
# programs.wlogout only accepts one command per button, so
|
|
# --post-cmd carries the actual poweroff.
|
|
programs.wlogout = {
|
|
enable = true;
|
|
layout = [
|
|
{
|
|
label = "lock";
|
|
action = "loginctl lock-session";
|
|
text = "Lock";
|
|
keybind = "l";
|
|
}
|
|
{
|
|
label = "hibernate";
|
|
action = "systemctl hibernate";
|
|
text = "Hibernate";
|
|
keybind = "h";
|
|
}
|
|
{
|
|
label = "logout";
|
|
action = "loginctl terminate-user $USER";
|
|
text = "Logout";
|
|
keybind = "e";
|
|
}
|
|
{
|
|
label = "shutdown";
|
|
action = "hyprshutdown --post-cmd 'systemctl poweroff'";
|
|
text = "Shutdown";
|
|
keybind = "s";
|
|
}
|
|
{
|
|
label = "suspend";
|
|
action = "systemctl suspend";
|
|
text = "Suspend";
|
|
keybind = "u";
|
|
}
|
|
{
|
|
label = "reboot";
|
|
action = "hyprshutdown --post-cmd 'systemctl reboot'";
|
|
text = "Reboot";
|
|
keybind = "r";
|
|
}
|
|
];
|
|
};
|
|
home.pointerCursor = {
|
|
enable = true;
|
|
name = "rose-pine-hyprcursor";
|
|
package = pkgs.rose-pine-hyprcursor;
|
|
hyprcursor.enable = true;
|
|
};
|
|
services.hyprpaper = {
|
|
enable = true;
|
|
settings = {
|
|
splash = true;
|
|
wallpaper = [
|
|
{
|
|
monitor = "HDMI-A-1";
|
|
path = "/home/doloro/Wallpaper.jpg";
|
|
}
|
|
{
|
|
monitor = "DP-2";
|
|
path = "/home/doloro/Wallpaper.jpg";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
# The systemd session activation hooks (hyprland-session.target
|
|
# start/stop + dbus-update-activation-environment --systemd --all)
|
|
# are still generated by Home Manager; only the imperative tail of
|
|
# the config needs raw Lua.
|
|
wayland.windowManager.hyprland = {
|
|
settings = {
|
|
config = {
|
|
general = {
|
|
gaps_in = 1;
|
|
gaps_out = 1;
|
|
border_size = 1;
|
|
resize_on_border = false;
|
|
allow_tearing = true;
|
|
layout = "dwindle";
|
|
};
|
|
input = {
|
|
follow_mouse = 2;
|
|
kb_layout = "gb";
|
|
};
|
|
decoration = {
|
|
rounding = 4;
|
|
rounding_power = 1;
|
|
active_opacity = 1.0;
|
|
inactive_opacity = 1.0;
|
|
shadow.enabled = false;
|
|
blur = {
|
|
enabled = true;
|
|
passes = 1;
|
|
new_optimizations = true;
|
|
ignore_opacity = false;
|
|
};
|
|
};
|
|
cursor.default_monitor = "DP-2";
|
|
cursor.no_hardware_cursors = false;
|
|
dwindle.preserve_split = true;
|
|
misc = {
|
|
force_default_wallpaper = -1;
|
|
disable_hyprland_logo = false;
|
|
enable_anr_dialog = false;
|
|
};
|
|
};
|
|
|
|
monitor = [
|
|
{
|
|
output = "HDMI-A-1";
|
|
mode = "1920x1080@60";
|
|
position = "0x0";
|
|
scale = "1";
|
|
}
|
|
{
|
|
output = "DP-2";
|
|
mode = "1920x1080@120";
|
|
position = "1920x0";
|
|
scale = "1";
|
|
}
|
|
];
|
|
workspace_rule = [
|
|
{
|
|
workspace = "2";
|
|
monitor = "DP-2";
|
|
default = true;
|
|
}
|
|
]
|
|
++ builtins.genList (i: {
|
|
workspace = toString (i + 2);
|
|
monitor = "DP-2";
|
|
}) 9;
|
|
|
|
device = {
|
|
name = "default";
|
|
kb_layout = "gb";
|
|
sensitivity = -0.5;
|
|
};
|
|
|
|
# Curves are sorted ahead of animations by the module's
|
|
# `importantPrefixes` default, so the beziers exist by name.
|
|
curve = [
|
|
(bezier "easeOutQuint" [
|
|
[
|
|
0.23
|
|
1
|
|
]
|
|
[
|
|
0.32
|
|
1
|
|
]
|
|
])
|
|
(bezier "easeInOutCubic" [
|
|
[
|
|
0.65
|
|
0.05
|
|
]
|
|
[
|
|
0.36
|
|
1
|
|
]
|
|
])
|
|
(bezier "linear" [
|
|
[
|
|
0
|
|
0
|
|
]
|
|
[
|
|
1
|
|
1
|
|
]
|
|
])
|
|
(bezier "almostLinear" [
|
|
[
|
|
0.5
|
|
0.5
|
|
]
|
|
[
|
|
0.75
|
|
1
|
|
]
|
|
])
|
|
(bezier "quick" [
|
|
[
|
|
0.15
|
|
0
|
|
]
|
|
[
|
|
0.1
|
|
1
|
|
]
|
|
])
|
|
];
|
|
|
|
animation = [
|
|
{
|
|
leaf = "global";
|
|
enabled = true;
|
|
speed = 10;
|
|
bezier = "default";
|
|
}
|
|
{
|
|
leaf = "border";
|
|
enabled = true;
|
|
speed = 5.39;
|
|
bezier = "easeOutQuint";
|
|
}
|
|
{
|
|
leaf = "windows";
|
|
enabled = true;
|
|
speed = 2.79;
|
|
bezier = "easeOutQuint";
|
|
}
|
|
{
|
|
leaf = "windowsIn";
|
|
enabled = true;
|
|
speed = 1;
|
|
bezier = "easeOutQuint";
|
|
style = "popin 87%";
|
|
}
|
|
{
|
|
leaf = "windowsOut";
|
|
enabled = true;
|
|
speed = 1;
|
|
bezier = "linear";
|
|
style = "popin 87%";
|
|
}
|
|
{
|
|
leaf = "windowsMove";
|
|
enabled = true;
|
|
speed = 1;
|
|
bezier = "easeInOutCubic";
|
|
}
|
|
{
|
|
leaf = "fadeIn";
|
|
enabled = true;
|
|
speed = 0.5;
|
|
bezier = "almostLinear";
|
|
}
|
|
{
|
|
leaf = "fadeOut";
|
|
enabled = true;
|
|
speed = 0.5;
|
|
bezier = "almostLinear";
|
|
}
|
|
{
|
|
leaf = "fade";
|
|
enabled = true;
|
|
speed = 1;
|
|
bezier = "quick";
|
|
}
|
|
{
|
|
leaf = "layers";
|
|
enabled = true;
|
|
speed = 3.81;
|
|
bezier = "easeOutQuint";
|
|
}
|
|
{
|
|
leaf = "layersIn";
|
|
enabled = true;
|
|
speed = 4;
|
|
bezier = "easeOutQuint";
|
|
style = "fade";
|
|
}
|
|
{
|
|
leaf = "layersOut";
|
|
enabled = true;
|
|
speed = 1.5;
|
|
bezier = "linear";
|
|
style = "fade";
|
|
}
|
|
{
|
|
leaf = "fadeLayersIn";
|
|
enabled = true;
|
|
speed = 1.79;
|
|
bezier = "almostLinear";
|
|
}
|
|
{
|
|
leaf = "fadeLayersOut";
|
|
enabled = true;
|
|
speed = 1.39;
|
|
bezier = "almostLinear";
|
|
}
|
|
{
|
|
leaf = "workspaces";
|
|
enabled = true;
|
|
speed = 1;
|
|
bezier = "almostLinear";
|
|
style = "slide";
|
|
}
|
|
{
|
|
leaf = "workspacesIn";
|
|
enabled = true;
|
|
speed = 1;
|
|
bezier = "easeInOutCubic";
|
|
style = "slide";
|
|
}
|
|
{
|
|
leaf = "workspacesOut";
|
|
enabled = true;
|
|
speed = 1;
|
|
bezier = "easeInOutCubic";
|
|
style = "slide";
|
|
}
|
|
{
|
|
leaf = "zoomFactor";
|
|
enabled = true;
|
|
speed = 7;
|
|
bezier = "quick";
|
|
}
|
|
];
|
|
|
|
bind = [
|
|
(bind "${mainMod} + R" (dsp ''hl.dsp.exec_cmd("${mreowScript}")''))
|
|
(bind "${mainMod} + C" (dsp "hl.dsp.window.close()"))
|
|
(bind "${mainMod} + M" (dsp ''hl.dsp.exec_cmd("uwsm stop")''))
|
|
(bind "${mainMod} + Q" (dsp ''hl.dsp.exec_cmd("kitty")''))
|
|
(bind "${mainMod} + V" (dsp ''hl.dsp.window.float({ action = "toggle" })''))
|
|
(bind "${mainMod} + left" (dsp ''hl.dsp.focus({ direction = "left" })''))
|
|
(bind "${mainMod} + right" (dsp ''hl.dsp.focus({ direction = "right" })''))
|
|
(bind "${mainMod} + up" (dsp ''hl.dsp.focus({ direction = "up" })''))
|
|
(bind "${mainMod} + down" (dsp ''hl.dsp.focus({ direction = "down" })''))
|
|
(bind "${mainMod} + L" (dsp ''hl.dsp.exec_cmd("wlogout")''))
|
|
(bind "${mainMod} + S" (dsp ''hl.dsp.exec_cmd("wayfreeze --after-freeze-cmd '${meowScript}'")''))
|
|
(bind "${mainMod} + F" (dsp ''hl.dsp.window.fullscreen({ action = "toggle" })''))
|
|
(bind "${mainMod} + SHIFT + Q" (dsp "hl.dsp.window.close()"))
|
|
(bind "${mainMod} + mouse_down" (dsp ''hl.dsp.focus({ workspace = "e+1" })''))
|
|
(bind "${mainMod} + mouse_up" (dsp ''hl.dsp.focus({ workspace = "e-1" })''))
|
|
|
|
# Mouse binds
|
|
(bind "${mainMod} + mouse:272" (dsp "hl.dsp.window.drag()"))
|
|
(bind "${mainMod} + mouse:273" (dsp "hl.dsp.window.resize()"))
|
|
|
|
# Media keys
|
|
(bind "XF86AudioLowerVolume" (dsp ''hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-")''))
|
|
(bind "XF86AudioRaiseVolume" (dsp ''hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+")''))
|
|
(bind "XF86AudioMute" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle")''))
|
|
(bind "XF86AudioPlay" (dsp ''hl.dsp.exec_cmd("playerctl play-pause")''))
|
|
(bind "XF86AudioNext" (dsp ''hl.dsp.exec_cmd("playerctl next")''))
|
|
(bind "XF86AudioPrev" (dsp ''hl.dsp.exec_cmd("playerctl previous")''))
|
|
(bind "XF86AudioMicMute" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle")''))
|
|
(bind "home" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle")''))
|
|
(bind "end" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle")''))
|
|
(bind "F8" (dsp "hl.dsp.pass({ window = [[match:class:^(com.obsproject.Studio)$]] })"))
|
|
|
|
# Workspaces
|
|
(bind "${mainMod} + 0" (dsp "hl.dsp.focus({ workspace = 10 })"))
|
|
(bind "${mainMod} + SHIFT + 0" (dsp "hl.dsp.window.move({ workspace = 10 })"))
|
|
]
|
|
++ wsBinds;
|
|
|
|
window_rule = [
|
|
{ suppress_event = "maximize"; }
|
|
{
|
|
match.class = "gamescope";
|
|
workspace = "5";
|
|
immediate = true;
|
|
confine_pointer = true;
|
|
}
|
|
{
|
|
match.initial_title = "OBS Studio Crash Detected";
|
|
pin = true;
|
|
}
|
|
# Steam, Discord (equibop), Telegram, Spotify and OBS
|
|
# always open on their own workspace regardless of how
|
|
# they were launched. These sit before the "Discord
|
|
# Popout" rule: window rules apply in declaration order
|
|
# and the last match wins, so popouts still land on
|
|
# workspace 1.
|
|
{
|
|
match.class = "steam";
|
|
workspace = "6 silent";
|
|
}
|
|
{
|
|
match.class = "equibop";
|
|
workspace = "8 silent";
|
|
}
|
|
{
|
|
match.class = "org.telegram.desktop";
|
|
workspace = "8 silent";
|
|
}
|
|
{
|
|
match.class = "spotify";
|
|
workspace = "9 silent";
|
|
}
|
|
{
|
|
match.class = "com.obsproject.Studio";
|
|
workspace = "10 silent";
|
|
}
|
|
{
|
|
match.initial_title = "Discord Popout";
|
|
workspace = "1 silent";
|
|
}
|
|
];
|
|
|
|
layer_rule = [
|
|
{
|
|
match.namespace = "notifications";
|
|
no_screen_share = true;
|
|
}
|
|
];
|
|
};
|
|
|
|
# Host-specific overrides: load ~/.config/hypr/hyprland-local.lua
|
|
# if present (must be valid Lua). Runs last so a host can override
|
|
# anything set above.
|
|
extraConfig = ''
|
|
local home = os.getenv("HOME")
|
|
if home then
|
|
local localPath = home .. '/.config/hypr/hyprland-local.lua'
|
|
local f = io.open(localPath, 'r')
|
|
if f then
|
|
f:close()
|
|
local chunk, err = loadfile(localPath)
|
|
if chunk then chunk() else io.stderr:write('hyprland-local.lua error: ' .. err .. '\n') end
|
|
end
|
|
end
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|