{ inputs, pkgs, lib, config, ... }: { options = let inherit (lib) mkOption types; in { hyprland = { mod = mkOption { type = types.str; default = "SUPER"; }; primaryMonitor = mkOption { type = types.str; }; monitors = mkOption { type = types.listOf ( types.submodule { options = { name = mkOption { type = types.str; }; width = mkOption { type = types.int; }; height = mkOption { type = types.int; }; refreshRate = mkOption { type = types.int; default = 60; }; x = mkOption { type = types.int; default = 0; }; y = mkOption { type = types.int; default = 0; }; transform = mkOption { type = types.int; default = 0; }; vrr = mkOption { type = types.int; default = 0; }; enabled = mkOption { type = types.bool; default = true; }; }; } ); default = [ ]; }; autoStart = mkOption { type = types.listOf types.str; default = [ ]; }; sensitivity = mkOption { type = types.float; default = 0.0; }; layerRules = mkOption { type = types.attrsOf (types.listOf types.str); default = { }; }; windowRules = mkOption { type = types.attrsOf (types.listOf types.str); default = { }; }; windowRulesV2 = mkOption { type = types.attrsOf (types.listOf types.str); default = { }; }; binds = mkOption { type = types.attrsOf types.str; default = { }; }; }; }; config = let cfg = config.hyprland; in { nix.settings = { substituters = [ "https://hyprland.cachix.org" ]; trusted-public-keys = [ "hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc=" ]; }; programs.hyprland = { enable = true; withUWSM = true; package = inputs.hyprland.packages.x86_64-linux.hyprland; }; xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; environment.systemPackages = with pkgs; [ grim # For xdg-desktop-portal-hyprland which does not declare this dependency slurp # Some for this one ]; home-manager.users.kalle = { home.packages = with pkgs; [ wl-clipboard cliphist ulauncher ]; wayland.windowManager.hyprland = { enable = true; # Disable systemd integration since we are using UWSM systemd.enable = false; settings = { monitor = map ( m: let resolution = "${toString m.width}x${toString m.height}@${toString m.refreshRate}"; position = "${toString m.x}x${toString m.y}"; vrr = if m.vrr != 0 then "vrr,${toString m.vrr}" else ""; transform = if m.transform != 0 then "transform,${toString m.transform}" else ""; in "${m.name},${if m.enabled then "${resolution},${position},1,${vrr}${transform}" else "disable"}" ) (cfg.monitors) # Automatically detect newly connected monitors ++ [ ",preferred,auto,auto" ]; # TODO: Make these start via UWSM exec-once = [ "${pkgs.wl-clipboard}/bin/wl-paste --watch ${pkgs.cliphist}/bin/cliphist store" "${pkgs.ulauncher}/bin/ulauncher --no-window-shadow --hide-window" # TODO: Replace this with hyprland's polkit agent "${pkgs.libsForQt5.polkit-kde-agent}/libexec/polkit-kde-authentication-agent-1" ] ++ cfg.autoStart; env = [ "XCURSOR_SIZE,24" ]; input = { kb_layout = "us"; kb_variant = ""; kb_options = ""; follow_mouse = 1; touchpad = { natural_scroll = "yes"; }; sensitivity = cfg.sensitivity; accel_profile = "flat"; }; general = { gaps_in = 5; gaps_out = 10; border_size = 1; # TODO: Make this use system color scheme "col.active_border" = "rgba(33ccffee) rgba(00ff99ee) 45deg"; "col.inactive_border" = "rgba(595959aa)"; layout = "dwindle"; }; misc.force_default_wallpaper = 1; decoration = { rounding = 5; blur = { enabled = "yes"; size = 3; passes = 1; new_optimizations = "on"; }; }; animations = { enabled = "yes"; bezier = [ "myBezier, 0.05, 0.9, 0.1, 1.05" ]; animation = [ "windows, 1, 3, myBezier" "windowsOut, 1, 3, default, popin 80%" "border, 1, 5, default" "borderangle, 1, 4, default" "fade, 1, 3, default" "workspaces, 1, 2, default" ]; }; layerrule = let y = ident: value: "${value},${ident}"; x = ident: values: map (y ident) values; in lib.flatten (lib.mapAttrsToList x cfg.layerRules); workspace = let # Create one work space for each non primary monitor perMonitorWorkspaces = map (m: "name:${m.name}, monitor:${m.name}") ( lib.filter (m: m.name != cfg.primaryMonitor) cfg.monitors ); # Create 10 work spaces on the primary monitor primaryMonitorWorkspaces = map (i: "${toString i}, monitor:${cfg.primaryMonitor}") (lib.range 1 10); in primaryMonitorWorkspaces ++ perMonitorWorkspaces; bind = let # Create binds to switch workspaces and move windows between them workspaceBinds = lib.flatten ( map ( i: let k = if i == 10 then 0 else i; in [ "${cfg.mod}, ${toString k}, workspace, ${toString i}" "${cfg.mod} SHIFT, ${toString k}, movetoworkspace, ${toString i}" ] ) (lib.range 1 10) ); # Create binds to manage windows windowManagementBinds = [ "${cfg.mod} SHIFT, Q, killactive" "${cfg.mod}, F, togglefloating" "${cfg.mod} SHIFT, F, fullscreen" ]; configBinds = lib.mapAttrsToList (k: v: "${k}, ${v}") cfg.binds; in configBinds ++ workspaceBinds ++ windowManagementBinds; bindm = [ "${cfg.mod}, mouse:272, movewindow" "${cfg.mod}, mouse:273, resizewindow" ]; windowrule = let y = ident: value: "${value},${ident}"; x = ident: values: map (y ident) values; in lib.flatten (lib.mapAttrsToList x cfg.windowRules); windowrulev2 = let y = ident: value: "${value},${ident}"; x = ident: values: map (y ident) values; in lib.flatten (lib.mapAttrsToList x cfg.windowRulesV2); }; }; }; }; }