{ config, lib, pkgs, namespace, ... }: with lib; let cfg = config.${namespace}.programs.hyprland; drawer = "nwg-drawer -fm nautilus -term kitty -mb 10 -mt 10 -ml 10 -mr 10 -pbuseicontheme -i ${config.stylix.icons.dark}"; in { imports = [ ./avizo.nix ./options.nix ]; config = mkIf cfg.enable { # Home packages home.packages = with pkgs; ( [ box64 brightnessctl ddcutil egl-wayland file-roller glib gnome-calculator gnome-calendar gnome-disk-utility gnome-firmware gnome-font-viewer gnome-logs gnome-photos gnome-tweaks gnome-weather gsettings-desktop-schemas hyprcursor hyprland hyprpaper hyprshot hyprsysteminfo kdePackages.qtmultimedia libnotify libz meson nautilus nomacs nwg-look overskride pamixer pavucontrol playerctl qt5.qtwayland qt6.qtwayland waybar wayland-protocols wayland-utils waypaper wev wl-clipboard wlogout wlroots xhost xsettingsd xwayland ] ++ (if cfg.notificationDaemon == "mako" then [ mako ] else [ dunst ]) ++ (if cfg.launcher == "wofi" then [ wofi ] else [ rofi ]) ++ (with pkgs.${namespace}; [ pipewire-python ]) ); # Session variables home.sessionVariables = { BROWSER = "${cfg.defaultApps.browser.pname}"; CLUTTER_BACKEND = "wayland"; EDITOR = "${cfg.defaultApps.editor.pname}"; VISUAL = "${cfg.defaultApps.visual.pname}"; ICON_THEME = config.gtk.iconTheme.name; GTK_CSD = "0"; GTK_THEME = config.gtk.theme.name; GTK_USE_PORTAL = "1"; HYPRCURSOR_THEME = config.stylix.cursor.name; HYPRCURSOR_SIZE = config.stylix.cursor.size; MOZ_ENABLE_WAYLAND = "1"; NIXOS_OZONE_WL = "1"; NIXOS_XDG_OPEN_USE_PORTAL = "1"; QT_AUTO_SCREEN_SCALE_FACTOR = "1"; QT_QPA_PLATFORM = "wayland-egl"; QT_QPA_PLATFORMTHEME = lib.mkDefault "gtk3"; QT_SCALE_FACTOR = "1"; QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; SDL_VIDEODRIVER = "wayland"; TERMINAL = "${cfg.defaultApps.terminal.pname}"; XCURSOR_THEME = config.stylix.cursor.name; XCURSOR_SIZE = config.stylix.cursor.size; XDG_CACHE_HOME = "\${HOME}/.cache"; XDG_CONFIG_HOME = "\${HOME}/.config"; XDG_CURRENT_DESKTOP = "Hyprland"; XDG_DATA_HOME = "\${HOME}/.local/share"; XDG_SESSION_DESKTOP = "Hyprland"; XDG_SESSION_TYPE = "wayland"; }; # Services services = { hyprpolkitagent.enable = true; hyprpaper = { enable = true; settings = { preload = [ cfg.hyprpaper.wallpaperPath ]; wallpaper = [ { monitor = ""; path = cfg.hyprpaper.wallpaperPath; } ]; splash = false; }; }; hypridle = { enable = true; settings = { general = { before_sleep_cmd = "loginctl lock-session"; # lock before suspend. after_sleep_cmd = "hyprctl dispatch dpms on"; # to avoid having to press a key twice to turn on the display. ignore_dbus_inhibit = false; lock_cmd = "pidof hyprlock || hyprlock"; # avoid starting multiple hyprlock instances. }; listener = [ # { # timeout = 300; # 5min # on-timeout = "brightnessctl -s set 10"; # set monitor backlight to minimum, avoid 0 on OLED monitor. # on-resume = "brightnessctl -r"; # monitor backlight restore. # } { timeout = cfg.hyprIdle.lockScreenTimer; on-timeout = "loginctl lock-session"; # lock screen when timeout has passed } { timeout = cfg.hyprIdle.screenOffTimer; on-timeout = "hyprctl dispatch dpms off"; # screen off when timeout has passed on-resume = "hyprctl dispatch dpms on"; # screen on when activity is detected after timeout has fired. } { timeout = cfg.hyprIdle.suspendTimer; on-timeout = "systemctl suspend"; # suspend pc } ]; }; }; }; # Programs programs = { vscode.profiles.default.userSettings."window"."titleBarStyle" = "custom"; hyprlock = { enable = true; settings = { background = mkForce [ { monitor = ""; path = "/run/wallpaper.jpg"; # supports png, jpg, webp (no animations, though) color = mkDefault "rgba(25, 20, 20, 1.0"; # all these options are taken from hyprland, see https://wiki.hyprland.org/Configuring/Variables/#blur for explanations blur_passes = mkDefault "3"; # 0 disables blurring blur_size = mkDefault "7"; noise = "0.0117"; contrast = "0.8916"; brightness = mkDefault "0.8172"; vibrancy = "0.1696"; vibrancy_darkness = "0.0"; } ]; label = [ # Date display { monitor = cfg.primaryDisplay; text = "cmd[update:1000] echo -e \"$(LC_TIME=en_US.UTF-8 date +\"%A, %B %d\")\""; color = config.lib.stylix.colors.base06; font_size = "25"; font_family = lib.mkDefault config.stylix.fonts.monospace.name; position = "0, 350"; halign = "center"; valign = "center"; } # Time display { monitor = cfg.primaryDisplay; text = "cmd[update:1000] echo \"$(date +\"%I:%M\")\""; color = config.lib.stylix.colors.base06; font_size = "120"; font_family = lib.mkDefault "${config.stylix.fonts.monospace.name} Bold"; position = "0, 230"; halign = "center"; valign = "center"; } { monitor = cfg.primaryDisplay; text = "$USER"; color = config.lib.stylix.colors.base06; outline_thickness = 2; dots_size = 0.2; dots_spacing = 0.2; dots_center = true; font_size = 18; font_family = lib.mkDefault "${config.stylix.fonts.monospace.name} Bold"; position = "0, 0"; halign = "center"; valign = "center"; } # weather { monitor = cfg.primaryDisplay; text = "cmd[update:30000] waybar-weather --hyprlock"; color = config.lib.stylix.colors.base06; font_size = "25"; font_family = lib.mkDefault config.stylix.fonts.monospace.name; position = "-100, 100"; halign = "right"; valign = "bottom"; } # media { monitor = cfg.primaryDisplay; text = "cmd[update:1000] waybar-media"; color = config.lib.stylix.colors.base06; font_size = "15"; font_family = lib.mkDefault config.stylix.fonts.monospace.name; position = "100, 100"; halign = "left"; valign = "bottom"; } ]; # user box shape = [ { monitor = cfg.primaryDisplay; size = "200, 50"; color = "rgba(46, 52, 64, .25"; rounding = -1; border_size = "0"; position = "0, 0"; halign = "center"; valign = "center"; } ]; input-field = [ { size = "200, 50"; position = "0, -80"; font_family = lib.mkDefault config.stylix.fonts.monospace.name; monitor = cfg.primaryDisplay; dots_center = true; fade_on_empty = true; font_color = config.lib.stylix.colors.base06; inner_color = config.lib.stylix.colors.base03; outer_color = config.lib.stylix.colors.base00; bothlock_color = -1; outline_thickness = 5; placeholder_text = ''Password...''; shadow_passes = 2; } ]; image = [ { monitor = cfg.primaryDisplay; # path = "/tmp/hyprlock-art"; reload_cmd = "waybar-media-art"; reload_time = 3; size = 150; rounding = 0; position = "100, 150"; halign = "left"; valign = "bottom"; } ]; }; }; }; # Wayland configuration wayland.windowManager.hyprland = { enable = true; xwayland.enable = true; systemd = { enable = true; enableXdgAutostart = true; }; plugins = with pkgs.hyprlandPlugins; [ # hyprgrass ]; settings = let useMonitorV2 = (lib.versionAtLeast pkgs.hyprland.version "0.40.0") && (cfg.monitorv2 != [ ]); names = if useMonitorV2 then map (m: m.name) cfg.monitorv2 else [ cfg.display1.input cfg.display2.input ]; firstMonitor = builtins.elemAt names 0; secondMonitor = if builtins.length names > 1 then builtins.elemAt names 1 else firstMonitor; in { inherit (cfg) workspace; "$mod" = cfg.modKey; # Mouse # mouse_[up|down] - scroll wheel # middle_mouse - 274 # thumb_up - 276 # thumb_down - 275 # l -> locked, will also work when an input inhibitor (e.g. a lockscreen) is active. # r -> release, will trigger on release of a key. # e -> repeat, will repeat when held. # n -> non-consuming, key/mouse events will be passed to the active window in addition to triggering the dispatcher. # m -> mouse, see below. # t -> transparent, cannot be shadowed by other binds. # i -> ignore mods, will ignore modifiers. # s -> separate, will arbitrarily combine keys between each mod/key, see [Keysym combos](#keysym-combos) above. # d -> has description, will allow you to write a description for your bind. # p -> bypasses the app's requests to inhibit keybinds. # https://wiki.hyprland.org/Configuring/Binds/ # https://wiki.hyprland.org/Configuring/Binds/#mouse-buttons bind = [ "$mod, Return, exec, ${cfg.defaultApps.terminal.pname}" "$mod, SPACE, exec, ${if cfg.launcher == "wofi" then "wofi --show drun" else "rofi -show drun"}" ", xf86Search, exec, ${if cfg.launcher == "wofi" then "wofi --show drun" else "rofi -show drun"}" "$mod, Q, killactive, " "$mod, M, exec, wlogout --protocol layer-shell" "$mod, E, exec, ${cfg.defaultApps.fileExplorer.pname}" "$mod, V, togglefloating, " "$mod, D, exec, ${drawer}" "$mod, P, pseudo, " # dwindle "$mod, S, togglesplit, " # dwindle "$mod SHIFT, Q, exec, hyprlock" "$mod SHIFT, 4, exec, hyprshot -m region --clipboard-only" "$mod, F, fullscreen, 1" "$mod SHIFT, F, fullscreen, 0" "$mod SHIFT, E, exec, smile" "$mod, mouse:276, movecurrentworkspacetomonitor, ${firstMonitor}" "$mod, mouse:275, movecurrentworkspacetomonitor, ${secondMonitor}" # alt-tab between workspaces on active monitor "$mod, Tab, workspace, m+1" "$mod SHIFT, Tab, workspace, m-1" "$mod, h, movefocus, l" "$mod, l, movefocus, r" "$mod, k, movefocus, u" "$mod, j, movefocus, d" "$mod, 1, workspace, 1" "$mod, 2, workspace, 2" "$mod, 3, workspace, 3" "$mod, 4, workspace, 4" "$mod, 5, workspace, 5" "$mod, 6, workspace, 6" "$mod, 7, workspace, 7" "$mod, 8, workspace, 8" "$mod, 9, workspace, 9" "$mod, 0, workspace, 10" "$mod ALT, 1, movetoworkspace, 1" "$mod ALT, 2, movetoworkspace, 2" "$mod ALT, 3, movetoworkspace, 3" "$mod ALT, 4, movetoworkspace, 4" "$mod ALT, 5, movetoworkspace, 5" "$mod ALT, 6, movetoworkspace, 6" "$mod ALT, 7, movetoworkspace, 7" "$mod ALT, 8, movetoworkspace, 8" "$mod ALT, 9, movetoworkspace, 9" "$mod ALT, 0, movetoworkspace, discord" "$mod CTRL, l, resizeactive, 10 0" "$mod CTRL, h, resizeactive, -10 0" "$mod CTRL, k, resizeactive, 0 -10" "$mod CTRL, j, resizeactive, 0 10" "$mod SHIFT, l, movewindow, r" "$mod SHIFT, h, movewindow, l" "$mod SHIFT, k, movewindow, u" "$mod SHIFT, j, movewindow, d" "$mod, b, exec, ${cfg.defaultApps.browser.pname}" ] ++ cfg.keybinds.bind; bindm = [ # Move/resize windows with mod + LMB/RMB and dragging "$mod, mouse:272, movewindow" "$mod, mouse:273, resizewindow" # middle mouse will grab a window, mod + middle mouse will close it "$mod SHIFT, mouse:274, movewindow" ] ++ cfg.keybinds.bindm; bindel = [ ", XF86AudioRaiseVolume, exec, volumectl -u up" ", XF86AudioLowerVolume, exec, volumectl -u down" ] ++ cfg.keybinds.bindel; bindl = [ ", XF86AudioMute, exec, volumectl toggle-mute" ", XF86AudioPlay, exec, playerctl play-pause" ", XF86AudioPrev, exec, playerctl previous" ", XF86AudioNext, exec, playerctl next" ", XF86AudioMicMute, exec, volumectl -m toggle-mute" ", XF86MonBrightnessUp, exec, lightctl up" ", XF86MonBrightnessDown, exec, lightctl down" ] ++ cfg.keybinds.bindl; monitor = cfg.monitor ++ map ( m: if m.disabled then "${m.name}, disable" else if m.mirrorOf != null then "${m.name}, mirror, ${m.mirrorOf}" else let mode = if m.mode == null then "preferred" else m.mode; position = if m.position == null then "0x0" else m.position; scale = if m.scale == null then "1" else (toString m.scale); parts = [ m.name mode position scale ]; # Append transform only when set, as "transform, " transformTokens = if m.transform == null then [ ] else [ "transform" m.transform ]; tokens = parts ++ transformTokens ++ m.extra; in builtins.concatStringsSep ", " tokens ) cfg.monitorv2; render = { cm_fs_passthrough = 1; }; misc = { vrr = if cfg.enableVRR then 1 else 0; force_default_wallpaper = 0; }; general = { gaps_in = 5; gaps_out = 10; border_size = 1; # "col.active_border" = "rgb(8aadf4) rgb(24273A) rgb(24273A) rgb(8aadf4) 45deg"; # "col.inactive_border" = "rgb(24273A) rgb(24273A) rgb(24273A) rgb(24273A) 45deg"; layout = "dwindle"; allow_tearing = cfg.allowTearing; }; decoration = { rounding = 10; blur = { enabled = true; size = 2; passes = 2; new_optimizations = true; xray = false; }; }; animations = { enabled = "yes"; bezier = [ "overshot, 0.05, 0.9, 0.1, 1.05" "smoothOut, 0.36, 0, 0.66, -0.56" "smoothIn, 0.25, 1, 0.5, 1" ]; animation = [ "windows, 1, 5, overshot, slide" "windowsOut, 1, 4, smoothOut, slide" "windowsMove, 1, 4, default" "border, 1, 10, default" "fade, 1, 10, smoothIn" "fadeDim, 1, 10, smoothIn" "workspaces, 1, 6, default" ]; }; dwindle = { pseudotile = "yes"; preserve_split = "yes"; }; windowrule = [ "match:title file_progress, float 1" "match:title .*[Cc]onfirm.*, float 1" "match:title .*[Dd]ialog.*, float 1" "match:title .*[Dd]ownload.*, float 1" "match:title .*[Nn]otification.*, float 1" "match:title .*[Ee]rror.*, float 1" "match:title .*[Ss]plash.*, float 1" "match:title .*[Cc]onfirmreset.*, float 1" "match:title .*[Ss]ign [Ii]n - .*, float 1" "match:title .*[Oo]pen [Ff]ile.*, float 1" "match:title .*branchdialog.*, float 1" "match:class .*pavucontrol.*, float 1" "match:class .*pavucontrol.*, move onscreen cursor 0% 0%" "match:class .*[Oo]verskride.*, float 1" "match:class .*FileRoller.*, float 1" "match:class .*wlogout.*, float 1" "match:title .*mpv.*, idle_inhibit stayfocused" "match:class .*nm-connection-editor.*, float 1" "match:class .*nm-connection-editor.*, move onscreen cursor 0% 0%" "match:title Media viewer, float 1" "match:class it.mijorus.smile),match:title Smile, float 1" "match:class .blueman-manager-wrapped)$,match:title Bluetooth Devices, float 1" # Picture in picture windows "match:title .*Picture-in-Picture.*, float 1" "match:title .*Picture-in-Picture.*, pin 1" # discord/vesktop # "workspace: name:discord, match:class .*vesktop" # "match:class .*vesktop),match:title .*Discord Popout.*, float 1" # "pin, match:class .*vesktop),match:title .*Discord Popout.*" # Music # "workspace: name:discord, match:class Apple Music.*" # Steam "match:class .*[Ss]team, match:title .*[Ss]team.*, float 1" "match:class .*[Ss]team, match:title .*[Ss]team.*, workspace name:steam silent" "match:class .*[Ss]team, match:title .*[Ss]team.*, tile 1" "match:class .*steam,match:title .*Friends List.*, float 1" # Code "match:class .*codium.*, match:title Save As, pin 1" "match:class .*codium.*, match:title Save As, float 1" "match:class xdg-desktop-portal-gtk, match:title Open Workspace from File, float 1" # Game Tearing??? https://wiki.hypr.land/Configuring/Tearing/ "match:class .*gamescope.*, idle_inhibit fullscreen, content game, immediate 1" "match:xdg_tag proton-game, idle_inhibit fullscreen, content game, immediate 1" "match:class steam_app_.*, idle_inhibit fullscreen, content game, immediate 1" # vmware # this tag will set the below options to the vdi window # this will have it auto open as a 2160x7680 window # and makes multi-monitor work "match:class .*[Hh]orizon-client, match:title USPS Next VDI, tag +horizonrdp" "match:tag horizonrdp, no_anim 1" "match:tag horizonrdp, no_blur 1" "match:tag horizonrdp, rounding 0" "match:tag horizonrdp, no_shadow 1" "match:tag horizonrdp, immediate 1" "match:tag horizonrdp, allows_input 1" "match:tag horizonrdp, border_size 0" "match:tag horizonrdp, max_size 2160 7680" "match:tag horizonrdp, min_size 1920 1080" "match:tag horizonrdp, render_unfocused 1" "match:tag horizonrdp, idle_inhibit 1" "match:tag horizonrdp, float 1" # float the vmware window cause its annoying to use in fullscreen "match:class .*[Hh]orizon-client),match:title [Oo]mnissa [Hh]orizon [Cc]lient, float 1" ] ++ cfg.windowRule; plugin = { # touch_gestures = { # # The default sensitivity is probably too low on tablet screens, # # I recommend turning it up to 4.0 # sensitivity = "4.0"; # # must be >= 3 # workspace_swipe_fingers = "3"; # # switching workspaces by swiping from an edge, this is separate from workspace_swipe_fingers # # and can be used at the same time # # possible values: l, r, u, or d # # to disable it set it to anything else # workspace_swipe_edge = "d"; # # in milliseconds # long_press_delay = "400"; # # resize windows by long-pressing on window borders and gaps. # # If general:resize_on_border is enabled, general:extend_border_grab_area is used for floating # # windows # resize_on_border_long_press = true; # # in pixels, the distance from the edge that is considered an edge # edge_margin = "10"; # # emulates touchpad swipes when swiping in a direction that does not trigger workspace swipe. # # ONLY triggers when finger count is equal to workspace_swipe_fingers # # # # might be removed in the future in favor of event hooks # emulate_touchpad_swipe = false; # experimental = { # # send proper cancel events to windows instead of hacky touch_up events, # # NOT recommended as it crashed a few times, once it's stabilized I'll make it the default # send_cancel = "0"; # }; # hyprgrass-bind = [ # # swipe left from right edge # ", edge:r:l, workspace, +1" # # swipe up from bottom edge # ", edge:d:u, exec, ${cfg.defaultApps.browser.pname}" # # swipe down from left edge # ", edge:l:d, exec, pactl set-sink-volume @DEFAULT_SINK@ -4%" # # swipe down with 4 fingers # ", swipe:4:d, killactive" # # swipe diagonally left and down with 3 fingers # # l (or r) must come before d and u # ", swipe:3:ld, exec, foot" # # tap with 3 fingers # ", tap:3, exec, foot" # # longpress can trigger mouse binds: # ", longpress:2, movewindow" # ", longpress:3, resizewindow" # ]; # }; }; gesture = [ "3, horizontal, scale: 0.75, workspace" # swipe 3 fingers to change workspace "3, pinch, mod: SUPER, resize" "4, pinch, fullscreen" ]; input = { kb_layout = "us"; kb_variant = ""; kb_model = ""; kb_options = ""; kb_rules = ""; numlock_by_default = true; follow_mouse = 1; touchpad = { clickfinger_behavior = 1; natural_scroll = "yes"; }; sensitivity = 0; # -1.0 - 1.0, 0 means no modification. }; # experimental = { # xx_color_management_v4 = true; # }; debug = { # full_cm_proto = cfg.debug.fullCmProto; disable_logs = cfg.debug.disableLogs; disable_scale_checks = cfg.debug.disableScaleChecks; }; }; extraConfig = let autostarts = builtins.concatStringsSep "\n" (map (cmd: "exec-once = ${cmd}") cfg.autostartCommands); in '' exec-once = dbus-update-activation-environment --systemd --all exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP exec-once = xhost +SI:localuser:root '' + autostarts + "\n" + (cfg.extraConfig or ""); }; }; }