{ 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 pkgs.nwg-drawer}" ]; bind = let screenshotScript = pkgs.writeScript "screenshot" '' temp_file=$(mktemp) trap 'rm -f "$temp_file"' EXIT mode="$1" case "$mode" in fullscreen) mode="output" ;; area) mode="area" ;; *) echo "usage: $0 [fullscreen|area]" >&2 exit 2 ;; esac ${lib.getExe pkgs.grimblast} --freeze save "$mode" - > "$temp_file" || exit 0 # exit successful, highly unlikely that grimblast failed, and it is more likely that the user cancelled the screenshot 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 ''; in [ "$mod, Q, killactive, " "$mod, F, togglefloating, " "$mod, P, pseudo, " # dwindle "$mod, J, togglesplit, " # dwindle # move focus with mod + arrow keys "$mod, left, movefocus, l" "$mod, right, movefocus, r" "$mod, up, movefocus, u" "$mod, down, movefocus, 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 pkgs.rofi-wayland} -show run" "$mod, T, exec, ${lib.getExe pkgs.wezterm}" "$mod, L, exec, ${lib.getExe config.modules.desktop.hyprlock.package}" # screenshot # area ", Print, exec, ${screenshotScript} area" "$mod SHIFT, S, exec, ${screenshotScript} area" # fullscreen ", Print, exec, ${screenshotScript} fullscreen" "$mod SHIFT CTRL, S, exec, ${screenshotScript} fullscreen" ", 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}" # alt-tab emulation # doesnt activate when we let go of alt sadly "ALT, Tab, exec, ${lib.getExe pkgs.rofi-wayland} -show window" ] ++ ( # 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 "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1" # polkit agent "${lib.getExe pkgs.wl-clip-persist} --clipboard regular" # to fix wl clipboards disappearing ] ++ config.modules.desktop.execOnStart; 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 = [ # common popups # we should totally dedupe these "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:^Select a file to open$" "size 1100 650, initialTitle:^Select a file to open$" "center, initialTitle:^Select a file to open$" "float, initialTitle:^File Upload$" "size 1100 650, initialTitle:^File Upload$" "center, initialTitle:^File Upload$" "float, class:\.exe$" # fix focus "stayfocused, class:^gcr-prompter" "dimaround, class:^gcr-prompter" "stayfocused, class:^pinentry-" "dimaround, class:^pinentry-" "stayfocused, class:^polkit-" "dimaround, class:^polkit-" # 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$" # 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 swtich for pseudotiling. enabling is mod + P in the keybinds section preserve_split = "yes"; }; master = { new_status = "master"; }; gestures = { workspace_swipe = true; workspace_swipe_min_speed_to_force = 0; workspace_swipe_cancel_ratio = 0.1; }; misc = { force_default_wallpaper = 0; disable_splash_rendering = true; disable_hyprland_logo = true; initial_workspace_tracking = 0; }; }; }; }; }