diff --git a/flake.lock b/flake.lock index c86583c..d651973 100644 --- a/flake.lock +++ b/flake.lock @@ -279,6 +279,26 @@ } }, "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730633670, + "narHash": "sha256-ZFJqIXpvVKvzOVFKWNRDyIyAo+GYdmEPaYi1bZB6uf0=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "8f6ca7855d409aeebe2a582c6fd6b6a8d0bf5661", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "home-manager_2": { "inputs": { "nixpkgs": [ "schizofox", @@ -729,6 +749,7 @@ "root": { "inputs": { "helix": "helix", + "home-manager": "home-manager", "hyprland": "hyprland", "hyprland-plugins": "hyprland-plugins", "hyprsplit": "hyprsplit", @@ -766,7 +787,7 @@ "inputs": { "flake-compat": "flake-compat_2", "flake-parts": "flake-parts", - "home-manager": "home-manager", + "home-manager": "home-manager_2", "nixpak": "nixpak", "nixpkgs": [ "nixpkgs" diff --git a/flake.nix b/flake.nix index dc208e6..90dc764 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,10 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; # Hyprland, my main compositor hyprland.url = "github:hyprwm/Hyprland"; diff --git a/modules/other/module.nix b/modules/other/module.nix index 7c23bd0..044212a 100644 --- a/modules/other/module.nix +++ b/modules/other/module.nix @@ -1,6 +1,6 @@ _: { imports = [ - # ./home-manager.nix + ./home-manager.nix ./system.nix ./xdg.nix ./git.nix diff --git a/modules/wms/wayland/hyprland/binds.nix b/modules/wms/wayland/hyprland/binds.nix index d56b291..fcca1f4 100644 --- a/modules/wms/wayland/hyprland/binds.nix +++ b/modules/wms/wayland/hyprland/binds.nix @@ -4,10 +4,9 @@ ... }: let cfg = config.modules.usrEnv.desktops.hyprland; - inherit (config.modules.other.system) username; inherit (builtins) map genList toString; in { - home-manager.users.${username}.wayland.windowManager.hyprland.settings = { + programs.hyprland.settings = { # Keybinds bind = # workspaces diff --git a/modules/wms/wayland/hyprland/decorations.nix b/modules/wms/wayland/hyprland/decorations.nix index 45f3887..5687324 100644 --- a/modules/wms/wayland/hyprland/decorations.nix +++ b/modules/wms/wayland/hyprland/decorations.nix @@ -1,70 +1,68 @@ {config, ...}: let inherit (config.modules.other.system) username; in { - home-manager.users.${username} = { - wayland.windowManager.hyprland.settings = { - #Decoration settings - decoration = { - rounding = 0; - blur = { - enabled = true; - size = 3; - passes = 2; - }; - drop_shadow = 1; - shadow_range = 15; - shadow_render_power = 2; - shadow_ignore_window = 1; - shadow_offset = "2 4"; - shadow_scale = 1; + programs.hyprland.settings = { + #Decoration settings + decoration = { + rounding = 0; + blur = { + enabled = true; + size = 3; + passes = 2; }; - # Bezier curves for aninmations. - # Generate your own at https://www.cssportal.com/css-cubic-bezier-generator/ - bezier = [ - "dupa, 0.1, 0.9, 0.1, 1.05" - ]; - # Hyprland anomations, using the above bezier curves - animations = { - enabled = false; - animation = [ - "windows, 1, 4, dupa, popin" - "windowsOut, 1, 4, dupa, slide" - "border, 1, 15, default" - "fade, 1, 10, default" - "workspaces, 1, 5, dupa, slidevert" - ]; - }; - - cursor = { - hide_on_key_press = true; - no_hardware_cursors = true; - }; - - misc = { - enable_swallow = true; - swallow_regex = "foot"; - focus_on_activate = true; - vrr = 1; - vfr = true; - animate_manual_resizes = false; - animate_mouse_windowdragging = false; - force_default_wallpaper = 0; - }; - - # Window rules for some programs. - windowrulev2 = [ - "float, class:^(Tor Browser)$" - "float, class:^(mpv)$" - "float, class:^(imv)$" - "float, title:^(Picture-in-Picture)$" - "float, title:^(.*)(Choose User Profile)(.*)$" - "float, title:^(blob:null/)(.*)$" - "float, class:^(xdg-desktop-portal-gtk)$" - "float, class:^(code), title: ^(Open*)" - "size 70% 70%, class:^(code), title: ^(Open*)" - "center, class: ^(code), title: ^(Open*)" - "float, class:^(org.keepassxc.KeePassXC)$" + drop_shadow = 1; + shadow_range = 15; + shadow_render_power = 2; + shadow_ignore_window = 1; + shadow_offset = "2 4"; + shadow_scale = 1; + }; + # Bezier curves for aninmations. + # Generate your own at https://www.cssportal.com/css-cubic-bezier-generator/ + bezier = [ + "dupa, 0.1, 0.9, 0.1, 1.05" + ]; + # Hyprland anomations, using the above bezier curves + animations = { + enabled = false; + animation = [ + "windows, 1, 4, dupa, popin" + "windowsOut, 1, 4, dupa, slide" + "border, 1, 15, default" + "fade, 1, 10, default" + "workspaces, 1, 5, dupa, slidevert" ]; }; + + cursor = { + hide_on_key_press = true; + no_hardware_cursors = true; + }; + + misc = { + enable_swallow = true; + swallow_regex = "foot"; + focus_on_activate = true; + vrr = 1; + vfr = true; + animate_manual_resizes = false; + animate_mouse_windowdragging = false; + force_default_wallpaper = 0; + }; + + # Window rules for some programs. + windowrulev2 = [ + "float, class:^(Tor Browser)$" + "float, class:^(mpv)$" + "float, class:^(imv)$" + "float, title:^(Picture-in-Picture)$" + "float, title:^(.*)(Choose User Profile)(.*)$" + "float, title:^(blob:null/)(.*)$" + "float, class:^(xdg-desktop-portal-gtk)$" + "float, class:^(code), title: ^(Open*)" + "size 70% 70%, class:^(code), title: ^(Open*)" + "center, class: ^(code), title: ^(Open*)" + "float, class:^(org.keepassxc.KeePassXC)$" + ]; }; } diff --git a/modules/wms/wayland/hyprland/exec.nix b/modules/wms/wayland/hyprland/exec.nix index 1181777..a49283b 100644 --- a/modules/wms/wayland/hyprland/exec.nix +++ b/modules/wms/wayland/hyprland/exec.nix @@ -3,32 +3,29 @@ pkgs, ... }: let - inherit (config.modules.other.system) username; inherit (config.modules.style) cursor; inherit (builtins) toString; in { - home-manager.users.${username} = { - wayland.windowManager.hyprland.settings = { - # Hyprland settings - # Programs which get executed at Hyprland start. - exec-once = [ - "hyprctl setcursor ${cursor.name} ${toString cursor.size}" - #start waybar - "${pkgs.waybar}/bin/waybar" - # "${pkgs.ianny}/bin/ianny" + programs.hyprland.settings = { + # Hyprland settings + # Programs which get executed at Hyprland start. + exec-once = [ + "hyprctl setcursor ${cursor.name} ${toString cursor.size}" + #start waybar + "${pkgs.waybar}/bin/waybar" + # "${pkgs.ianny}/bin/ianny" - # run persistent special workspace windows - # "[workspace special:nixos; silent;tile] ${pkgs.foot}/bin/foot -D ~/projects/nichts" + # run persistent special workspace windows + # "[workspace special:nixos; silent;tile] ${pkgs.foot}/bin/foot -D ~/projects/nichts" - "[workspace special:keepassxc; silent;tile] ${pkgs.keepassxc}/bin/keepassxc" - "[workspace special:audio; silent;tile] ${pkgs.pwvucontrol}/bin/pwvucontrol" + "[workspace special:keepassxc; silent;tile] ${pkgs.keepassxc}/bin/keepassxc" + "[workspace special:audio; silent;tile] ${pkgs.pwvucontrol}/bin/pwvucontrol" - "${pkgs.swww}/bin/swww-daemon" + "${pkgs.swww}/bin/swww-daemon" - "${pkgs.wlsunset}/bin/wlsunset -S 06:00 -s 20:00" - "${pkgs.lxqt.lxqt-policykit}/bin/lxqt-policykit-agent" - "hyprctl dispatch split:workspace 1" - ]; - }; + "${pkgs.wlsunset}/bin/wlsunset -S 06:00 -s 20:00" + "${pkgs.lxqt.lxqt-policykit}/bin/lxqt-policykit-agent" + "hyprctl dispatch split:workspace 1" + ]; }; } diff --git a/modules/wms/wayland/hyprland/module.nix b/modules/wms/wayland/hyprland/module.nix index 3209299..1707eff 100644 --- a/modules/wms/wayland/hyprland/module.nix +++ b/modules/wms/wayland/hyprland/module.nix @@ -5,7 +5,6 @@ ... }: let cfg = config.modules.usrEnv.desktops.hyprland; - inherit (config.meta.mainUser) username; # inherit (inputs.hyprsplit.packages.${pkgs.system}) hyprsplit; inherit (lib) mkIf mkDefault; in { @@ -15,6 +14,7 @@ in { ./exec.nix ./settings.nix ./workspaces.nix + ./nixos-module.nix ]; # we disable the default hyprland module disabledModules = ["programs/hyprland.nix"]; @@ -22,8 +22,13 @@ in { config = mkIf cfg.enable { programs.hyprland = { enable = true; + xwayland.enable = true; package = pkgs.hyprland; portalPackage = pkgs.xdg-desktop-portal-hyprland; + plugins = [ + pkgs.hyprlandPlugins.hyprsplit + # pkgs.hyprlandPlugins.hypr-dynamic-cursors + ]; }; # xdg Portal xdg.portal = { @@ -39,26 +44,5 @@ in { common.default = ["gtk" "hyprland"]; }; }; - - home-manager.users.${username} = { - wayland.windowManager.hyprland = { - enable = true; - package = pkgs.hyprland; - - # Split-monitor-workspaces provides awesome-like workspace behaviour - plugins = [ - pkgs.hyprlandPlugins.hyprsplit - pkgs.hyprlandPlugins.hypr-dynamic-cursors - ]; - - # Xwayland for X applications - xwayland.enable = true; - # No idea why I set this - systemd = { - enable = true; - variables = ["--all"]; - }; - }; - }; }; } diff --git a/modules/wms/wayland/hyprland/nixos-module.nix b/modules/wms/wayland/hyprland/nixos-module.nix new file mode 100644 index 0000000..a77a27a --- /dev/null +++ b/modules/wms/wayland/hyprland/nixos-module.nix @@ -0,0 +1,204 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.programs.hyprland; + + toHyprconf = { + attrs, + indentLevel ? 0, + importantPrefixes ? ["$"], + }: let + inherit + (lib) + all + concatMapStringsSep + concatStrings + concatStringsSep + filterAttrs + foldl + generators + hasPrefix + isAttrs + isList + mapAttrsToList + replicate + ; + + initialIndent = concatStrings (replicate indentLevel " "); + + toHyprconf' = indent: attrs: let + sections = + filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs; + + mkSection = n: attrs: + if lib.isList attrs + then (concatMapStringsSep "\n" (a: mkSection n a) attrs) + else '' + ${indent}${n} { + ${toHyprconf' " ${indent}" attrs}${indent}} + ''; + + mkFields = generators.toKeyValue { + listsAsDuplicateKeys = true; + inherit indent; + }; + + allFields = + filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) + attrs; + + isImportantField = n: _: + foldl (acc: prev: + if hasPrefix prev n + then true + else acc) + false + importantPrefixes; + + importantFields = filterAttrs isImportantField allFields; + + fields = + builtins.removeAttrs allFields + (mapAttrsToList (n: _: n) importantFields); + in + mkFields importantFields + + concatStringsSep "\n" (mapAttrsToList mkSection sections) + + mkFields fields; + in + toHyprconf' initialIndent attrs; +in { + options.programs.hyprland = { + plugins = lib.mkOption { + type = with lib.types; listOf (either package path); + default = []; + description = '' + List of Hyprland plugins to use. Can either be packages or + absolute plugin paths. + ''; + }; + + settings = lib.mkOption { + type = with lib.types; let + valueType = + nullOr (oneOf [ + bool + int + float + str + path + (attrsOf valueType) + (listOf valueType) + ]) + // { + description = "Hyprland configuration value"; + }; + in + valueType; + default = {}; + description = '' + Hyprland configuration written in Nix. Entries with the same key + should be written as lists. Variables' and colors' names should be + quoted. See for more examples. + + ::: {.note} + Use the [](#opt-wayland.windowManager.hyprland.plugins) option to + declare plugins. + ::: + + ''; + example = lib.literalExpression '' + { + decoration = { + shadow_offset = "0 5"; + "col.shadow" = "rgba(00000099)"; + }; + + "$mod" = "SUPER"; + + bindm = [ + # mouse movements + "$mod, mouse:272, movewindow" + "$mod, mouse:273, resizewindow" + "$mod ALT, mouse:272, resizewindow" + ]; + } + ''; + }; + + extraConfig = lib.mkOption { + type = lib.types.lines; + default = ""; + example = '' + # window resize + bind = $mod, S, submap, resize + + submap = resize + binde = , right, resizeactive, 10 0 + binde = , left, resizeactive, -10 0 + binde = , up, resizeactive, 0 -10 + binde = , down, resizeactive, 0 10 + bind = , escape, submap, reset + submap = reset + ''; + description = '' + Extra configuration lines to add to `~/.config/hypr/hyprland.conf`. + ''; + }; + + sourceFirst = + lib.mkEnableOption '' + putting source entries at the top of the configuration + '' + // { + default = true; + }; + + importantPrefixes = lib.mkOption { + type = with lib.types; listOf str; + default = + ["$" "bezier" "name"] + ++ lib.optionals cfg.sourceFirst ["source"]; + example = ["$" "bezier"]; + description = '' + List of prefix of attributes to source at the top of the config. + ''; + }; + }; + config = lib.mkIf cfg.enable { + environment.systemPackages = lib.concatLists [ + (lib.optional (cfg.package != null) cfg.package) + (lib.optional (cfg.xwayland.enable) pkgs.xwayland) + ]; + environment.etc."xdg/hypr/hyprland.conf" = let + shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != []; + + pluginsToHyprconf = plugins: + toHyprconf { + attrs = { + plugin = let + mkEntry = entry: + if lib.types.package.check entry + then "${entry}/lib/lib${entry.pname}.so" + else entry; + in + map mkEntry cfg.plugins; + }; + inherit (cfg) importantPrefixes; + }; + in + lib.mkIf shouldGenerate { + text = + lib.optionalString (cfg.plugins != []) + (pluginsToHyprconf cfg.plugins) + + lib.optionalString (cfg.settings != {}) + (toHyprconf { + attrs = cfg.settings; + inherit (cfg) importantPrefixes; + }) + + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig; + }; + }; +} diff --git a/modules/wms/wayland/hyprland/settings.nix b/modules/wms/wayland/hyprland/settings.nix index 847d56f..86dec3c 100644 --- a/modules/wms/wayland/hyprland/settings.nix +++ b/modules/wms/wayland/hyprland/settings.nix @@ -9,68 +9,70 @@ inherit (builtins) toString; inherit (config.modules.style.colorScheme) colors; in { - home-manager.users.${username} = { - wayland.windowManager.hyprland.settings = { - # Hyprland settings - "$mainMod" = "SUPER"; + config = { + programs.hyprland = { + settings = { + # Hyprland settings + "$mainMod" = "SUPER"; - # Monitor config - # Thanks Poz for inspiration, using an attrSet is actually much smarter - # than using a normal list. - monitor = - mapAttrsToList ( - name: m: let - w = toString m.resolution.x; - h = toString m.resolution.y; - refreshRate = toString m.refreshRate; - x = toString m.position.x; - y = toString m.position.y; - scale = toString m.scale; - in "${name},${w}x${h}@${refreshRate},${x}x${y},${scale}" - ) - monitors; + # Monitor config + # Thanks Poz for inspiration, using an attrSet is actually much smarter + # than using a normal list. + monitor = + mapAttrsToList ( + name: m: let + w = toString m.resolution.x; + h = toString m.resolution.y; + refreshRate = toString m.refreshRate; + x = toString m.position.x; + y = toString m.position.y; + scale = toString m.scale; + in "${name},${w}x${h}@${refreshRate},${x}x${y},${scale}" + ) + monitors; - # Input settings - input = { - kb_layout = "de,ru, us"; - kb_variant = ",phonetic_winkeys,dvorak"; - kb_options = "grp:rctrl_rshift_toggle, caps:escape"; + # Input settings + input = { + kb_layout = "de,ru, us"; + kb_variant = ",phonetic_winkeys,dvorak"; + kb_options = "grp:rctrl_rshift_toggle, caps:escape"; - follow_mouse = true; + follow_mouse = true; - repeat_rate = 60; - repeat_delay = 200; + repeat_rate = 60; + repeat_delay = 200; - touchpad = { - disable_while_typing = true; - }; - }; - - general = { - layout = "dwindle"; - gaps_in = 0; - gaps_out = 0; - border_size = 2; - - "col.active_border" = "0xff${colors.base07}"; - no_border_on_floating = true; - }; - - plugin = { - hyprsplit = { - num_workspaces = 10; - persistent_workspaces = true; - }; - dynamic-cursors = { - enabled = true; - - mode = "rotate"; - rotate = { - length = 20; - offset = 0.0; + touchpad = { + disable_while_typing = true; + }; + }; + + general = { + layout = "dwindle"; + gaps_in = 0; + gaps_out = 0; + border_size = 2; + + "col.active_border" = "0xff${colors.base07}"; + no_border_on_floating = true; + }; + + plugin = { + hyprsplit = { + num_workspaces = 10; + persistent_workspaces = true; + }; + dynamic-cursors = { + enabled = true; + + mode = "rotate"; + rotate = { + length = 20; + offset = 0.0; + }; + threshhold = 2; + shake.enabled = false; }; - threshhold = 2; - shake.enabled = false; }; }; }; diff --git a/modules/wms/wayland/hyprland/workspaces.nix b/modules/wms/wayland/hyprland/workspaces.nix index 315dc38..83d6ad2 100644 --- a/modules/wms/wayland/hyprland/workspaces.nix +++ b/modules/wms/wayland/hyprland/workspaces.nix @@ -3,45 +3,42 @@ lib, ... }: let - inherit (config.meta.mainUser) username; inherit (config.modules.system.hardware) monitors; inherit (lib) imap0 flatten optionalString; inherit (builtins) map genList attrNames toString; in { - home-manager.users.${username} = { - wayland.windowManager.hyprland.settings = { - # INFO: This is a custom function to map all of my monitors to workspaces. - # Since I use split-monitor-workspaces, I map 10 workspaces to each monitor - # and set the first one to be the default one. - # To be able to use this for a varying amount of monitors we do some nasty trickery. - # This was inspired by jacekpoz, whose configuration is linked in this project's README.md. - workspace = - # We're creating several lists of workspace assignments, one for each monitor, - # and have to merge them into one big list. - (flatten - # We then use imap0 insted of map because imap0 starts indexing at zero as oppsed to one with map. - (imap0 (monitorIndex: monitorName: ( - map ( - i: let - # we define our own modulo operation for this, - # since only the first workspace on each monitor is the default workspace. - mod = a: b: a - (b * (a / b)); - workspace = toString i; - isDefault = (mod i 10) == 1; # 11, 21, 31, ... - in "${workspace}, monitor:${monitorName}${optionalString isDefault ", default:true"}" - ) - # we generate a list of 10 elements for each monitor. We have to add 1 each time since genList starts indexing at 0. - # also, we add the monitorIndex * 10 to get 10 workspaces for each individual monitor. - (genList (i: i + 1 + (10 * monitorIndex)) 10) - )) - # our attrSet of different monitors - (attrNames monitors))) - # These are my two special workspaces - ++ [ - "special:nixos, decorate:false" - "special:keepassxc, decorate:false" - "special:audio, decorate:false" - ]; - }; + programs.hyprland.settings = { + # INFO: This is a custom function to map all of my monitors to workspaces. + # Since I use split-monitor-workspaces, I map 10 workspaces to each monitor + # and set the first one to be the default one. + # To be able to use this for a varying amount of monitors we do some nasty trickery. + # This was inspired by jacekpoz, whose configuration is linked in this project's README.md. + workspace = + # We're creating several lists of workspace assignments, one for each monitor, + # and have to merge them into one big list. + (flatten + # We then use imap0 insted of map because imap0 starts indexing at zero as oppsed to one with map. + (imap0 (monitorIndex: monitorName: ( + map ( + i: let + # we define our own modulo operation for this, + # since only the first workspace on each monitor is the default workspace. + mod = a: b: a - (b * (a / b)); + workspace = toString i; + isDefault = (mod i 10) == 1; # 11, 21, 31, ... + in "${workspace}, monitor:${monitorName}${optionalString isDefault ", default:true"}" + ) + # we generate a list of 10 elements for each monitor. We have to add 1 each time since genList starts indexing at 0. + # also, we add the monitorIndex * 10 to get 10 workspaces for each individual monitor. + (genList (i: i + 1 + (10 * monitorIndex)) 10) + )) + # our attrSet of different monitors + (attrNames monitors))) + # These are my two special workspaces + ++ [ + "special:nixos, decorate:false" + "special:keepassxc, decorate:false" + "special:audio, decorate:false" + ]; }; }