diff --git a/flake.nix b/flake.nix index b1e8d6c3..29eb2496 100644 --- a/flake.nix +++ b/flake.nix @@ -40,25 +40,25 @@ system, }: let inherit (pkgs) callPackage; - mango = callPackage ./nix { + mango = callPackage ./nix/package.nix { inherit (scenefx.packages.${system}) scenefx; }; in { inherit mango; default = mango; hm-options-json = callPackage (import ./nix/generate-options.nix self) { - module = ./nix/hm-modules.nix; + module = ./nix/hm-module.nix; optionPrefix = "wayland.windowManager.mango."; }; nixos-options-json = callPackage (import ./nix/generate-options.nix self) { - module = ./nix/nixos-modules.nix; + module = ./nix/nixos-module.nix; optionPrefix = "programs.mango."; }; } ); - nixosModules.mango = import ./nix/nixos-modules.nix self; - hmModules.mango = import ./nix/hm-modules.nix self; + nixosModules.mango = import ./nix/nixos-module.nix self; + hmModules.mango = import ./nix/hm-module.nix self; devShells = forEachSystem ( {system, ...}: { diff --git a/nix/generate-options.nix b/nix/generate-options.nix index f8df3745..cc1bb1bf 100644 --- a/nix/generate-options.nix +++ b/nix/generate-options.nix @@ -1,20 +1,18 @@ -self: -{ +self: { pkgs, lib ? pkgs.lib, module, optionPrefix, -}: -let +}: let # Absolute store path of the flake root, used to compute relative subpaths repoPath = toString self; eval = lib.evalModules { modules = [ (import module self) - { _module.check = false; } + {_module.check = false;} ]; - specialArgs = { inherit pkgs; }; + specialArgs = {inherit pkgs;}; }; # Relative path of the module file within the repo (e.g. "nix/hm-modules.nix") @@ -28,15 +26,14 @@ let optionsDoc = pkgs.nixosOptionsDoc { options = eval.options; - transformOptions = - opt: + transformOptions = opt: opt // { visible = opt.visible && !opt.internal; # Strip the option prefix so docs show "enable" instead of "programs.mango.enable" name = lib.removePrefix optionPrefix opt.name; - declarations = [ moduleDeclaration ]; + declarations = [moduleDeclaration]; }; }; in -optionsDoc.optionsJSON + optionsDoc.optionsJSON diff --git a/nix/hm-modules.nix b/nix/hm-module.nix similarity index 85% rename from nix/hm-modules.nix rename to nix/hm-module.nix index eba05e2f..3f3b3bfb 100644 --- a/nix/hm-modules.nix +++ b/nix/hm-module.nix @@ -1,11 +1,9 @@ -self: -{ +self: { lib, config, pkgs, ... -}: -let +}: let cfg = config.wayland.windowManager.mango; selflib = import ./lib.nix lib; variables = lib.concatStringsSep " " cfg.systemd.variables; @@ -15,8 +13,7 @@ let ${lib.optionalString cfg.systemd.enable systemdActivation} ${cfg.autostart_sh} ''; -in -{ +in { options = { wayland.windowManager.mango = with lib; { enable = mkOption { @@ -59,7 +56,7 @@ in "XCURSOR_THEME" "XCURSOR_SIZE" ]; - example = [ "--all" ]; + example = ["--all"]; description = '' Environment variables imported into the systemd and D-Bus user environment. ''; @@ -80,25 +77,23 @@ in ''; }; settings = mkOption { - type = - with lib.types; - let - valueType = - nullOr (oneOf [ - bool - int - float - str - path - (attrsOf valueType) - (listOf valueType) - ]) - // { - description = "Mango configuration value"; - }; - in + type = with lib.types; let + valueType = + nullOr (oneOf [ + bool + int + float + str + path + (attrsOf valueType) + (listOf valueType) + ]) + // { + description = "Mango configuration value"; + }; + in valueType; - default = { }; + default = {}; description = '' Mango configuration written in Nix. Entries with the same key should be written as lists. Variables and colors names should be @@ -178,21 +173,21 @@ in }; topPrefixes = mkOption { type = with lib.types; listOf str; - default = [ ]; + default = []; description = '' List of prefixes for attributes that should appear at the top of the config file. Attributes starting with these prefixes will be sorted to the beginning. ''; - example = [ "source" ]; + example = ["source"]; }; bottomPrefixes = mkOption { type = with lib.types; listOf str; - default = [ ]; + default = []; description = '' List of prefixes for attributes that should appear at the bottom of the config file. Attributes starting with these prefixes will be sorted to the end. ''; - example = [ "source" ]; + example = ["source"]; }; autostart_sh = mkOption { description = '' @@ -217,25 +212,25 @@ in finalConfigText = # Support old string-based config during transition period ( - if builtins.isString cfg.settings then - cfg.settings + if builtins.isString cfg.settings + then cfg.settings else - lib.optionalString (cfg.settings != { }) ( + lib.optionalString (cfg.settings != {}) ( selflib.toMango { topCommandsPrefixes = cfg.topPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes; - } cfg.settings + } + cfg.settings ) ) + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig + lib.optionalString (cfg.autostart_sh != "") "\nexec-once=~/.config/mango/autostart.sh\n"; - validatedConfig = pkgs.runCommand "mango-config.conf" { } '' + validatedConfig = pkgs.runCommand "mango-config.conf" {} '' cp ${pkgs.writeText "mango-config.conf" finalConfigText} "$out" ${cfg.package}/bin/mango -c "$out" -p || exit 1 ''; - in - { + in { # Backwards compatibility warning for old string-based config warnings = lib.optional (builtins.isString cfg.settings) '' wayland.windowManager.mango.settings: Using a string for settings is deprecated. @@ -244,13 +239,13 @@ in The old string format will be removed in a future release. ''; - home.packages = [ cfg.package ]; + home.packages = [cfg.package]; xdg.configFile = { "mango/config.conf" = - lib.mkIf (cfg.settings != { } || cfg.extraConfig != "" || cfg.autostart_sh != "") - { - source = validatedConfig; - }; + lib.mkIf (cfg.settings != {} || cfg.extraConfig != "" || cfg.autostart_sh != "") + { + source = validatedConfig; + }; "mango/autostart.sh" = lib.mkIf (cfg.autostart_sh != "") { source = autostart_sh; executable = true; @@ -259,13 +254,14 @@ in systemd.user.targets.mango-session = lib.mkIf cfg.systemd.enable { Unit = { Description = "mango compositor session"; - Documentation = [ "man:systemd.special(7)" ]; - BindsTo = [ "graphical-session.target" ]; - Wants = [ - "graphical-session-pre.target" - ] - ++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; - After = [ "graphical-session-pre.target" ]; + Documentation = ["man:systemd.special(7)"]; + BindsTo = ["graphical-session.target"]; + Wants = + [ + "graphical-session-pre.target" + ] + ++ lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; + After = ["graphical-session-pre.target"]; Before = lib.optional cfg.systemd.xdgAutostart "xdg-desktop-autostart.target"; }; }; diff --git a/nix/lib.nix b/nix/lib.nix index 9dfd2ff6..27264707 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,6 +1,6 @@ -lib: -let - inherit (lib) +lib: let + inherit + (lib) attrNames filterAttrs foldl @@ -9,304 +9,299 @@ let removeAttrs ; - inherit (lib.strings) + inherit + (lib.strings) concatMapStrings hasPrefix ; /** - Convert a structured Nix attribute set into Mango's configuration format. + Convert a structured Nix attribute set into Mango's configuration format. - This function takes a nested attribute set and converts it into Mango-compatible - configuration syntax, supporting top, bottom, and regular command sections. + This function takes a nested attribute set and converts it into Mango-compatible + configuration syntax, supporting top, bottom, and regular command sections. - Commands are flattened using the `flattenAttrs` function, and attributes are formatted as - `key = value` pairs. Lists are expanded as duplicate keys to match Mango's expected format. + Commands are flattened using the `flattenAttrs` function, and attributes are formatted as + `key = value` pairs. Lists are expanded as duplicate keys to match Mango's expected format. - Configuration: + Configuration: - * `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `[]`). - * `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`). + * `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `[]`). + * `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`). - Attention: + Attention: - - The function ensures top commands appear **first** and bottom commands **last**. - - The generated configuration is a **single string**, suitable for writing to a config file. - - Lists are converted into multiple entries, ensuring compatibility with Mango. + - The function ensures top commands appear **first** and bottom commands **last**. + - The generated configuration is a **single string**, suitable for writing to a config file. + - Lists are converted into multiple entries, ensuring compatibility with Mango. - # Inputs + # Inputs - Structured function argument: + Structured function argument: - : topCommandsPrefixes (optional, default: `[]`) - : A list of prefixes that define **top** commands. Any key starting with one of these - prefixes will be placed at the beginning of the configuration. - : bottomCommandsPrefixes (optional, default: `[]`) - : A list of prefixes that define **bottom** commands. Any key starting with one of these - prefixes will be placed at the end of the configuration. + : topCommandsPrefixes (optional, default: `[]`) + : A list of prefixes that define **top** commands. Any key starting with one of these + prefixes will be placed at the beginning of the configuration. + : bottomCommandsPrefixes (optional, default: `[]`) + : A list of prefixes that define **bottom** commands. Any key starting with one of these + prefixes will be placed at the end of the configuration. - Value: + Value: - : The attribute set to be converted to Hyprland configuration format. + : The attribute set to be converted to Hyprland configuration format. - # Type + # Type - ``` - toMango :: AttrSet -> AttrSet -> String - ``` + ``` + toMango :: AttrSet -> AttrSet -> String + ``` - # Examples - :::{.example} + # Examples + :::{.example} - ## Basic mangowc configuration + ## Basic mangowc configuration - ```nix - let - config = { - blur = 1; - blur_params_radius = 5; - border_radius = 6; - animations = 1; - animation_duration_open = 400; + ```nix + let + config = { + blur = 1; + blur_params_radius = 5; + border_radius = 6; + animations = 1; + animation_duration_open = 400; + }; + in lib.toMango {} config + ``` + + **Output:** + ``` + animations = 1 + animation_duration_open = 400 + blur = 1 + blur_params_radius = 5 + border_radius = 6 + ``` + + ## Using nested attributes + + ```nix + let + config = { + blur = 1; + blur_params = { + radius = 5; + num_passes = 2; + noise = 0.02; }; - in lib.toMango {} config - ``` + animation_curve = { + open = "0.46,1.0,0.29,1"; + close = "0.08,0.92,0,1"; + }; + }; + in lib.toMango {} config + ``` - **Output:** - ``` - animations = 1 - animation_duration_open = 400 - blur = 1 - blur_params_radius = 5 - border_radius = 6 - ``` + **Output:** + ``` + animation_curve_close = 0.08,0.92,0,1 + animation_curve_open = 0.46,1.0,0.29,1 + blur = 1 + blur_params_noise = 0.02 + blur_params_num_passes = 2 + blur_params_radius = 5 + ``` - ## Using nested attributes + ## Using lists for duplicate keys - ```nix - let - config = { - blur = 1; - blur_params = { - radius = 5; - num_passes = 2; - noise = 0.02; - }; - animation_curve = { - open = "0.46,1.0,0.29,1"; - close = "0.08,0.92,0,1"; + ```nix + let + config = { + bind = [ + "SUPER,r,reload_config" + "Alt,space,spawn,rofi -show drun" + "Alt,Return,spawn,foot" + ]; + tagrule = [ + "id:1,layout_name:tile" + "id:2,layout_name:scroller" + ]; + }; + in lib.toMango {} config + ``` + + **Output:** + ``` + bind = SUPER,r,reload_config + bind = Alt,space,spawn,rofi -show drun + bind = Alt,Return,spawn,foot + tagrule = id:1,layout_name:tile + tagrule = id:2,layout_name:scroller + ``` + + ## Using keymodes (submaps) + + ```nix + let + config = { + bind = [ + "SUPER,Q,killclient" + "ALT,R,setkeymode,resize" + ]; + keymode = { + resize = { + bind = [ + "NONE,Left,resizewin,-10,0" + "NONE,Right,resizewin,10,0" + "NONE,Escape,setkeymode,default" + ]; }; }; - in lib.toMango {} config - ``` + }; + in lib.toMango {} config + ``` - **Output:** - ``` - animation_curve_close = 0.08,0.92,0,1 - animation_curve_open = 0.46,1.0,0.29,1 - blur = 1 - blur_params_noise = 0.02 - blur_params_num_passes = 2 - blur_params_radius = 5 - ``` + **Output:** + ``` + bind = SUPER,Q,killclient + bind = ALT,R,setkeymode,resize - ## Using lists for duplicate keys + keymode = resize + bind = NONE,Left,resizewin,-10,0 + bind = NONE,Right,resizewin,10,0 + bind = NONE,Escape,setkeymode,default + ``` - ```nix - let - config = { - bind = [ - "SUPER,r,reload_config" - "Alt,space,spawn,rofi -show drun" - "Alt,Return,spawn,foot" - ]; - tagrule = [ - "id:1,layout_name:tile" - "id:2,layout_name:scroller" - ]; - }; - in lib.toMango {} config - ``` - - **Output:** - ``` - bind = SUPER,r,reload_config - bind = Alt,space,spawn,rofi -show drun - bind = Alt,Return,spawn,foot - tagrule = id:1,layout_name:tile - tagrule = id:2,layout_name:scroller - ``` - - ## Using keymodes (submaps) - - ```nix - let - config = { - bind = [ - "SUPER,Q,killclient" - "ALT,R,setkeymode,resize" - ]; - keymode = { - resize = { - bind = [ - "NONE,Left,resizewin,-10,0" - "NONE,Right,resizewin,10,0" - "NONE,Escape,setkeymode,default" - ]; - }; - }; - }; - in lib.toMango {} config - ``` - - **Output:** - ``` - bind = SUPER,Q,killclient - bind = ALT,R,setkeymode,resize - - keymode = resize - bind = NONE,Left,resizewin,-10,0 - bind = NONE,Right,resizewin,10,0 - bind = NONE,Escape,setkeymode,default - ``` - - ::: + ::: */ - toMango = - { - topCommandsPrefixes ? [ ], - bottomCommandsPrefixes ? [ ], - }: - attrs: - let - toMango' = - attrs: - let - # Specially configured `toKeyValue` generator with support for duplicate keys - # and a legible key-value separator. - mkCommands = generators.toKeyValue { - mkKeyValue = generators.mkKeyValueDefault { } " = "; - listsAsDuplicateKeys = true; - indent = ""; # No indent, since we don't have nesting - }; + toMango = { + topCommandsPrefixes ? [], + bottomCommandsPrefixes ? [], + }: attrs: let + toMango' = attrs: let + # Specially configured `toKeyValue` generator with support for duplicate keys + # and a legible key-value separator. + mkCommands = generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault {} " = "; + listsAsDuplicateKeys = true; + indent = ""; # No indent, since we don't have nesting + }; - # Extract keymode definitions if they exist - keymodes = attrs.keymode or { }; - attrsWithoutKeymodes = removeAttrs attrs [ "keymode" ]; + # Extract keymode definitions if they exist + keymodes = attrs.keymode or {}; + attrsWithoutKeymodes = removeAttrs attrs ["keymode"]; - # Generate keymode blocks - # Format: keymode=name\nbind=...\nbind=...\n - mkKeymodeBlock = - name: modeAttrs: - let - modeCommands = flattenAttrs (p: k: "${p}_${k}") modeAttrs; - in - "keymode = ${name}\n${mkCommands modeCommands}"; + # Generate keymode blocks + # Format: keymode=name\nbind=...\nbind=...\n + mkKeymodeBlock = name: modeAttrs: let + modeCommands = flattenAttrs (p: k: "${p}_${k}") modeAttrs; + in "keymode = ${name}\n${mkCommands modeCommands}"; - keymodeBlocks = - if keymodes == { } then - "" - else - "\n" + concatMapStrings (name: mkKeymodeBlock name keymodes.${name} + "\n") (attrNames keymodes); + keymodeBlocks = + if keymodes == {} + then "" + else "\n" + concatMapStrings (name: mkKeymodeBlock name keymodes.${name} + "\n") (attrNames keymodes); - # Flatten the attrset, combining keys in a "path" like `"a_b_c" = "x"`. - # Uses `flattenAttrs` with an underscore separator. - commands = flattenAttrs (p: k: "${p}_${k}") attrsWithoutKeymodes; + # Flatten the attrset, combining keys in a "path" like `"a_b_c" = "x"`. + # Uses `flattenAttrs` with an underscore separator. + commands = flattenAttrs (p: k: "${p}_${k}") attrsWithoutKeymodes; - # General filtering function to check if a key starts with any prefix in a given list. - filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; + # General filtering function to check if a key starts with any prefix in a given list. + filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; - # Partition keys into top commands and the rest - result = partition (filterCommands topCommandsPrefixes) (attrNames commands); - topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; - remainingCommands = removeAttrs commands result.right; + # Partition keys into top commands and the rest + result = partition (filterCommands topCommandsPrefixes) (attrNames commands); + topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; + remainingCommands = removeAttrs commands result.right; - # Partition remaining commands into bottom commands and regular commands - result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; - bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; - regularCommands = removeAttrs remainingCommands result2.right; - in - # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. - # Keymodes are appended at the end. - concatMapStrings mkCommands [ - topCommands - regularCommands - bottomCommands - ] - + keymodeBlocks; + # Partition remaining commands into bottom commands and regular commands + result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; + bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; + regularCommands = removeAttrs remainingCommands result2.right; in + # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. + # Keymodes are appended at the end. + concatMapStrings mkCommands [ + topCommands + regularCommands + bottomCommands + ] + + keymodeBlocks; + in toMango' attrs; /** - Flatten a nested attribute set into a flat attribute set, using a custom key separator function. + Flatten a nested attribute set into a flat attribute set, using a custom key separator function. - This function recursively traverses a nested attribute set and produces a flat attribute set - where keys are joined using a user-defined function (`pred`). It allows transforming deeply - nested structures into a single-level attribute set while preserving key-value relationships. + This function recursively traverses a nested attribute set and produces a flat attribute set + where keys are joined using a user-defined function (`pred`). It allows transforming deeply + nested structures into a single-level attribute set while preserving key-value relationships. - Configuration: + Configuration: - * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. + * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. - # Inputs + # Inputs - Structured function argument: + Structured function argument: - : pred (required) - : A function that determines how parent and child keys should be combined into a single key. - It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. + : pred (required) + : A function that determines how parent and child keys should be combined into a single key. + It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. - Value: + Value: - : The nested attribute set to be flattened. + : The nested attribute set to be flattened. - # Type + # Type - ``` - flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet - ``` + ``` + flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} + # Examples + :::{.example} - ```nix - let - nested = { - a = "3"; - b = { c = "4"; d = "5"; }; - }; + ```nix + let + nested = { + a = "3"; + b = { c = "4"; d = "5"; }; + }; - separator = (prefix: key: "${prefix}.${key}"); # Use dot notation - in lib.flattenAttrs separator nested - ``` + separator = (prefix: key: "${prefix}.${key}"); # Use dot notation + in lib.flattenAttrs separator nested + ``` - **Output:** - ```nix - { - "a" = "3"; - "b.c" = "4"; - "b.d" = "5"; - } - ``` + **Output:** + ```nix + { + "a" = "3"; + "b.c" = "4"; + "b.d" = "5"; + } + ``` - ::: + ::: */ - flattenAttrs = - pred: attrs: - let - flattenAttrs' = - prefix: attrs: - builtins.foldl' ( - acc: key: - let - value = attrs.${key}; - newKey = if prefix == "" then key else pred prefix key; - in - acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; }) - ) { } (builtins.attrNames attrs); - in + flattenAttrs = pred: attrs: let + flattenAttrs' = prefix: attrs: + builtins.foldl' ( + acc: key: let + value = attrs.${key}; + newKey = + if prefix == "" + then key + else pred prefix key; + in + acc + // ( + if builtins.isAttrs value + then flattenAttrs' newKey value + else {"${newKey}" = value;} + ) + ) {} (builtins.attrNames attrs); + in flattenAttrs' "" attrs; -in -{ +in { inherit flattenAttrs toMango; } diff --git a/nix/nixos-modules.nix b/nix/nixos-module.nix similarity index 94% rename from nix/nixos-modules.nix rename to nix/nixos-module.nix index 9144bbf1..482b734e 100644 --- a/nix/nixos-modules.nix +++ b/nix/nixos-module.nix @@ -23,10 +23,9 @@ in { }; config = lib.mkIf cfg.enable { - environment.systemPackages = - [ - cfg.package - ]; + environment.systemPackages = [ + cfg.package + ]; xdg.portal = { enable = lib.mkDefault true; @@ -60,7 +59,7 @@ in { programs.xwayland.enable = lib.mkDefault true; services = { - displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [ cfg.package ]; + displayManager.sessionPackages = lib.mkIf cfg.addLoginEntry [cfg.package]; graphical-desktop.enable = lib.mkDefault true; }; diff --git a/nix/default.nix b/nix/package.nix similarity index 100% rename from nix/default.nix rename to nix/package.nix