{ modules, ... }: { modules.hyprland = { provides = { doloro-settings = { includes = [ modules.dunst ]; homeManager = { pkgs, lib, ... }: let tofiCfg = pkgs.writeTextFile { name = "hyprland-tofi-config"; text = '' width = 100% height = 100% border-width = 0 outline-width = 0 padding-left = 35% padding-top = 35% result-spacing = 25 num-results = 5 font = monospace background-color = #000A ''; }; meowScript = pkgs.writeShellScript "meow" '' fish -c 'grim -g $(slurp) -t png - &| wl-copy -t image/png; killall wayfreeze' ''; mreowScript = pkgs.writeShellScript "mreow" '' exec $(tofi-drun -c ${tofiCfg}) ''; mainMod = "SUPER"; # Each `settings.` list entry renders as `hl.()`. # `_args` renders a multi-argument call instead of one Lua table, # and mkLuaInline passes raw Lua through for dispatchers. dsp = lib.generators.mkLuaInline; bind = key: dispatcher: { _args = [ key dispatcher ]; }; bezier = name: points: { _args = [ name { type = "bezier"; inherit points; } ]; }; wsBinds = lib.concatLists ( builtins.genList ( i: let ws = toString (i + 1); in [ (bind "${mainMod} + ${ws}" (dsp "hl.dsp.focus({ workspace = ${ws} })")) (bind "${mainMod} + SHIFT + ${ws}" (dsp "hl.dsp.window.move({ workspace = ${ws} })")) ] ) 9 ); in { home.packages = [ pkgs.hyprcursor pkgs.hyprshutdown pkgs.tofi pkgs.killall pkgs.grim pkgs.slurp pkgs.wayclip ]; # wlogout menu. Default layout, except Shutdown hands off to # hyprshutdown so apps close gracefully before poweroff. # programs.wlogout only accepts one command per button, so # --post-cmd carries the actual poweroff. programs.wlogout = { enable = true; layout = [ { label = "lock"; action = "loginctl lock-session"; text = "Lock"; keybind = "l"; } { label = "hibernate"; action = "systemctl hibernate"; text = "Hibernate"; keybind = "h"; } { label = "logout"; action = "loginctl terminate-user $USER"; text = "Logout"; keybind = "e"; } { label = "shutdown"; action = "hyprshutdown --post-cmd 'systemctl poweroff'"; text = "Shutdown"; keybind = "s"; } { label = "suspend"; action = "systemctl suspend"; text = "Suspend"; keybind = "u"; } { label = "reboot"; action = "hyprshutdown --post-cmd 'systemctl reboot'"; text = "Reboot"; keybind = "r"; } ]; }; home.pointerCursor = { enable = true; name = "rose-pine-hyprcursor"; package = pkgs.rose-pine-hyprcursor; hyprcursor.enable = true; }; services.hyprpaper = { enable = true; settings = { splash = true; wallpaper = [ { monitor = "HDMI-A-1"; path = "/home/doloro/Wallpaper.jpg"; } { monitor = "DP-2"; path = "/home/doloro/Wallpaper.jpg"; } ]; }; }; # The systemd session activation hooks (hyprland-session.target # start/stop + dbus-update-activation-environment --systemd --all) # are still generated by Home Manager; only the imperative tail of # the config needs raw Lua. wayland.windowManager.hyprland = { settings = { config = { general = { gaps_in = 1; gaps_out = 1; border_size = 1; resize_on_border = false; allow_tearing = true; layout = "dwindle"; }; input = { follow_mouse = 2; kb_layout = "gb"; }; decoration = { rounding = 4; rounding_power = 1; active_opacity = 1.0; inactive_opacity = 1.0; shadow.enabled = false; blur = { enabled = true; passes = 1; new_optimizations = true; ignore_opacity = false; }; }; cursor.default_monitor = "DP-2"; cursor.no_hardware_cursors = false; dwindle.preserve_split = true; misc = { force_default_wallpaper = -1; disable_hyprland_logo = false; enable_anr_dialog = false; }; }; monitor = [ { output = "HDMI-A-1"; mode = "1920x1080@60"; position = "0x0"; scale = "1"; } { output = "DP-2"; mode = "1920x1080@120"; position = "1920x0"; scale = "1"; } ]; workspace_rule = [ { workspace = "2"; monitor = "DP-2"; default = true; } ] ++ builtins.genList (i: { workspace = toString (i + 2); monitor = "DP-2"; }) 9; device = { name = "default"; kb_layout = "gb"; sensitivity = -0.5; }; # Curves are sorted ahead of animations by the module's # `importantPrefixes` default, so the beziers exist by name. curve = [ (bezier "easeOutQuint" [ [ 0.23 1 ] [ 0.32 1 ] ]) (bezier "easeInOutCubic" [ [ 0.65 0.05 ] [ 0.36 1 ] ]) (bezier "linear" [ [ 0 0 ] [ 1 1 ] ]) (bezier "almostLinear" [ [ 0.5 0.5 ] [ 0.75 1 ] ]) (bezier "quick" [ [ 0.15 0 ] [ 0.1 1 ] ]) ]; animation = [ { leaf = "global"; enabled = true; speed = 10; bezier = "default"; } { leaf = "border"; enabled = true; speed = 5.39; bezier = "easeOutQuint"; } { leaf = "windows"; enabled = true; speed = 2.79; bezier = "easeOutQuint"; } { leaf = "windowsIn"; enabled = true; speed = 1; bezier = "easeOutQuint"; style = "popin 87%"; } { leaf = "windowsOut"; enabled = true; speed = 1; bezier = "linear"; style = "popin 87%"; } { leaf = "windowsMove"; enabled = true; speed = 1; bezier = "easeInOutCubic"; } { leaf = "fadeIn"; enabled = true; speed = 0.5; bezier = "almostLinear"; } { leaf = "fadeOut"; enabled = true; speed = 0.5; bezier = "almostLinear"; } { leaf = "fade"; enabled = true; speed = 1; bezier = "quick"; } { leaf = "layers"; enabled = true; speed = 3.81; bezier = "easeOutQuint"; } { leaf = "layersIn"; enabled = true; speed = 4; bezier = "easeOutQuint"; style = "fade"; } { leaf = "layersOut"; enabled = true; speed = 1.5; bezier = "linear"; style = "fade"; } { leaf = "fadeLayersIn"; enabled = true; speed = 1.79; bezier = "almostLinear"; } { leaf = "fadeLayersOut"; enabled = true; speed = 1.39; bezier = "almostLinear"; } { leaf = "workspaces"; enabled = true; speed = 1; bezier = "almostLinear"; style = "slide"; } { leaf = "workspacesIn"; enabled = true; speed = 1; bezier = "easeInOutCubic"; style = "slide"; } { leaf = "workspacesOut"; enabled = true; speed = 1; bezier = "easeInOutCubic"; style = "slide"; } { leaf = "zoomFactor"; enabled = true; speed = 7; bezier = "quick"; } ]; bind = [ (bind "${mainMod} + R" (dsp ''hl.dsp.exec_cmd("${mreowScript}")'')) (bind "${mainMod} + C" (dsp "hl.dsp.window.close()")) (bind "${mainMod} + M" (dsp ''hl.dsp.exec_cmd("uwsm stop")'')) (bind "${mainMod} + Q" (dsp ''hl.dsp.exec_cmd("kitty")'')) (bind "${mainMod} + V" (dsp ''hl.dsp.window.float({ action = "toggle" })'')) (bind "${mainMod} + left" (dsp ''hl.dsp.focus({ direction = "left" })'')) (bind "${mainMod} + right" (dsp ''hl.dsp.focus({ direction = "right" })'')) (bind "${mainMod} + up" (dsp ''hl.dsp.focus({ direction = "up" })'')) (bind "${mainMod} + down" (dsp ''hl.dsp.focus({ direction = "down" })'')) (bind "${mainMod} + L" (dsp ''hl.dsp.exec_cmd("wlogout")'')) (bind "${mainMod} + S" (dsp ''hl.dsp.exec_cmd("wayfreeze --after-freeze-cmd '${meowScript}'")'')) (bind "${mainMod} + F" (dsp ''hl.dsp.window.fullscreen({ action = "toggle" })'')) (bind "${mainMod} + SHIFT + Q" (dsp "hl.dsp.window.close()")) (bind "${mainMod} + mouse_down" (dsp ''hl.dsp.focus({ workspace = "e+1" })'')) (bind "${mainMod} + mouse_up" (dsp ''hl.dsp.focus({ workspace = "e-1" })'')) # Mouse binds (bind "${mainMod} + mouse:272" (dsp "hl.dsp.window.drag()")) (bind "${mainMod} + mouse:273" (dsp "hl.dsp.window.resize()")) # Media keys (bind "XF86AudioLowerVolume" (dsp ''hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-")'')) (bind "XF86AudioRaiseVolume" (dsp ''hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+")'')) (bind "XF86AudioMute" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle")'')) (bind "XF86AudioPlay" (dsp ''hl.dsp.exec_cmd("playerctl play-pause")'')) (bind "XF86AudioNext" (dsp ''hl.dsp.exec_cmd("playerctl next")'')) (bind "XF86AudioPrev" (dsp ''hl.dsp.exec_cmd("playerctl previous")'')) (bind "XF86AudioMicMute" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle")'')) (bind "home" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle")'')) (bind "end" (dsp ''hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle")'')) (bind "F8" (dsp "hl.dsp.pass({ window = [[match:class:^(com.obsproject.Studio)$]] })")) # Workspaces (bind "${mainMod} + 0" (dsp "hl.dsp.focus({ workspace = 10 })")) (bind "${mainMod} + SHIFT + 0" (dsp "hl.dsp.window.move({ workspace = 10 })")) ] ++ wsBinds; window_rule = [ { suppress_event = "maximize"; } { match.class = "gamescope"; workspace = "5"; immediate = true; confine_pointer = true; } { match.initial_title = "OBS Studio Crash Detected"; pin = true; } # Steam, Discord (equibop), Telegram, Spotify and OBS # always open on their own workspace regardless of how # they were launched. These sit before the "Discord # Popout" rule: window rules apply in declaration order # and the last match wins, so popouts still land on # workspace 1. { match.class = "steam"; workspace = "6 silent"; } { match.class = "equibop"; workspace = "8 silent"; } { match.class = "org.telegram.desktop"; workspace = "8 silent"; } { match.class = "spotify"; workspace = "9 silent"; } { match.class = "com.obsproject.Studio"; workspace = "10 silent"; } { match.initial_title = "Discord Popout"; workspace = "1 silent"; } ]; layer_rule = [ { match.namespace = "notifications"; no_screen_share = true; } ]; }; # Host-specific overrides: load ~/.config/hypr/hyprland-local.lua # if present (must be valid Lua). Runs last so a host can override # anything set above. extraConfig = '' local home = os.getenv("HOME") if home then local localPath = home .. '/.config/hypr/hyprland-local.lua' local f = io.open(localPath, 'r') if f then f:close() local chunk, err = loadfile(localPath) if chunk then chunk() else io.stderr:write('hyprland-local.lua error: ' .. err .. '\n') end end end ''; }; }; }; }; }; }