{ config, inputs, lib, pkgs, ... }: let cfg = config.myOptions.programs.hypr.land; inherit (config.myOptions.other.system) username; hmCfg = config.home-manager.users.${username}; inherit (lib) elem getExe getExe' mkEnableOption mkIf mkOption optionals; inherit (lib.types) bool listOf nullOr package path str submodule; inherit (lib.strings) removeSuffix; inherit (lib.attrsets) mapAttrsToList; isAmdGpuPresent = elem "amdgpu" config.boot.initrd.kernelModules; smwPresent = elem inputs.split-monitor-workspaces.packages.${pkgs.system}.split-monitor-workspaces cfg.extraPlugins; foot' = inputs.nixpkgs-wayland.packages.${pkgs.system}.foot; foot = getExe' foot' "foot"; footclient = getExe' foot' "footclient"; inherit (inputs.nixpkgs-wayland.packages.${pkgs.system}) wl-clipboard; wl-copy = getExe' wl-clipboard "wl-copy"; wl-paste = getExe' wl-clipboard "wl-paste"; swww = getExe inputs.nixpkgs-wayland.packages.${pkgs.system}.swww; wlsunset = getExe inputs.nixpkgs-wayland.packages.${pkgs.system}.wlsunset; anyrun = getExe inputs.anyrun.packages.${pkgs.system}.anyrun; hyprcontrib = inputs.hyprcontrib.packages.${pkgs.system}; grimblast = getExe hyprcontrib.grimblast; shadower = getExe inputs.shadower.packages.${pkgs.system}.shadower; inherit (inputs.hyprland.packages.${pkgs.system}) hyprland xdg-desktop-portal-hyprland; hyprctl = getExe' hyprland "hyprctl"; waybar = getExe inputs.waybar.packages.${pkgs.system}.waybar; woomer = getExe inputs.woomer.packages.${pkgs.system}.default; pkill = getExe' pkgs.procps "pkill"; tee = getExe' pkgs.coreutils-full "tee"; xdg-user-dir = getExe' pkgs.xdg-user-dirs "xdg-user-dir"; xdg-open = getExe' pkgs.xdg-utils "xdg-open"; notify-send = getExe pkgs.libnotify; swappy = getExe pkgs.swappy; asusctl = getExe' config.services.asusd.package "asusctl"; rog-control-center = getExe' config.services.asusd.package "rog-control-center"; systemctl = getExe' pkgs.systemd "systemctl"; loginctl = getExe' pkgs.systemd "loginctl"; dbus-update-activation-environment = getExe' pkgs.dbus "dbus-update-activation-environment"; btop = getExe pkgs.btop; amdgpu_top = getExe pkgs.amdgpu_top; helvum = getExe pkgs.helvum; pwvucontrol = getExe pkgs.pwvucontrol; nm-applet = getExe pkgs.networkmanagerapplet; blueman-applet = getExe' pkgs.blueman "blueman-applet"; keepassxc = getExe' pkgs.keepassxc "keepassxc"; playerctl = getExe pkgs.playerctl; pamixer = getExe pkgs.pamixer; brightnessctl = getExe pkgs.brightnessctl; rg = getExe pkgs.ripgrep; gawk = getExe pkgs.gawk; mpv = getExe pkgs.mpv; in { options.myOptions.programs.hypr.land = { enable = mkEnableOption "hiper ląd"; startupSound = mkOption { type = nullOr path; description = "sound to play on hyprland startup"; default = null; }; tearing = mkOption { type = bool; description = "enable tearing"; default = false; }; # there's unfortunately no recursive merge function that I know of # which means just `//`ing cfg.extraSettings with settings will # overwrite the default settings from this module # my workaround for that right now is to manually `//` or `++` each # list or attrs in cfg.extraSettings - that's why I need these types # whenever I wanna add something else I'll need to add that here extraSettings = mkOption { type = submodule { options = { exec-once = mkOption { description = "commands ran on hyprland launch"; default = []; type = listOf str; }; exec = mkOption { description = "commands ran on hyprland reload"; default = []; type = listOf str; }; }; }; description = "extra per host hyprland settings"; default = {}; }; extraPlugins = mkOption { type = listOf package; description = "extra per host hyprland plugins"; default = []; }; }; disabledModules = [ "programs/hyprland.nix" ]; config = mkIf cfg.enable { environment.sessionVariables = { XDG_CURRENT_DESKTOP = "Hyprland"; XDG_SESSION_TYPE = "wayland"; XDG_SESSION_DESKTOP = "Hyprland"; SDL_VIDEODRIVER = "wayland"; _JAVA_AWT_WM_NONEREPARENTING = "1"; CLUTTER_BACKEND = "wayland"; GDK_BACKEND = "wayland"; QT_QPA_PLATFORM = "wayland"; LIBSEAT_BACKEND = "logind"; }; xdg.portal = { enable = true; extraPortals = [ pkgs.xdg-desktop-portal-gtk xdg-desktop-portal-hyprland ]; # TODO look into and make use of this config.common.default = "*"; }; programs.hyprland = { enable = true; package = hyprland; portalPackage = xdg-desktop-portal-hyprland; }; home-manager.users.${username} = { # TODO swap back to this whenever fufexan or anyone else # adds an equivalent of hm's settings to this module #imports = [ inputs.hyprland.homeManagerModules.default ]; xdg.dataFile = mkIf (cfg.startupSound != null) { "hypr/startup_sound".source = cfg.startupSound; }; wayland.windowManager.hyprland = { enable = true; package = hyprland; plugins = cfg.extraPlugins; # TODO make these work with the config below (infinite recursion) # and make this file stop hanging my neovim every 2 seconds # and figure out why it triggers E79 every single time I edit something extraConfig = '' bind=$mainMod, W, exec, ${removeSuffix " %U" hmCfg.xdg.desktopEntries.firefox-schizo.exec} bind=$mainMod SHIFT, W, exec, ${removeSuffix " %U" hmCfg.xdg.desktopEntries.firefox-unschizo.exec} ${if (cfg.startupSound != null) then "exec-once=${mpv} --no-video --volume=100 ${hmCfg.xdg.dataHome}/hypr/startup_sound" else ""} ''; settings = { "$mainMod" = "SUPER"; monitor = mapAttrsToList (name: m: "${name},${toString m.resolution.w}x${toString m.resolution.h}@${toString m.refreshRate},${toString m.position.x}x${toString m.position.y},${toString m.scale}" ) config.myOptions.other.hardware.monitors; workspace = [ # laptop "1, monitor:eDP-1, default:true" "2, monitor:eDP-1" "3, monitor:eDP-1" "4, monitor:eDP-1" "5, monitor:eDP-1" "6, monitor:eDP-1" "7, monitor:eDP-1" "8, monitor:eDP-1" "9, monitor:eDP-1" "10, monitor:eDP-1" # second monitor "11, monitor:HDMI-A-1, default:true" "12, monitor:HDMI-A-1" "13, monitor:HDMI-A-1" "14, monitor:HDMI-A-1" "15, monitor:HDMI-A-1" "16, monitor:HDMI-A-1" "17, monitor:HDMI-A-1" "18, monitor:HDMI-A-1" "19, monitor:HDMI-A-1" "20, monitor:HDMI-A-1" # scratchpads "special:btop, decorate:false" (mkIf isAmdGpuPresent "special:amdgpu_top, decorate:false") "special:pipewire, decorate:false" "special:nixos, decorate:false" (mkIf config.services.asusd.enable "special:rog, decorate:false") "special:keepassxc, decorate:false" ]; input = { kb_layout = "pl,ru"; kb_variant = ""; kb_model = ""; kb_options = ""; kb_rules = ""; follow_mouse = true; touchpad = { disable_while_typing = false; }; repeat_rate = 50; repeat_delay = 250; touchdevice = { output = "eDP-1"; }; tablet = { output = "eDP-1"; }; }; general = { sensitivity = 1.0; gaps_in = 5; gaps_out = 5; border_size = 2; apply_sens_to_raw = 0; #no_border_on_floating = true; allow_tearing = mkIf cfg.tearing true; "col.active_border" = "0xFFF5C2E7"; "col.inactive_border" = "0xFF45475A"; #col.group_border_active = "0xFFA6E3A1"; #col.group_border = "0xFF45475A"; }; env = optionals cfg.tearing [ "WLR_DRM_NO_ATOMIC,1" ]; decoration = { rounding = 10; blur = { enabled = true; size = 3; passes = 2; }; drop_shadow = 1; shadow_range = 15; shadow_render_power = 2; shadow_ignore_window = 1; shadow_offset = "2 4"; shadow_scale = 1; "col.shadow" = "0xAF1E1E2E"; }; gestures = { workspace_swipe = 1; workspace_swipe_distance = 200; workspace_swipe_invert = 1; workspace_swipe_min_speed_to_force = 20; workspace_swipe_cancel_ratio = 0.5; }; bezier = [ "dupa, 0.1, 0.9, 0.1, 1.05" ]; animations = { enabled = true; animation = [ "windows, 1, 4, dupa, popin" "windowsOut, 1, 7, dupa, slide" "border, 1, 15, default" "fade, 1, 10, default" "workspaces, 1, 5, dupa, slidevert" ]; }; dwindle = { no_gaps_when_only = true; }; misc = { enable_swallow = false; swallow_regex = "foot"; focus_on_activate = true; vrr = 1; vfr = true; animate_manual_resizes = false; animate_mouse_windowdragging = false; force_default_wallpaper = 0; }; windowrulev2 = [ "float, class:^(Tor Browser)$" "float, class:^(Mullvad Browser)$" "float, class:^(foot)$" "float, class:^(mpv)$" "float, class:^(imv)$" "float, class:^(Vieb)$" "float, title:^(Picture-in-Picture)$" "float, title:^(.*)(Choose User Profile)(.*)$" "float, title:^(blob:null/)(.*)$" "float, class:^(xdg-desktop-portal-gtk)$" "float, class:^(code), title: ^(Open*)" "size 70% 70%, class:^(code), title: ^(Open*)" "center, class: ^(code), title: ^(Open*)" "float, class:^(org.keepassxc.KeePassXC)$" (mkIf config.services.asusd.enable "size 960 670, title:^(ROG Control Center)$") (mkIf cfg.tearing "immediate, class:^(osu!)$") ]; bind = [ "$mainMod, RETURN, exec, ${if config.myOptions.programs.foot.server then footclient else foot}" "$mainMod, Q, killactive" "$mainMod, F, fullscreen, 0" "$mainMod, M, fullscreen, 1" "$mainMod, D, exec, ${pkill} anyrun || ${anyrun}" "$mainMod, SPACE, togglefloating, active" "$mainMod SHIFT, C, centerwindow" "$mainMod CONTROL, R, bringactivetotop" "$mainMod SHIFT, P, pin" # workspaces "$mainMod, 1, ${if smwPresent then "split-" else ""}workspace, 1" "$mainMod, 2, ${if smwPresent then "split-" else ""}workspace, 2" "$mainMod, 3, ${if smwPresent then "split-" else ""}workspace, 3" "$mainMod, 4, ${if smwPresent then "split-" else ""}workspace, 4" "$mainMod, 5, ${if smwPresent then "split-" else ""}workspace, 5" "$mainMod, 6, ${if smwPresent then "split-" else ""}workspace, 6" "$mainMod, 7, ${if smwPresent then "split-" else ""}workspace, 7" "$mainMod, 8, ${if smwPresent then "split-" else ""}workspace, 8" "$mainMod, 9, ${if smwPresent then "split-" else ""}workspace, 9" "$mainMod, 0, ${if smwPresent then "split-" else ""}workspace, 10" "$mainMod SHIFT, 1, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 1" "$mainMod SHIFT, 2, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 2" "$mainMod SHIFT, 3, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 3" "$mainMod SHIFT, 4, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 4" "$mainMod SHIFT, 5, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 5" "$mainMod SHIFT, 6, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 6" "$mainMod SHIFT, 7, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 7" "$mainMod SHIFT, 8, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 8" "$mainMod SHIFT, 9, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 9" "$mainMod SHIFT, 0, ${if smwPresent then "split-" else ""}movetoworkspacesilent, 10" # screenshots "$mainMod, S, exec, ${grimblast} save area - | ${tee} \"$(${xdg-user-dir} PICTURES)/Screenshots/$(date +'screenshot-%Y%m%d%H%M%S.png')\" | ${wl-copy} && ${notify-send} \"Screenshot taken\"" "$mainMod SHIFT, S, exec, ${grimblast} save area - | ${shadower} | ${tee} \"$(${xdg-user-dir} PICTURES)/Screenshots/$(date +'screenshot-%Y%m%d%H%M%S.png')\" | ${wl-copy} && ${notify-send} \"Screenshot taken (shadower)\"" "$mainMod CONTROL, S, exec, ${grimblast} save area - | ${swappy} -f - -o - | ${tee} \"$(${xdg-user-dir} PICTURES)/Screenshots/$(date +'screenshot-%Y%m%d%H%M%S.png')\" | ${wl-copy} && ${notify-send} \"Screenshot taken\"" "$mainMod SHIFT CONTROL, S, exec, ${grimblast} save area - | ${swappy} -f - -o - | ${shadower} | ${tee} \"$(${xdg-user-dir} PICTURES)/Screenshots/$(date +'screenshot-%Y%m%d%H%M%S.png')\" | ${wl-copy} && ${notify-send} \"Screenshot taken (shadower)\"" "$mainMod SHIFT, R, exec, ${hyprctl} reload" (mkIf config.services.asusd.enable ", XF86Launch3, exec, ${asusctl} led-mode -n") (mkIf config.services.asusd.enable ", XF86Launch4, exec, ${asusctl} profile -n") # TODO what the fuck is this ", XF86TouchpadToggle, exec, " # TODO fix this maybe "$mainMod, T, exec, grim -g \"$(slurp)\" -t ppm - | tesseract -l pol - - | wl-copy && ${notify-send} \"Copied text: \" \"\\\"$(wl-paste)\\\"\"" # TODO fix this "$mainMod, SEMICOLON, exec, ~/.config/rofi/powermenu/type-2/powermenu.sh" "$mainMod, G, togglegroup" # this sometimes fixes the portal "$mainMod CONTROL, K, exec, ${systemctl} --user restart xdg-desktop-portal-hyprland" "$mainMod SHIFT, K, exec, ${hyprctl} kill" # scratchpad binds "$mainMod, B, togglespecialworkspace, btop" (mkIf isAmdGpuPresent "$mainMod, A, togglespecialworkspace, amdgpu_top") "$mainMod, V, togglespecialworkspace, pipewire" "$mainMod, N, togglespecialworkspace, nixos" # rog button on laptop keyboard (mkIf config.services.asusd.enable ", XF86Launch1, togglespecialworkspace, rog") (mkIf config.services.asusd.enable "$mainMod, R, togglespecialworkspace, rog") "$mainMod, X, togglespecialworkspace, keepassxc" "$mainMod, C, exec, ${loginctl} lock-session" # toggle waybar "$mainMod CONTROL, B, exec, ${pkill} waybar || ${waybar}" "$mainMod CONTROL, minus, exec, ${hyprctl} keyword misc:cursor_zoom_factor 2" "$mainMod CONTROL, apostrophe, exec, ${hyprctl} keyword misc:cursor_zoom_factor 1" "$mainMod, Z, exec, ${woomer}" # I love Microsoft !!!!!! "$mainMod ALT CONTROL SHIFT, L, exec, ${xdg-open} https://www.linkedin.com/" ]; # repeat binde = [ # window focus "$mainMod, J, movefocus, d" "$mainMod, K, movefocus, u" # monitor focus "$mainMod, H, focusmonitor, l" "$mainMod, L, focusmonitor, r" # keyboard brightness (mkIf config.services.asusd.enable ", XF86KbdBrightnessUp, exec, ${asusctl} -n") (mkIf config.services.asusd.enable ", XF86KbdBrightnessDown, exec, ${asusctl} -p") "$mainMod SHIFT, G, changegroupactive, f" "$mainMod CONTROL, G, changegroupactive, b" "$mainMod, minus, exec, ${hyprctl} keyword misc:cursor_zoom_factor \"$(${hyprctl} getoption misc:cursor_zoom_factor | ${rg} float | ${gawk} '{print $2 + 0.05}')\"" "$mainMod, apostrophe, exec, ${hyprctl} keyword misc:cursor_zoom_factor \"$(${hyprctl} getoption misc:cursor_zoom_factor | ${rg} float | ${gawk} '{if($2 != 1) {print $2 - 0.05}}')\"" ]; # locked bindl = let play-pause = "${playerctl} play-pause"; stop = "${playerctl} stop"; prev = "${playerctl} previous"; next = "${playerctl} next"; toggle-mute = "${pamixer} --toggle-mute"; in [ ", XF86AudioMedia, exec, ${play-pause}" ", XF86AudioPlay, exec, ${play-pause}" "$mainMod, Y, exec, ${play-pause}" ", XF86AudioStop, exec, ${stop}" "$mainMod, U, exec, ${stop}" ", XF86AudioPrev, exec, ${prev}" "$mainMod, I, exec, ${prev}" ", XF86AudioNext, exec, ${next}" "$mainMod, O, exec, ${next}" ", XF86AudioMute, exec, ${toggle-mute}" "$mainMod, P, exec, ${toggle-mute}" ]; # locked + repeat bindle = let brightness_up = "${brightnessctl} set +5%"; brightness_down = "${brightnessctl} set 5%-"; volume_up = "${pamixer} -ui 5"; volume_down = "${pamixer} -ud 5"; in [ # TODO integrate the scripts from https://codeberg.org/jacekpoz/dotconfig/src/branch/master/Scripts ", XF86MonBrightnessUp, exec, ${brightness_up}" "$mainMod SHIFT, Z, exec, ${brightness_up}" ", XF86MonBrightnessDown, exec, ${brightness_down}" "$mainMod SHIFT, X, exec, ${brightness_down}" ", XF86AudioRaiseVolume, exec, ${volume_up}" "$mainMod SHIFT, C, exec, ${volume_up}" ", XF86AudioLowerVolume, exec, ${volume_down}" "$mainMod SHIFT, V, exec, ${volume_down}" ]; # mouse bindm = [ "$mainMod, mouse:272, movewindow" "$mainMod, mouse:273, resizewindow" ]; binds = { pass_mouse_when_bound = false; movefocus_cycles_fullscreen = false; }; exec-once = let # https://wiki.hyprland.org/IPC/#how-to-use-socket2-with-bash handle_hyprland_events = pkgs.writeShellScript "handle_hyprland_events" '' #!/bin/sh handle() { case $1 in configreloaded*) ${hyprctl} notify 1 2500 "" " Reloading Hyprland..." ;; esac } ${pkgs.socat}/bin/socat -U - UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do handle "$line" done ''; in [ "${waybar}" # run persistent special workspace windows "[workspace special:nixos silent;tile] cd ~/niksos; ${foot}" (mkIf config.services.asusd.enable "[workspace special:rog silent;tile] ${rog-control-center}") "[workspace special:keepassxc silent;tile] ${keepassxc}" (mkIf config.myOptions.programs.foot.server "sleep 0.5 && ${systemctl} --user restart foot.service") "${hyprctl} setcursor Bibata-Modern-Classic 24" # TODO do something with this #"${wl-paste} --watch cliphist store" "${swww} init" "${wlsunset} -S 06:00 -s 20:00" # TODO fix fcitx5 #"fcitx5" # TODO do these do anything anymore "${systemctl} --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP" "${dbus-update-activation-environment} --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP" "${handle_hyprland_events}" ] ++ cfg.extraSettings.exec-once; exec = [ # kill (almost) everything on special workspaces "${pkill} btop" (mkIf isAmdGpuPresent "${pkill} amdgpu_top") "${pkill} helvum" "${pkill} pwvucontrol" # and run it all again "[workspace special:btop silent;tile] ${foot} -e ${btop}" (mkIf isAmdGpuPresent "[workspace special:amdgpu_top silent;tile] ${foot} -e ${amdgpu_top} -i 0") (mkIf isAmdGpuPresent "[workspace special:amdgpu_top silent;tile] ${foot} -e ${amdgpu_top} -i 1") "[workspace special:pipewire silent;tile] ${helvum}" "[workspace special:pipewire silent;tile] ${pwvucontrol}" "${nm-applet} --indicator" "${blueman-applet}" ] ++ cfg.extraSettings.exec; layerrule = [ # no black border on grimblast screenshots "noanim, ^(selection)$" "blur, ^(waybar)$" "blur, ^(eww)$" # TODO maybe this isn't needed # temporary fix to swaylock screenshoting rofi before locking #"noanim, ^(rofi)$" "blur, ^(anyrun)$" ]; plugin = { csgo-vulkan-fix = { res_w = 2560; res_h = 1440; }; split-monitor-workspaces = { count = 10; keep_focused = true; }; }; }; }; }; }; }