{ lib, config, inputs, pkgs, ... }: let cfg = config.marleyos.wayland.hyprland; in { options.marleyos.wayland.hyprland = { enable = lib.mkEnableOption "hyprland"; monitors = lib.mkOption { description = "Monitor configuration."; type = with lib.types; attrsOf str; }; mainMonitor = lib.mkOption { description = "Which monitor to treat as the main for workspace assignment"; type = with lib.types; str; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = if cfg ? monitors then cfg ? mainMonitor else true; message = '' You have defined monitors but not selected the main monitor. Please define marleyos.wayland.hyprland.mainMonitor. ''; } ]; marleyos = { programs.wofi.enable = true; services.waybar.enable = true; }; home.pointerCursor.hyprcursor.enable = true; home.packages = lib.optional config.rose-pine.enable inputs.rose-pine-hyprcursor.packages.${pkgs.system}.default; services.hyprpaper = { enable = true; settings = { preload = "${./wallpaper.png}"; wallpaper = ", ${./wallpaper.png}"; }; }; wayland.windowManager.hyprland = { enable = true; systemd.enable = true; xwayland.enable = true; settings = let numMonitors = lib.length (lib.attrNames (cfg.monitors or {})); workspaces = lib.range 1 10; wsPerMonitor = 10 / numMonitors; mainMonWs = lib.range 1 wsPerMonitor; secondaryWs = lib.subtractLists mainMonWs workspaces; monitors = lib.mapAttrs (mon: _: if (mon == (cfg.mainMonitor or "")) then "" else lib.replicate wsPerMonitor "${mon}") (cfg.monitors or {}); secondaryMons = removeAttrs monitors [(cfg.mainMonitor or "")]; monList = lib.concatLists (lib.attrValues secondaryMons); firstWsToMons = lib.zipListsWith (mon: ws: { name = toString ws; value = mon; }) monList secondaryWs; leftover = lib.sublist (lib.length monList) (10 - (wsPerMonitor * numMonitors)) secondaryWs; wsToMons = firstWsToMons ++ (map (ws: { name = toString ws; value = lib.last monList; }) leftover) ++ (map (ws: { name = toString ws; value = cfg.mainMonitor or ""; }) mainMonWs); wsMons = lib.listToAttrs wsToMons; in { monitor = lib.attrValues cfg.monitors; "$terminal" = lib.getExe config.marleyos.apps.terminal; "$launcher" = let inherit (config.marleyos.apps) launcher; in "${lib.getExe launcher.package} ${launcher.command}"; "$browser" = lib.getExe config.marleyos.apps.browser; exec-once = let browserWs = if (cfg ? monitors) then wsPerMonitor + 1 else 2; in [ (lib.getExe config.programs.waybar.package) "[workspace 1 silent] $terminal" "[workspace ${toString browserWs} silent] $browser" ]; # Fix WM thinking the cursor is bigger than it is, causing the pointer # to be offset. env = [ "XCURSOR_SIZE,24" "HYPRCURSOR_SIZE,24" ] ++ (lib.optional config.rose-pine.enable "HYPRCURSOR_THEME,rose-pine-hyprcursor"); workspace = lib.mkIf (cfg ? monitors) (map (ws: "${toString ws}, monitor:${wsMons."${toString ws}"}") workspaces); "$main" = lib.mkIf config.rose-pine.enable "$love"; "$inactive" = lib.mkIf config.rose-pine.enable "$muted"; general = { gaps_in = 5; gaps_out = 20; border_size = 2; "col.inactive_border" = "$inactive"; "col.active_border" = lib.mkIf config.rose-pine.enable "$rose $pine $love $iris 90deg"; resize_on_border = false; layout = "dwindle"; }; decoration = { rounding = 10; shadow = { enabled = true; range = 4; render_power = 3; color = "rgba(1a1a1aee)"; }; blur = { enabled = true; size = 3; passes = 1; vibrancy = 0.1696; }; }; animations = { enabled = true; bezier = [ "easeOutQuint,0.23,1,0.32,1" "easeInOutCubic,0.65,0.05,0.36,1" "linear,0,0,1,1" "almostLinear,0.5,0.5,0.75,1.0" "quick,0.15,0,0.1,1" ]; animation = [ "global, 1, 10, default" "border, 1, 5.39, easeOutQuint" "windows, 1, 4.79, easeOutQuint" "windowsIn, 1, 4.1, easeOutQuint, popin 87%" "windowsOut, 1, 1.49, linear, popin 87%" "fadeIn, 1, 1.73, almostLinear" "fadeOut, 1, 1.46, almostLinear" "fade, 1, 3.03, quick" "layers, 1, 3.81, easeOutQuint" "layersIn, 1, 4, easeOutQuint, fade" "layersOut, 1, 1.5, linear, fade" "fadeLayersIn, 1, 1.79, almostLinear" "fadeLayersOut, 1, 1.39, almostLinear" "workspaces, 1, 1.94, almostLinear, fade" "workspacesIn, 1, 1.21, almostLinear, fade" "workspacesOut, 1, 1.94, almostLinear, fade" ]; }; dwindle = { pseudotile = true; preserve_split = true; }; input = { kb_layout = "us"; numlock_by_default = true; follow_mouse = 2; }; "$mod" = "SUPER"; bind = [ "$mod, Q, killactive," "$mod SHIFT, E, exit," "$mod SHIFT, SPACE, togglefloating" "$mod, E, togglesplit" "$mod, RETURN, exec, $terminal" "$mod, R, exec, $launcher" "$mod, h, movefocus, l" "$mod, j, movefocus, d" "$mod, k, movefocus, u" "$mod, l, movefocus, r" "$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 SHIFT, 1, movetoworkspace, 1" "$mod SHIFT, 2, movetoworkspace, 2" "$mod SHIFT, 3, movetoworkspace, 3" "$mod SHIFT, 4, movetoworkspace, 4" "$mod SHIFT, 5, movetoworkspace, 5" "$mod SHIFT, 6, movetoworkspace, 6" "$mod SHIFT, 7, movetoworkspace, 7" "$mod SHIFT, 8, movetoworkspace, 8" "$mod SHIFT, 9, movetoworkspace, 9" "$mod SHIFT, 0, movetoworkspace, 10" "$mod, mouse_down, workspace, e+1" "$mod, mouse_up, workspace, e-1" ]; bindm = [ "$mod, mouse:272, movewindow" "$mod, mouse:273, resizewindow" ]; windowrulev2 = [ # Ignore maximize requests from apps. "suppressevent maximize, class:.*" # Fix some dragging issues with XWayland. "nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0" ]; }; }; }; }