nix-dotfiles/modules/desktop/hyprland.nix
2025-01-02 16:19:49 -06:00

412 lines
15 KiB
Nix

{ inputs, lib, config, system, pkgs, ... }:
with lib;
let
cfg = config.modules.desktop.hyprland;
hyprpkgs = inputs.hyprland.packages.${system};
in {
options.modules.desktop.hyprland = {
enable = mkEnableOption "Enable hyprland, a dynamic tiling wayland compositor based on wlroots that doesn't sacrifice on its looks";
package = mkOption {
type = types.package;
default = hyprpkgs.hyprland;
example = "pkgs.hyprland";
};
portalPackage = mkOption {
type = types.package;
default = hyprpkgs.xdg-desktop-portal-hyprland;
example = "pkgs.xdg-desktop-portal-hyprland";
};
};
config = mkIf cfg.enable {
services.displayManager.sessionPackages = [ cfg.package ];
xdg.portal = {
enable = true;
extraPortals = [ pkgs.xdg-desktop-portal-gtk cfg.portalPackage ];
config = {
common = {
default = [ "hyprland" "gtk" ];
};
};
};
# fixes a few things, most notibly, xwayland
programs.hyprland = {
enable = true;
xwayland.enable = true;
package = cfg.package;
portalPackage = cfg.portalPackage;
};
hm.wayland.windowManager.hyprland = {
enable = true;
xwayland.enable = true;
package = cfg.package;
systemd.variables = [ "--all" ];
settings = let
wobSock = config.modules.desktop.wob.sockPath;
in {
source = [];
"$mod" = "SUPER";
bindm = [ # "bind mouse"
# move/resize windows with mod + lmb/rmb and dragging
"$mod, mouse:272, movewindow"
"$mod, mouse:273, resizewindow"
];
bindel = (if config.modules.desktop.wob.enable then [ # "bind held & locked"
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ && wpctl get-volume @DEFAULT_AUDIO_SINK@ | sed 's/[^0-9]//g' > ${wobSock}"
", XF86AudioLowerVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%- && wpctl get-volume @DEFAULT_AUDIO_SINK@ | sed 's/[^0-9]//g' > ${wobSock}"
", XF86MonBrightnessUp, exec, ${lib.getExe pkgs.brightnessctl} -c backlight s +5% | sed -n 's/.*(\\([0-9]*\\)%).*/\\1/p' > ${wobSock}"
", XF86MonBrightnessDown, exec, ${lib.getExe pkgs.brightnessctl} -c backlight s 5%- | sed -n 's/.*(\\([0-9]*\\)%).*/\\1/p' > ${wobSock}"
", XF86KbdBrightnessUp, exec, ${lib.getExe pkgs.brightnessctl} -d '*:kbd_backlight' s +5% | sed -n 's/.*(\\([0-9]*\\)%).*/\\1/p' > ${wobSock}"
", XF86KbdBrightnessDown, exec, ${lib.getExe pkgs.brightnessctl} -d '*:kbd_backlight' s 5%- | sed -n 's/.*(\\([0-9]*\\)%).*/\\1/p' > ${wobSock}"
] else [
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"
", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
", XF86MonBrightnessUp, exec, ${lib.getExe pkgs.brightnessctl} -c backlight s +5%"
", XF86MonBrightnessDown, exec, ${lib.getExe pkgs.brightnessctl} -c backlight s 5%-"
", XF86KbdBrightnessUp, exec, ${lib.getExe pkgs.brightnessctl} -d '*:kbd_backlight' s +5%"
", XF86KbdBrightnessDown, exec, ${lib.getExe pkgs.brightnessctl} -d '*:kbd_backlight' s 5%-"
]);
bindl = ([ # "bind locked"
",switch:Lid Switch,exec,${lib.getExe config.modules.desktop.hyprlock.package}"
", XF86AudioPlay, exec, ${lib.getExe pkgs.playerctl} play-pause"
", XF86AudioStop, exec, ${lib.getExe pkgs.playerctl} stop"
", XF86AudioPrev, exec, ${lib.getExe pkgs.playerctl} previous"
", XF86AudioNext, exec, ${lib.getExe pkgs.playerctl} next"
", XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
] ++ (if config.modules.desktop.wob.enable then [
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle && (wpctl get-volume @DEFAULT_AUDIO_SINK@ | grep -q MUTED && echo 0 > ${wobSock}) || wpctl get-volume @DEFAULT_AUDIO_SINK@ | sed 's/[^0-9]//g' > ${wobSock}"
] else [
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
]));
bindr = [ # "bind released"
"SUPER, Super_L, exec, ${lib.getExe config.modules.desktop.nwg-drawer.package}"
];
bind = let
screenshotScript = pkgs.writeShellScript "screenshot" ''
set -euo pipefail
temp_file=$(mktemp)
trap 'rm -f "$temp_file"' EXIT
mode="$1"
case "$mode" in
screen)
mode="output"
;;
area)
mode="area"
;;
window)
mode="active"
;;
*)
echo "usage: $0 [screen|area|window]" >&2
exit 2
;;
esac
${lib.getExe pkgs.grimblast} --freeze save "$mode" - > "$temp_file"
cat "$temp_file" | ${lib.getExe pkgs.wl-clipboard-x11} -selection clipboard -t image/png
action=$(${pkgs.libnotify}/bin/notify-send "Screenshot Captured" \
--app-name="Screenshot" \
--urgency="normal" \
--action="default=Edit Image" \
--icon="$temp_file" \
"Click to annotate image")
case "$action" in
default)
${lib.getExe pkgs.satty} --filename "$temp_file"
;;
esac
'';
pickerScript = pkgs.writeShellScript "picker" ''
set -euo pipefail
${lib.getExe pkgs.hyprpicker} --render-inactive --format hex | sed -z 's/\n//g' | ${lib.getExe pkgs.wl-clipboard-x11} -selection clipboard -t text/plain
${pkgs.libnotify}/bin/notify-send "Color Captured" \
--app-name="Picker" \
--urgency="normal" \
"Color copied to clipboard"
'';
ocrScript = pkgs.writeShellScript "ocr" ''
set -euo pipefail
output=$(${lib.getExe pkgs.grimblast} --freeze save area - | ${lib.getExe pkgs.tesseract5} - -)
echo "$output" | ${lib.getExe pkgs.wl-clipboard-x11} -selection clipboard -t text/plain
${pkgs.libnotify}/bin/notify-send "OCR Captured" \
--app-name="OCR" \
--urgency="normal" \
--icon="text-x-generic-symbolic" \
"Text copied to clipboard"
'';
in [
"$mod, Q, killactive, "
"$mod, F, togglefloating, "
"$mod, P, pseudo, " # dwindle
"$mod, J, togglesplit, " # dwindle
"$mod SHIFT, F, fullscreen, "
# move focus with mod + arrow keys
"$mod, left, movefocus, l"
"$mod, right, movefocus, r"
"$mod, up, movefocus, u"
"$mod, down, movefocus, d"
# move windows with mod + shift + arrow keys
"$mod SHIFT, left, movewindow, l"
"$mod SHIFT, right, movewindow, r"
"$mod SHIFT, up, movewindow, u"
"$mod SHIFT, down, movewindow, d"
# example special workspace (scratchpad)
"$mod, grave, togglespecialworkspace, magic"
"$mod SHIFT, grave, movetoworkspace, special:magic"
# scroll through workspaces with mod + scroll
"$mod, mouse_down, workspace, e+1"
"$mod, mouse_up, workspace, e-1"
# alternatively, scroll through workspaces with mod + brackets
"$mod, bracketleft, workspace, e-1"
"$mod, bracketright, workspace, e+1"
"$mod, R, exec, ${lib.getExe config.modules.desktop.rofi.package} -show run"
"$mod, T, exec, ${lib.getExe config.modules.software.system.wezterm.package}"
"$mod, L, exec, ${lib.getExe config.modules.desktop.hyprlock.package}"
"$mod, V, exec, ${lib.getExe config.modules.software.system.wezterm.package} start --class 'clipse' -e '${lib.getExe config.modules.desktop.clipse.package}'"
# screenshot
# area
", Print, exec, ${screenshotScript} area"
"$mod SHIFT, S, exec, ${screenshotScript} area"
# screen
"CTRL, Print, exec, ${screenshotScript} screen"
"$mod SHIFT CTRL, S, exec, ${screenshotScript} screen"
# window
"ALT, Print, exec, ${screenshotScript} window"
"$mod SHIFT ALT, S, exec, ${screenshotScript} window"
# pick color
"$mod SHIFT, C, exec, ${pickerScript}"
# ocr
"$mod SHIFT, T, exec, ${ocrScript}"
", XF86LaunchA, exec, ${screenshotScript} area"
", XF86LaunchB, exec, ${lib.getExe pkgs.rofi-rbw-wayland} -a copy -t password --clear-after 20"
", XF86ScreenSaver, exec, ${lib.getExe config.modules.desktop.hyprlock.package}"
] ++ (
# workspaces
# binds $mod + [shift +] {1..10} to [move to] workspace {1..10}
builtins.concatLists (builtins.genList (
x: let
ws = let
c = (x + 1) / 10;
in
builtins.toString (x + 1 - (c * 10));
in [
"$mod, ${ws}, workspace, ${toString (x + 1)}"
"$mod SHIFT, ${ws}, movetoworkspace, ${toString (x + 1)}"
]
)
10)
);
input = {
kb_layout = "us";
follow_mouse = 1;
# uhm?? why do i have to do this here. should be automatic from libinput
# whatever. ill just hardcode for now
# TODO
touchpad = {
disable_while_typing = false;
natural_scroll = true;
};
};
monitor=",highrr,auto,auto";
exec-once = [
"${lib.getExe pkgs.networkmanagerapplet}" # network applet
"${lib.getExe pkgs.wl-clip-persist} --clipboard regular" # to fix wl clipboards disappearing
];
env = [
"XCURSOR_THEME,${config.modules.desktop.themes.cursorTheme.name}"
"XCURSOR_SIZE,${toString config.modules.desktop.themes.cursorTheme.size}"
];
general = {
gaps_in = 6;
gaps_out = 6;
border_size = 1;
no_border_on_floating = false;
layout = "dwindle";
resize_on_border = true;
};
windowrulev2 = [
# TODO: dedupe
# commons
"float, class:^org.gnome.FileRoller$"
"size 1100 650, class:^org.gnome.FileRoller$"
"center, class:^org.gnome.FileRoller$"
"float, class:^org.gnome.Loupe$"
"size 1100 650, class:^org.gnome.Loupe$"
"center class:^org.gnome.Loupe$"
"float, class:^com.gabm.satty$"
"size 1100 650, class:^com.gabm.satty$"
"center 1100 650, class:^com.gabm.satty$"
"float, initialTitle:^Open Folder$"
"size 1100 650, initialTitle:^Open Folder$"
"center, initialTitle:^Open Folder$"
"float, initialTitle:^Open File$"
"size 1100 650, initialTitle:^Open File$"
"center, initialTitle:^Open File$"
"float, initialTitle:^Open Files$"
"size 1100 650, initialTitle:^Open Files$"
"center, initialTitle:^Open Files$"
"float, initialTitle:^Save As$"
"size 1100 650, initialTitle:^Save As$"
"center, initialTitle:^Save As$"
"float, initialTitle:^File Upload$"
"size 1100 650, initialTitle:^File Upload$"
"center, initialTitle:^File Upload$"
"float, initialTitle:^MainPicker$"
"size 1100 650, initialTitle:^MainPicker$"
"center, initialTitle:^MainPicker$"
"float, class:^clipse$"
"size 1100 650, class:^clipse$"
"center, class:^clipse$"
# privacy popups
"stayfocused, class:^clipse$"
"dimaround, class:^clipse$"
"stayfocused, class:^gcr-prompter$"
"dimaround, class:^gcr-prompter$"
"stayfocused, class:^pinentry-"
"dimaround, class:^pinentry-"
"stayfocused, class:^polkit-"
"dimaround, class:^polkit-"
"float, class:\.exe$"
"rounding 0, class:\.exe$"
# steam notifs
"rounding 0, title:^notificationtoasts_"
"pin, title:^notificationtoasts_"
"noborder, title:^notificationtoasts_"
"nofocus, title:^notificationtoasts_"
"noshadow, title:^notificationtoasts_"
# firefox pip
"float, title:^Picture-in-Picture$"
"pin, title:^Picture-in-Picture$"
"noborder, title:^Picture-in-Picture$"
"keepaspectratio, title:^Picture-in-Picture$"
# discord pip
"pin, initialTitle:^Discord Popout$"
"float, initialTitle:^Discord Popout$"
"noborder, initialTitle:^Discord Popout$"
# workspace moving
"workspace 1 silent, class:^firefox$"
"workspace 2 silent, class:^code$"
"workspace 4 silent, class:^vesktop$"
];
layerrule = [
"animation slide, notifications"
"animation slide, waybar"
"animation popin, rofi"
"dimaround, rofi"
"blur, nwg-drawer"
"blur, wob"
"blur, notifications"
"blur, waybar"
"blur, rofi"
"ignorezero, notifications" # fucks up the blur otherwise, rounded corners
"ignorezero, waybar"
"ignorezero, rofi"
];
decoration = {
rounding = 10;
blur = {
enabled = true;
size = 6;
passes = 3;
# popups and special workspace have blur disabled by default
popups = true;
special = true;
};
shadow = {
enabled = true;
range = 6;
render_power = 3;
ignore_window = true;
};
};
animations = {
enabled = true;
bezier = [
"outCubic, 0.33, 1, 0.68, 1"
"outExpo, 0.16, 1, 0.3, 1"
];
animation = [
"windows, 1, 5, outExpo, popin"
"windowsOut, 1, 5, outCubic, popin 80%"
"border, 1, 2, outExpo"
"fade, 1, 3, outCubic"
"workspaces, 1, 6, outExpo"
"specialWorkspace, 1, 2, outCubic, fade"
];
};
dwindle = {
pseudotile = "yes"; # master switch for pseudotiling. enabling is mod + P in the keybinds section
preserve_split = "yes";
};
gestures = {
workspace_swipe = true;
workspace_swipe_min_speed_to_force = 0;
workspace_swipe_cancel_ratio = 0.1;
};
misc = {
# disable default eye candy
force_default_wallpaper = 0;
disable_splash_rendering = true;
disable_hyprland_logo = true;
# there is zero reason to have this enabled
# what were they thinking
initial_workspace_tracking = 0;
vfr = true;
vrr = 2;
};
};
};
};
}