{ lib, config, ... }: let inherit (lib) mkOption mkIf types trivial strings replaceStrings; inherit (builtins) concatStringsSep elem; cfg = config.horseman.wm.hyprland; homeCfg = config.horseman; keybinding = types.submodule { options = { flags = mkOption { type = types.listOf (types.enum ["l" "r" "c" "g" "o" "e" "m" "t" "i" "s" "d" "p"]); }; mods = mkOption { type = types.listOf types.str; }; key = mkOption { type = types.str; }; dispatcher = mkOption { type = types.str; }; params = mkOption { type = types.str; }; }; }; submapKeybinding = types.submodule { options = { flags = mkOption { type = types.listOf (types.enum ["l" "r" "c" "g" "o" "e" "m" "t" "i" "s" "d" "p"]); }; mods = mkOption { type = types.listOf types.str; }; key = mkOption { type = types.str; }; }; }; in { options = { horseman.wm.hyprland.config = { execOnce = mkOption {type = types.listOf types.str;}; env = mkOption {type = types.listOf types.str;}; windowrules = mkOption {type = types.listOf types.str;}; workspaces = mkOption { type = types.listOf types.str; }; general = { gapsIn = mkOption {type = types.int;}; gapsOut = mkOption {type = types.int;}; borderSize = mkOption {type = types.int;}; col.activeBorder = mkOption {type = types.str;}; col.inactiveBorder = mkOption {type = types.str;}; resizeOnBorder = mkOption {type = types.bool;}; allowTearing = mkOption {type = types.bool;}; layout = mkOption {type = types.enum ["dwindle" "master"];}; }; decoration = { rounding = mkOption {type = types.int;}; activeOpacity = mkOption {type = types.float;}; inactiveOpacity = mkOption {type = types.float;}; shadow = { enabled = mkOption {type = types.bool;}; range = mkOption {type = types.int;}; renderPower = mkOption {type = types.int;}; color = mkOption {type = types.str;}; }; blur = { enabled = mkOption {type = types.bool;}; size = mkOption {type = types.int;}; passes = mkOption {type = types.int;}; vibrancy = mkOption {type = types.float;}; }; }; animations = { enabled = mkOption { type = types.bool; }; beziers = mkOption { type = types.listOf (types.submodule { options = { name = mkOption {type = types.str;}; startX = mkOption {type = types.number;}; startY = mkOption {type = types.number;}; endX = mkOption {type = types.number;}; endY = mkOption {type = types.number;}; }; }); }; animations = mkOption { type = types.listOf (types.submodule { options = { name = mkOption {type = types.str;}; on = mkOption {type = types.bool;}; speed = mkOption { type = types.number; default = 0; }; curve = mkOption { type = types.str; default = ""; }; style = mkOption { type = types.str; default = ""; }; }; }); }; }; dwindle = { pseudotile = mkOption {type = types.bool;}; preserveSplit = mkOption {type = types.bool;}; }; master = { newStatus = mkOption {type = types.enum ["master" "slave" "inherit"];}; }; misc = { forceDefaultWallpaper = mkOption {type = types.enum [0 1 2 (-1)];}; keyPressEnablesDPMS = mkOption {type = types.bool;}; }; input = { kbLayout = mkOption {type = types.str;}; repeatRate = mkOption {type = types.int;}; repeatDelay = mkOption {type = types.int;}; followMouse = mkOption {type = types.enum [0 1 2 3];}; sensitivity = mkOption {type = types.numbers.between (-1.0) 1.0;}; numlockByDefault = mkOption {type = types.bool;}; touchpad = { naturalScroll = mkOption {type = types.bool;}; }; }; gestures = { workspaceSwipe = mkOption {type = types.bool;}; }; monitors = { defaultMonitor = mkOption {type = types.bool;}; displays = mkOption { type = types.listOf (types.submodule { options = { output = mkOption {type = types.str;}; resolution = mkOption { type = types.str; default = "highres"; }; refreshRate = mkOption { type = types.str; default = "highrr"; }; x = mkOption {type = types.int;}; y = mkOption {type = types.int;}; scale = mkOption { type = types.number; default = 1; }; transform = mkOption { type = types.enum [0 1 2 3 4 5 6 7]; default = 0; }; wallpaper = mkOption { type = types.nullOr types.path; default = null; }; }; }); }; }; keybindings = { binds = mkOption {type = types.listOf keybinding;}; submaps = mkOption { type = types.listOf (types.submodule { options = { name = mkOption {type = types.str;}; enterBind = mkOption {type = submapKeybinding;}; exitBind = mkOption {type = submapKeybinding;}; binds = mkOption {type = types.listOf keybinding;}; }; }); }; }; sleep = { lockCommand = mkOption {type = types.str;}; listeners = mkOption { type = types.listOf (types.submodule { options = { timeout = mkOption {type = types.ints.positive;}; onTimeout = mkOption {type = types.str;}; onResume = mkOption { type = types.str; default = ""; }; ignoreInhibit = mkOption { type = types.bool; default = false; }; }; }); }; }; }; }; config = mkIf cfg.enable { home-manager.users.${homeCfg.username} = { xdg.configFile."hypr/hypridle.conf".text = '' # This file has been auto-generated. general { lock_cmd = ${cfg.config.sleep.lockCommand} } ${concatStringsSep "\n\n" (map (lis: '' listener { timeout = ${toString lis.timeout} on-timeout = ${lis.onTimeout} on-resume = ${lis.onResume} } '') cfg.config.sleep.listeners)} ''; xdg.configFile."hypr/hyprpaper.conf".text = '' # This file has been auto-generated. ${concatStringsSep "\n" (map (dis: if dis.wallpaper != null then '' preload = ${dis.wallpaper} wallpaper = ${dis.output}, ${dis.wallpaper} '' else "") cfg.config.monitors.displays)} preload = /home/horseman/nix-config/misc/wallpaper.jpg wallpaper = , /home/horseman/nix-config/misc/wallpaper.jpg ''; xdg.configFile."hypr/hyprland.conf".text = '' # This file has been auto-generated. ${concatStringsSep "\n" (map (kb: "bind${concatStringsSep "" kb.flags} = ${concatStringsSep " " kb.mods}, ${kb.key}, ${kb.dispatcher}${ if (elem "m" kb.flags) # Mouse binds take one argument less then "" else ", ${replaceStrings ["%"] ["%%"] kb.params}" }") cfg.config.keybindings.binds)} ${concatStringsSep "\n\n" (map (sm: '' bind${concatStringsSep "" sm.enterBind.flags} = ${concatStringsSep " " sm.enterBind.mods}, ${sm.enterBind.key}, submap, ${sm.name} submap = ${sm.name} ${concatStringsSep "\n" (map (kb: "bind${concatStringsSep "" kb.flags} = ${concatStringsSep " " kb.mods}, ${kb.key}, ${kb.dispatcher}${ if (elem "m" kb.flags) # Mouse binds take one argument less then "" else ", ${replaceStrings ["%"] ["%%"] kb.params}" }") sm.binds)} bind${concatStringsSep "" sm.exitBind.flags} = ${concatStringsSep " " sm.exitBind.mods}, ${sm.exitBind.key}, submap, reset submap = reset '') cfg.config.keybindings.submaps)} ${concatStringsSep "\n" (map (x: "exec-once = " + x) cfg.config.execOnce)} ${concatStringsSep "\n" (map (x: "env = " + x) cfg.config.env)} general { gaps_in = ${toString cfg.config.general.gapsIn} gaps_out = ${toString cfg.config.general.gapsOut} border_size = ${toString cfg.config.general.borderSize} col.active_border = ${cfg.config.general.col.activeBorder} col.inactive_border = ${cfg.config.general.col.inactiveBorder} resize_on_border = ${trivial.boolToString cfg.config.general.resizeOnBorder} allow_tearing = ${trivial.boolToString cfg.config.general.allowTearing} layout = ${cfg.config.general.layout} } decoration { rounding = ${toString cfg.config.decoration.rounding} active_opacity = ${strings.floatToString cfg.config.decoration.activeOpacity} inactive_opacity = ${strings.floatToString cfg.config.decoration.inactiveOpacity} shadow { enabled = ${trivial.boolToString cfg.config.decoration.shadow.enabled} range = ${toString cfg.config.decoration.shadow.range} render_power = ${toString cfg.config.decoration.shadow.renderPower} color = ${cfg.config.decoration.shadow.color} } blur { enabled = ${trivial.boolToString cfg.config.decoration.blur.enabled} size = ${toString cfg.config.decoration.blur.size} passes = ${toString cfg.config.decoration.blur.passes} vibrancy = ${strings.floatToString cfg.config.decoration.blur.vibrancy} } } animations { enabled = ${trivial.boolToString cfg.config.animations.enabled} ${concatStringsSep "\n " (map (bez: "bezier = ${bez.name}, ${strings.floatToString bez.startX}, ${strings.floatToString bez.startY}, ${strings.floatToString bez.endX}, ${strings.floatToString bez.endY}") cfg.config.animations.beziers)} ${concatStringsSep "\n " (map (anim: "animation = ${anim.name}, ${toString anim.on}, ${strings.floatToString anim.speed}, ${anim.curve}, ${replaceStrings ["%"] ["%%"] anim.style}") cfg.config.animations.animations)} } dwindle { pseudotile = ${trivial.boolToString cfg.config.dwindle.pseudotile} preserve_split = ${trivial.boolToString cfg.config.dwindle.preserveSplit} } master { new_status = ${cfg.config.master.newStatus} } misc { force_default_wallpaper = ${toString cfg.config.misc.forceDefaultWallpaper} key_press_enables_dpms = ${trivial.boolToString cfg.config.misc.keyPressEnablesDPMS} } input { kb_layout = ${cfg.config.input.kbLayout} repeat_rate = ${toString cfg.config.input.repeatRate} repeat_delay = ${toString cfg.config.input.repeatDelay} follow_mouse = ${toString cfg.config.input.followMouse} sensitivity = ${strings.floatToString cfg.config.input.sensitivity} numlock_by_default = ${trivial.boolToString cfg.config.input.numlockByDefault} touchpad { natural_scroll = ${trivial.boolToString cfg.config.input.touchpad.naturalScroll} } } gestures { workspace_swipe = ${trivial.boolToString cfg.config.gestures.workspaceSwipe} } ${concatStringsSep "\n" (map (mon: "monitor = ${mon.output}, ${mon.resolution}@${mon.refreshRate}, ${toString mon.x}x${toString mon.y}, ${strings.floatToString mon.scale}, transform, ${toString mon.transform}") cfg.config.monitors.displays)} ${ if cfg.config.monitors.defaultMonitor then "monitor = ,preferred, auto, auto" else "" } ${concatStringsSep "\n" (map (ws: "workspace = " + ws) cfg.config.workspaces)} ${concatStringsSep "\n" (map (wr: "windowrule = " + replaceStrings ["%"] ["%%"] wr) cfg.config.windowrules)} ''; }; xdg.configFile."hypr/hyprlock.conf".text = '' $font = Monospace general { hide_cursor = false } animations { enabled = true bezier = linear, 1, 1, 0, 0 animation = fadeIn, 1, 5, linear animation = fadeOut, 1, 5, linear animation = inputFieldDots, 1, 2, linear } background { monitor = path = screenshot blur_passes = 3 } input-field { monitor = size = 20%, 5% outline_thickness = 3 inner_color = rgba(0, 0, 0, 0.0) # no fill outer_color = rgba(33ccffee) rgba(00ff99ee) 45deg check_color = rgba(00ff99ee) rgba(ff6633ee) 120deg fail_color = rgba(ff6633ee) rgba(ff0066ee) 40deg font_color = rgb(143, 143, 143) fade_on_empty = false rounding = 15 font_family = $font placeholder_text = Input password... fail_text = $PAMFAIL dots_spacing = 0.3 position = 0, -20 halign = center valign = center } label { monitor = text = $TIME font_size = 90 font_family = $font position = 0, -50 halign = center valign = top } ''; }; }