{ inputs , lib , config , pkgs , ... }: { options = let inherit (lib) mkOption types; in { hyprland = { mod = mkOption { type = types.str; default = "SUPER"; }; autoStart = mkOption { type = types.listOf types.str; default = [ ]; }; 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 = [ ]; }; environment = mkOption { type = types.attrsOf types.str; default = { }; }; sensitivity = mkOption { type = types.float; default = 0.0; }; keyboard = { layout = mkOption { type = types.str; default = "us"; }; variant = mkOption { type = types.str; default = "qwerty"; }; options = mkOption { type = types.str; default = ""; }; }; inner_gaps = mkOption { type = types.int; }; outer_gaps = mkOption { type = types.int; }; border = { size = mkOption { type = types.int; }; active = mkOption { type = types.str; }; inactive = mkOption { type = types.str; }; }; animations = { enable = mkOption { type = types.bool; default = true; }; beziers = mkOption { type = types.listOf types.str; default = [ "myBezier, 0.05, 0.9, 0.1, 1.05" ]; }; windows = mkOption { type = types.str; default = "1, 3, myBezier"; }; windowsOut = mkOption { type = types.str; default = "1, 3, default, popin 80%"; }; border = mkOption { type = types.str; default = "1, 5, default"; }; borderangle = mkOption { type = types.str; default = "1, 4, default"; }; fade = mkOption { type = types.str; default = "1, 3, default"; }; workspaces = mkOption { type = types.str; default = "1, 2, default"; }; }; layout = mkOption { type = types.str; }; rounding = mkOption { type = types.int; }; blur = { enable = mkOption { type = types.bool; default = true; }; size = mkOption { type = types.int; default = 3; }; passes = mkOption { type = types.int; default = 1; }; }; shadow = { enable = mkOption { type = types.bool; default = true; }; range = mkOption { type = types.int; default = 4; }; strength = mkOption { type = types.int; default = 3; }; color = mkOption { type = types.str; default = "rgba(1a1a1aee)"; }; }; 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 = { }; }; generateWorkspaceBinds = mkOption { type = types.bool; default = true; }; generateWindowManagementBinds = mkOption { type = types.bool; default = true; }; binds = mkOption { type = types.attrsOf types.str; default = { }; }; mouseBinds = mkOption { type = types.attrsOf types.str; default = { }; }; }; }; config = let cfg = config.hyprland; in { home.packages = with pkgs; [ wl-clipboard cliphist ulauncher ]; wayland.windowManager.hyprland = { enable = true; 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" ]; exec-once = [ "${pkgs.wl-clipboard}/bin/wl-paste --watch ${pkgs.cliphist}/bin/cliphist store" "${pkgs.ulauncher}/bin/ulauncher --no-window-shadow --hide-window" "${pkgs.libsForQt5.polkit-kde-agent}/libexec/polkit-kde-authentication-agent-1" # kde-connect (somehow, wasnt working well last time) ] ++ cfg.autoStart; env = lib.mapAttrsToList (k: v: "${toString k},${v}") cfg.environment; input = { kb_layout = cfg.keyboard.layout; kb_variant = cfg.keyboard.variant; kb_options = cfg.keyboard.options; follow_mouse = 1; touchpad = { natural_scroll = "yes"; }; sensitivity = cfg.sensitivity; accel_profile = "flat"; }; general = { gaps_in = cfg.inner_gaps; gaps_out = cfg.outer_gaps; border_size = cfg.border.size; "col.active_border" = cfg.border.active; "col.inactive_border" = cfg.border.inactive; layout = cfg.layout; }; misc = { force_default_wallpaper = 2; }; decoration = { rounding = cfg.rounding; blur = { enabled = if cfg.blur.enable then "yes" else "no"; size = cfg.blur.size; passes = cfg.blur.passes; new_optimizations = "on"; }; drop_shadow = if cfg.shadow.enable then "yes" else "no"; shadow_range = cfg.shadow.range; shadow_render_power = cfg.shadow.strength; "col.shadow" = cfg.shadow.color; }; dwindle = { pseudotile = "yes"; preserve_split = "yes"; }; master = { new_is_master = true; }; gestures = { workspace_swipe = "on"; }; animations = { enabled = if cfg.animations.enable then "yes" else "no"; # Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more bezier = cfg.animations.beziers; animation = [ "windows, ${cfg.animations.windows}" "windowsOut, ${cfg.animations.windowsOut}" "border, ${cfg.animations.border}" "borderangle, ${cfg.animations.borderangle}" "fade, ${cfg.animations.fade}" "workspaces, ${cfg.animations.workspaces}" ]; }; 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: "${m.name}, name:${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 = if cfg.generateWorkspaceBinds then 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) ) else [ ]; # Create binds to manage wiwdows windowManagementBinds = if cfg.generateWindowManagementBinds then [ "${cfg.mod} SHIFT, Q, killactive" "${cfg.mod}, F, togglefloating" "${cfg.mod} SHIFT, F, fullscreen" ] else [ ]; configBinds = lib.mapAttrsToList (k: v: "${k}, ${v}") cfg.binds; in configBinds ++ workspaceBinds ++ windowManagementBinds; bindm = let windowManagementBinds = if cfg.generateWindowManagementBinds then [ "${cfg.mod}, mouse:272, movewindow" "${cfg.mod}, mouse:273, resizewindow" ] else [ ]; configBinds = lib.mapAttrsToList (k: v: "${k}, ${v}") cfg.mouseBinds; in configBinds ++ windowManagementBinds; 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); }; }; }; }