{ pkgs, config, lib, self, osConfig, theme, ... }: let cfg = config.my.graphical.wayland.hyprland; in { options.my.graphical.wayland.hyprland = { enable = lib.mkEnableOption null; tearing = lib.mkEnableOption null; hyprcursor = lib.mkEnableOption null // { default = true; }; mainMod = lib.mkOption { type = lib.types.str; default = "SUPER"; }; lockBinary = lib.mkOption { type = lib.types.str; }; wallpaper = { enable = lib.mkEnableOption null // { default = true; }; backend = lib.mkOption { type = lib.types.enum [ "hyprpaper" "swaybg" ]; default = "swaybg"; }; image = lib.mkOption { type = lib.types.pathInStore; default = "${self}/assets/snowyMountain.jpg"; }; }; }; config = lib.mkIf cfg.enable (let toggleOption = pkgs.writeShellScriptBin "hypr-toggle-opt" '' hyprctl keyword "$1" "$(hyprctl getoption "$1" -j | ${pkgs.jaq}/bin/jaq '1 - .int')" ''; copyOutput = pkgs.writeShellScript "hypr-copy-output" '' MONITOR="$(hyprctl monitors -j | ${pkgs.jaq}/bin/jaq -r '.[] | select(.focused).name')" grim -o "$MONITOR" - | wl-copy -t image/png ''; copyWindow = pkgs.writeShellScript "hypr-copy-window" '' WINDOW="$(hyprctl activewindow -j | ${pkgs.jaq}/bin/jaq -r '.address')" grim -w "$WINDOW" - | wl-copy -t image/png ''; winShiftS = with pkgs; writeShellScriptBin "win-shift-s" '' FILENAME="/tmp/win-shift-s-$PPID.png" MONITOR="$(hyprctl monitors -j | ${_ jaq} -r '.[] | select(.focused)')" ${_ grim} -o "$(${_ jaq} -r '.name' <<< "$MONITOR")" "$FILENAME" ${_ imv} -f "$FILENAME" & IMV_PROC=$! DIMENSIONS="$(${_ slurp} -f '%wx%h+%X+%Y')" [ "$?" -eq 0 ] && \ ${_ imagemagick} "$FILENAME" -crop "$DIMENSIONS" - | \ ${wl-clipboard}/bin/wl-copy -t image/png kill $IMV_PROC exec rm "$FILENAME" ''; in { home.packages = [ toggleOption ]; wayland.windowManager.hyprland = let c = builtins.mapAttrs (_: t: "0xFF${t}") theme; inherit (cfg) mainMod; activeBorder = "${c.peach} ${c.mauve} 45deg"; transparent = "0x00000000"; in { enable = true; package = osConfig.programs.hyprland.package; settings = { exec-once = lib.optional (!osConfig.my.fs.luks.enable) "${pkgs.writeShellScript "start-hyprland" '' [ -n "$GREETD_SOCK" ] && ${pkgs.swaylock-effects}/bin/swaylock ${ lib.optionalString cfg.wallpaper.enable "-i ${cfg.wallpaper.image}" } & ''}"; env = [ ] ++ lib.optional config.my.graphical.emacs.enable "VISUAL, emacsclient -c"; monitor = [ "eDP-1, preferred, 0x0, 1.33" ", preferred, auto, 1" # Manually reserved area for bar ", addreserved, 42, 0, 0, 0" ]; input = { kb_layout = "us"; kb_options = "numpad:mac"; repeat_delay = 300; repeat_rate = 20; float_switch_override_focus = 2; touchpad = { natural_scroll = true; scroll_factor = 0.3; }; }; general = { layout = "dwindle"; allow_tearing = cfg.tearing; gaps_in = 5; gaps_out = 13; border_size = 3; "col.active_border" = activeBorder; "col.inactive_border" = transparent; }; dwindle = { force_split = 2; preserve_split = true; special_scale_factor = 0.9; }; decoration = { rounding = 11; dim_special = 0; blur = { enabled = false; size = 8; passes = 2; ignore_opacity = true; xray = true; noise = 0; contrast = 1; brightness = 1; }; shadow = { enabled = false; }; }; group = { "col.border_active" = activeBorder; "col.border_inactive" = transparent; "col.border_locked_active" = c.red; "col.border_locked_inactive" = transparent; groupbar = { render_titles = false; height = 3; "col.active" = c.green; "col.inactive" = transparent; "col.locked_active" = c.red; "col.locked_inactive" = transparent; }; }; animations = { enabled = true; bezier = [ "easeInSine, 0.12, 0, 0.39, 0" "easeOutSine, 0.61, 1, 0.88, 1" ]; animation = [ "windowsIn, true, 2, easeOutSine, slide" "windowsOut, true, 2, easeInSine, slide" "windowsMove, true, 1.5, easeOutSine, slide" "workspaces, true, 1, easeOutSine, slide" "specialWorkspace, true, 3, default, slidevert" "fade, 0" ]; }; gestures = { workspace_swipe = true; }; binds = { allow_workspace_cycles = true; }; xwayland = { use_nearest_neighbor = false; force_zero_scaling = true; }; misc = { vrr = 1; disable_hyprland_logo = true; disable_splash_rendering = true; background_color = c.crust; disable_autoreload = true; focus_on_activate = true; animate_manual_resizes = false; animate_mouse_windowdragging = false; new_window_takes_over_fullscreen = 2; enable_swallow = false; swallow_regex = "^(foot(client)?|kitty)$"; }; workspace = [ "s[false] w[t1], gapsout:0" "s[false] f[1], gapsout:0" ]; windowrulev2 = [ "noborder, onworkspace:s[false] w[t1], floating:false" "norounding, onworkspace:s[false] w[t1], floating:false" "noborder, onworkspace:s[false] f[1], floating:false" "norounding, onworkspace:s[false] f[1], floating:false" "tile, class:^(Aseprite)$" "fullscreen, class:^(Baba Is You|ADanceOfFireAndIce)$" "float, class:^(EverestSplash-linux)$" "float, title:^(emacs-everywhere)$" "float, class:^(Eddie)$, title:^$" "float, class:^(firefox)$, title:^(Picture-in-Picture)$" "pin, class:^(firefox)$, title:^(Picture-in-Picture)$" "tile, class:^(Spotify)$, title:^(?!Ozone X11$).*$" "float, class:^(Spotify)$, title:^$" ] ++ lib.optional cfg.tearing "immediate, class:^(ADanceOfFireAndIce|Celeste|hollow_knight\\.x86_64|portal2_linux)$"; layerrule = [ "noanim, ^(notifications)$" ]; bind = [ "${mainMod}, Return, exec, foot" "${mainMod}, C, killactive," "${mainMod} SHIFT, C, exec, hyprctl activewindow -j | ${pkgs.jaq}/bin/jaq -r '.pid' | xargs kill -9" "${mainMod}, E, exec, nautilus" "${mainMod}, M, exec, emacsclient -c" "${mainMod} SHIFT, M, exec, emacsclient -e '(emacs-everywhere)'" "${mainMod} SHIFT, Return, togglefloating," "${mainMod}, Space, fullscreen, 1" "${mainMod} SHIFT, Space, fullscreen, 0" "${mainMod}, I, exec, dunstctl action" "${mainMod} SHIFT, I, exec, dunstctl context" "${mainMod} ALT, I, exec, dunstctl history-pop" "${mainMod} CTRL, I, exec, dunstctl close" "${mainMod}, Semicolon, exec, killall fuzzel || fuzzel" "${mainMod}, Y, pin," # Groups "${mainMod}, F, changegroupactive, f" "${mainMod} SHIFT, F, togglegroup," "${mainMod} CTRL, F, lockactivegroup, toggle" # Directions "${mainMod}, Period, layoutmsg, preselect r" "${mainMod}, Comma, layoutmsg, preselect d" "${mainMod}, S, layoutmsg, togglesplit" # Moving windows (with groups) "${mainMod} SHIFT, left, movewindoworgroup, l" "${mainMod} SHIFT, right, movewindoworgroup, r" "${mainMod} SHIFT, up, movewindoworgroup, u" "${mainMod} SHIFT, down, movewindoworgroup, d" "${mainMod} SHIFT, H, movewindoworgroup, l" "${mainMod} SHIFT, L, movewindoworgroup, r" "${mainMod} SHIFT, K, movewindoworgroup, u" "${mainMod} SHIFT, J, movewindoworgroup, d" # Moving windows (without groups) "${mainMod} ALT, left, movewindow, l" "${mainMod} ALT, right, movewindow, r" "${mainMod} ALT, up, movewindow, u" "${mainMod} ALT, down, movewindow, d" "${mainMod} ALT, H, movewindow, l" "${mainMod} ALT, L, movewindow, r" "${mainMod} ALT, K, movewindow, u" "${mainMod} ALT, J, movewindow, d" # Workspaces "${mainMod} CTRL, P, focusworkspaceoncurrentmonitor, previous" "${mainMod} CTRL, N, focusworkspaceoncurrentmonitor, empty" "${mainMod} CTRL SHIFT, P, movetoworkspace, previous" "${mainMod} CTRL SHIFT, N, movetoworkspace, empty" # Screenshot ", Print, exec, ${copyOutput}" "ALT, Print, exec, ${copyWindow}" "${mainMod} SHIFT, S, exec, grim -g \"$(slurp)\" - | wl-copy -t image/png" # Move focus "${mainMod}, left, movefocus, l" "${mainMod}, right, movefocus, r" "${mainMod}, up, movefocus, u" "${mainMod}, down, movefocus, d" "${mainMod}, H, movefocus, l" "${mainMod}, L, movefocus, r" "${mainMod}, K, movefocus, u" "${mainMod}, J, movefocus, d" # Switch workspaces with mainMod + [1-9] "${mainMod}, 1, focusworkspaceoncurrentmonitor, 1" "${mainMod}, 2, focusworkspaceoncurrentmonitor, 2" "${mainMod}, 3, focusworkspaceoncurrentmonitor, 3" "${mainMod}, 4, focusworkspaceoncurrentmonitor, 4" "${mainMod}, 5, focusworkspaceoncurrentmonitor, 5" "${mainMod}, 6, focusworkspaceoncurrentmonitor, 6" "${mainMod}, 7, focusworkspaceoncurrentmonitor, 7" "${mainMod}, 8, focusworkspaceoncurrentmonitor, 8" "${mainMod}, 9, focusworkspaceoncurrentmonitor, 9" # Switch workspaces with mainMod + N/P/O "${mainMod}, N, focusworkspaceoncurrentmonitor, e+1" "${mainMod}, P, focusworkspaceoncurrentmonitor, e-1" "${mainMod} SHIFT, N, movetoworkspace, e+1" "${mainMod} SHIFT, P, movetoworkspace, e-1" # Move active window to a workspace with mainMod + SHIFT + [1-9] "${mainMod} SHIFT, 1, movetoworkspace, 1" "${mainMod} SHIFT, 2, movetoworkspace, 2" "${mainMod} SHIFT, 3, movetoworkspace, 3" "${mainMod} SHIFT, 4, movetoworkspace, 4" "${mainMod} SHIFT, 5, movetoworkspace, 5" "${mainMod} SHIFT, 6, movetoworkspace, 6" "${mainMod} SHIFT, 7, movetoworkspace, 7" "${mainMod} SHIFT, 8, movetoworkspace, 8" "${mainMod} SHIFT, 9, movetoworkspace, 9" # Scroll through existing workspaces with mainMod + scroll/side buttons "${mainMod}, mouse_down, focusworkspaceoncurrentmonitor, e-1" "${mainMod}, mouse_up, focusworkspaceoncurrentmonitor, e+1" "${mainMod}, mouse:275, focusworkspaceoncurrentmonitor, e-1" "${mainMod}, mouse:276, focusworkspaceoncurrentmonitor, e+1" # Scratchpads "${mainMod} SHIFT, 0, movetoworkspace, special:a" "${mainMod}, 0, togglespecialworkspace, a" "${mainMod} SHIFT, minus, movetoworkspace, special:b" "${mainMod}, minus, togglespecialworkspace, b" "${mainMod} SHIFT, equal, movetoworkspace, special:c" "${mainMod}, equal, togglespecialworkspace, c" "${mainMod}, Q, exec, ${cfg.lockBinary}" "${mainMod}, T, exec, ${toggleOption}/bin/hypr-toggle-opt input:touchpad:disable_while_typing" ]; bindl = [ "${mainMod} SHIFT, Q, exit," ", XF86AudioMute, exec, dunst-volume set-mute toggle" ]; binde = [ # Resize windows "${mainMod} CTRL ALT, left, resizeactive, -10 0" "${mainMod} CTRL ALT, right, resizeactive, 10 0" "${mainMod} CTRL ALT, up, resizeactive, 0 -10" "${mainMod} CTRL ALT, down, resizeactive, 0 10" "${mainMod} CTRL ALT, H, resizeactive, -10 0" "${mainMod} CTRL ALT, L, resizeactive, 10 0" "${mainMod} CTRL ALT, K, resizeactive, 0 -10" "${mainMod} CTRL ALT, J, resizeactive, 0 10" # Move floating windows "${mainMod} CTRL, left, moveactive, -50 0" "${mainMod} CTRL, right, moveactive, 50 0" "${mainMod} CTRL, up, moveactive, 0 -50" "${mainMod} CTRL, down, moveactive, 0 50" "${mainMod} CTRL, H, moveactive, -50 0" "${mainMod} CTRL, L, moveactive, 50 0" "${mainMod} CTRL, K, moveactive, 0 -50" "${mainMod} CTRL, J, moveactive, 0 50" ]; bindm = [ # Move/resize windows with mainMod + LMB/RMB and dragging "${mainMod}, mouse:272, movewindow" "${mainMod}, mouse:273, resizewindow" ]; bindel = [ # Media keys ", XF86AudioRaiseVolume, exec, dunst-volume set-volume 5%+" ", XF86AudioLowerVolume, exec, dunst-volume set-volume 5%-" ", XF86MonBrightnessDown, exec, dunst-brightness 5%+" ", XF86MonBrightnessUp, exec, dunst-brightness 5%-" ]; }; extraConfig = '' # Passthrough mode bind = ${mainMod}, Escape, submap, passthrough submap = passthrough bind = ${mainMod}, Escape, submap, reset submap = reset # OBS bind = ${mainMod} CTRL, O, submap, OBS submap = OBS bind = , Space, pass, ^(com\.obsproject\.Studio)$ bind = , P, pass, ^(com\.obsproject\.Studio)$ bind = , Escape, submap, reset bind = ${mainMod}, Escape, submap, reset submap = reset ''; }; systemd.user.services = lib.mkIf cfg.wallpaper.enable { hyprpaper = lib.mkIf (cfg.wallpaper.backend == "hyprpaper") { Unit = { Description = "Background wallpaper image via hyprpaper"; After = "hyprland-session.target"; }; Service = { ExecStart = let hyprpaperConfig = pkgs.writeText "hyprpaper.conf" '' preload = ${cfg.wallpaper.image} wallpaper = ,${cfg.wallpaper.image} ipc = off splash = off ''; in "${pkgs.hyprpaper}/bin/hyprpaper -c ${hyprpaperConfig}"; }; Install = { WantedBy = [ "hyprland-session.target" ]; }; }; swaybg = lib.mkIf (cfg.wallpaper.backend == "swaybg") { Unit = { Description = "Background wallpaper image via swaybg"; After = "hyprland-session.target"; }; Service = { ExecStart = "${pkgs.swaybg}/bin/swaybg -i ${cfg.wallpaper.image} -m fill -o '*'"; }; Install = { WantedBy = [ "hyprland-session.target" ]; }; }; }; }); }