diff --git a/README.md b/README.md index 69252d4..9b9fc14 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,10 @@ A wayland native krunner-like runner, made with customizability in mind. image > [!NOTE] -> If you use Nvidia and Anyrun refuses to close for you, you need to set `GSK_RENDERER=ngl` for Anyrun. -> As in, running it with `GSK_RENDERER=ngl anyrun`. This is a [known issue](https://forums.developer.nvidia.com/t/580-65-06-gtk-4-apps-hang-when-attempting-to-exit-close/341308/6) +> If you use Nvidia and Anyrun refuses to close for you, you need to set +> `GSK_RENDERER=ngl` for Anyrun. As in, running it with +> `GSK_RENDERER=ngl anyrun`. This is a +> [known issue](https://forums.developer.nvidia.com/t/580-65-06-gtk-4-apps-hang-when-attempting-to-exit-close/341308/6) > and is quite driver version dependent. ## Features @@ -30,8 +32,8 @@ A wayland native krunner-like runner, made with customizability in mind. ### Dependencies -Anyrun mainly depends various GTK4 libraries, and rust of course for building the -project. Rust you can get with [rustup](https://rustup.rs). The rest are +Anyrun mainly depends various GTK4 libraries, and rust of course for building +the project. Rust you can get with [rustup](https://rustup.rs). The rest are statically linked in the binary. Here are the libraries you need to have to build & run it: @@ -48,11 +50,13 @@ build & run it: ### Nix -An Anyrun package that contains all the official plugins is available in [nixpkgs](https://search.nixos.org/packages?channel=unstable&show=anyrun&from=0&size=50&sort=relevance&type=packages&query=anyrun). +An Anyrun package that contains all the official plugins is available in +[nixpkgs](https://search.nixos.org/packages?channel=unstable&show=anyrun&from=0&size=50&sort=relevance&type=packages&query=anyrun). #### Home-Manager module -The preferred way to use Home-Manager with Anyrun is by using the upstream module. +The preferred way to use Home-Manager with Anyrun is by using the upstream +module. You may use it in your system like this: @@ -145,8 +149,39 @@ nix.settings = { > [!WARNING] > While using the Anyrun flake, overriding the `nixpkgs` input for Anyrun will -> cause cache misses, i.e., you will have to build from source every time. To use -> the cache, do _not_ override the Nixpkgs input. +> cause cache misses, i.e., you will have to build from source every time. To +> use the cache, do _not_ override the Nixpkgs input. + +### Hjem module + +We also provide a Hjem module, which comes with the exact same API as the +Home-Manager one. You may import it in your configuration like so: + +```nix +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + hjem.url = "github:feel-co/hjem"; + anyrun.url = "github:anyrun-org/anyrun"; + }; + outputs = {self, ...}@inputs: { + nixosConfigurations.hostname = inputs.nixpkgs.lib.nixosSystem { + modules = [ + ({ + hjem.extraModules = [ + inputs.anyrun.hjemModules.default + ]; + }) + { + programs.anyrun = { + # ... + }; + } + ]; + }; + } +} +``` ### Manual installation @@ -224,8 +259,8 @@ use them. ## Styling -Anyrun supports [GTK4 CSS](https://docs.gtk.org/gtk4/css-properties.html) styling. -The style classes and widgets that use them are as follows: +Anyrun supports [GTK4 CSS](https://docs.gtk.org/gtk4/css-properties.html) +styling. The style classes and widgets that use them are as follows: - No class, unique widget: - `GtkText`: The main entry box @@ -248,8 +283,8 @@ The style classes and widgets that use them are as follows: - `.description` - `GtkLabel`: The description (if present) -Refer to the [default style](anyrun/res/style.css) for an example, and use `GTK_DEBUG=interactive anyrun` -to edit styles live. +Refer to the [default style](anyrun/res/style.css) for an example, and use +`GTK_DEBUG=interactive anyrun` to edit styles live. ## Arguments diff --git a/flake.nix b/flake.nix index 8ff843c..d920da8 100644 --- a/flake.nix +++ b/flake.nix @@ -14,105 +14,102 @@ }; }; - outputs = - { - self, - flake-parts, - systems, - ... - }@inputs: - flake-parts.lib.mkFlake { inherit inputs; } { - imports = [ flake-parts.flakeModules.easyOverlay ]; + outputs = { + self, + flake-parts, + systems, + ... + } @ inputs: + flake-parts.lib.mkFlake {inherit inputs;} { + imports = [flake-parts.flakeModules.easyOverlay]; systems = import systems; - perSystem = - { - self', - config, - pkgs, - ... - }: - let - inherit (pkgs) callPackage; - in - { - packages = - let - lockFile = ./Cargo.lock; + perSystem = { + self', + config, + pkgs, + ... + }: let + inherit (pkgs) callPackage; + in { + packages = let + lockFile = ./Cargo.lock; - # Since all plugin derivations are called with the exact same arguments - # it is possible to streamline calling packages with a single function - # that takes name as an argument, and handles default inherits. - mkPlugin = - name: - callPackage ./nix/packages/plugin.nix { - inherit inputs lockFile; - inherit name; - }; - in - { - default = self'.packages.anyrun; - - # By default the anyrun package is built without any plugins - # as per the `dontBuildPlugins` arg. - anyrun = callPackage ./nix/packages/anyrun.nix { inherit inputs lockFile; }; - anyrun-with-all-plugins = callPackage ./nix/packages/anyrun.nix { - inherit inputs lockFile; - dontBuildPlugins = false; - }; + # Since all plugin derivations are called with the exact same arguments + # it is possible to streamline calling packages with a single function + # that takes name as an argument, and handles default inherits. + mkPlugin = name: + callPackage ./nix/packages/plugin.nix { + inherit inputs lockFile; + inherit name; + }; + in { + default = self'.packages.anyrun; - # Expose each plugin as a separate package. This uses the mkPlugin function - # to call the same derivation with same default inherits and the name of the - # plugin every time. - applications = mkPlugin "applications"; - dictionary = mkPlugin "dictionary"; - kidex = mkPlugin "kidex"; - nix-run = mkPlugin "nix-run"; - randr = mkPlugin "randr"; - rink = mkPlugin "rink"; - shell = mkPlugin "shell"; - stdin = mkPlugin "stdin"; - symbols = mkPlugin "symbols"; - translate = mkPlugin "translate"; - websearch = mkPlugin "websearch"; - niri-focus = mkPlugin "niri-focus"; + # By default the anyrun package is built without any plugins + # as per the `dontBuildPlugins` arg. + anyrun = callPackage ./nix/packages/anyrun.nix {inherit inputs lockFile;}; + anyrun-with-all-plugins = callPackage ./nix/packages/anyrun.nix { + inherit inputs lockFile; + dontBuildPlugins = false; + }; - anyrun-provider = inputs.anyrun-provider.packages.${pkgs.system}.default; - }; + # Expose each plugin as a separate package. This uses the mkPlugin function + # to call the same derivation with same default inherits and the name of the + # plugin every time. + applications = mkPlugin "applications"; + dictionary = mkPlugin "dictionary"; + kidex = mkPlugin "kidex"; + nix-run = mkPlugin "nix-run"; + randr = mkPlugin "randr"; + rink = mkPlugin "rink"; + shell = mkPlugin "shell"; + stdin = mkPlugin "stdin"; + symbols = mkPlugin "symbols"; + translate = mkPlugin "translate"; + websearch = mkPlugin "websearch"; + niri-focus = mkPlugin "niri-focus"; - # Set up an overlay from packages exposed by this flake - overlayAttrs = config.packages; + anyrun-provider = inputs.anyrun-provider.packages.${pkgs.system}.default; + }; - devShells = { - default = pkgs.mkShell { - inputsFrom = builtins.attrValues self'.packages; - packages = with pkgs; [ - rustc - gcc - cargo - clippy - rustfmt - ]; - }; + # Set up an overlay from packages exposed by this flake + overlayAttrs = config.packages; - nix = pkgs.mkShellNoCC { - packages = with pkgs; [ - alejandra # formatter - statix # linter - deadnix # dead-code finder - ]; - }; + devShells = { + default = pkgs.mkShell { + inputsFrom = builtins.attrValues self'.packages; + packages = with pkgs; [ + rustc + gcc + cargo + clippy + rustfmt + ]; }; - # provide the formatter for nix fmt - formatter = pkgs.alejandra; + nix = pkgs.mkShellNoCC { + packages = with pkgs; [ + alejandra # formatter + statix # linter + deadnix # dead-code finder + ]; + }; }; + # provide the formatter for nix fmt + formatter = pkgs.alejandra; + }; + flake = { homeManagerModules = { anyrun = import ./nix/modules/home-manager.nix self; default = self.homeManagerModules.anyrun; }; + hjemModules = { + anyrun = import ./nix/modules/hjem.nix self; + default = self.hjemModules.anyrun; + }; }; }; } diff --git a/nix/lib.nix b/nix/lib.nix new file mode 100644 index 0000000..0a042d7 --- /dev/null +++ b/nix/lib.nix @@ -0,0 +1,82 @@ +lib: let + inherit + (builtins) + map + toString + substring + stringLength + ; + inherit + (lib.strings) + concatMapStringsSep + toLower + toUpper + replaceStrings + optionalString + ; + + assertNumeric = numeric: { + assertion = + !( + (numeric ? absolute && numeric.absolute != null) && (numeric ? fraction && numeric.fraction != null) + ); + message = "Invalid numeric definition, you can only specify one of absolute or fraction."; + }; + + stringifyNumeric = numeric: + if (numeric ? absolute && numeric.absolute != null) + then "Absolute(${toString numeric.absolute})" + else "Fraction(${toString numeric.fraction})"; + + capitalize = string: toUpper (substring 0 1 string) + toLower (substring 1 ((stringLength string) - 1) string); + + parsedPlugins = plugins: anyrunPackage: + if plugins == null + then [] + else + map ( + entry: + if lib.types.package.check entry + then "${entry}/lib/lib${replaceStrings ["-"] ["_"] entry.pname}.so" + else let + path = "${anyrunPackage}/lib/${entry}"; + in + if builtins.pathExists path + then path + else let + path = "${anyrunPackage}/lib/lib${replaceStrings ["-"] ["_"] entry}.so"; + in + if builtins.pathExists path + then path + else if lib.strings.hasPrefix "/" entry + then entry + else throw "Anyrun: Plugin ${entry} does not exist" + ) + plugins; + + keybinds = keybinds: + if keybinds == null + then "" + else '' + keybinds: [ + ${ + concatMapStringsSep "\n" (x: '' + Keybind( + ${optionalString x.ctrl "ctrl: true,"} + ${optionalString x.alt "alt: true,"} + key: "${x.key}", + action: ${capitalize x.action}, + ), + '') + keybinds + }], + ''; + keyboardMode = mode: + { + "exclusive" = "Exclusive"; + "on-demand" = "OnDemand"; + } + .${ + mode + }; +in {inherit assertNumeric stringifyNumeric capitalize parsedPlugins keybinds keyboardMode;} diff --git a/nix/modules/hjem.nix b/nix/modules/hjem.nix new file mode 100644 index 0000000..28cce73 --- /dev/null +++ b/nix/modules/hjem.nix @@ -0,0 +1,100 @@ +self: { + config, + lib, + ... +}: let + inherit + (builtins) + toJSON + toString + ; + + anyrunLib = import ../lib.nix lib; + + inherit (lib.modules) mkIf mkMerge; + inherit (lib.lists) optional; + inherit (lib.attrsets) mapAttrs' nameValuePair; + inherit + (lib.strings) + optionalString + ; + inherit (lib.trivial) boolToString; + + inherit (anyrunLib) assertNumeric stringifyNumeric keyboardMode parsedPlugins capitalize keybinds; + + cfg = config.programs.anyrun; +in { + imports = [(import ./options.nix self)]; + + config = mkIf cfg.enable { + assertions = [ + (assertNumeric cfg.config.width) + (assertNumeric cfg.config.height) + (assertNumeric cfg.config.x) + (assertNumeric cfg.config.y) + ]; + + warnings = + if cfg.config.plugins == null + then [ + '' + You haven't enabled any plugins. Anyrun will not show any results, unless you specify plugins with the --override-plugins flag. + Add plugins to programs.anyrun.config.plugins, or set it to [] to silence the warning. + '' + ] + else []; + + systemd.services.anyrun = mkIf cfg.daemon.enable { + description = "Anyrun daemon"; + script = "${lib.getExe cfg.package} daemon"; + partOf = ["graphical-session.target"]; + after = ["graphical-session.target"]; + wantedBy = ["graphical-session.target"]; + + serviceConfig = { + Type = "simple"; + Restart = "on-failure"; + KillMode = "process"; + }; + }; + + packages = optional (cfg.package != null) cfg.package; + + xdg.config.files = mkMerge [ + (mapAttrs' (name: value: nameValuePair ("anyrun/" + name) value) cfg.extraConfigFiles) + + { + "anyrun/config.ron".text = '' + Config( + x: ${stringifyNumeric cfg.config.x}, + y: ${stringifyNumeric cfg.config.y}, + width: ${stringifyNumeric cfg.config.width}, + height: ${stringifyNumeric cfg.config.height}, + hide_icons: ${boolToString cfg.config.hideIcons}, + ignore_exclusive_zones: ${boolToString cfg.config.ignoreExclusiveZones}, + layer: ${capitalize cfg.config.layer}, + keyboard_mode: ${keyboardMode cfg.config.keyboardMode}, + hide_plugin_info: ${boolToString cfg.config.hidePluginInfo}, + close_on_click: ${boolToString cfg.config.closeOnClick}, + show_results_immediately: ${boolToString cfg.config.showResultsImmediately}, + max_entries: ${ + if cfg.config.maxEntries == null + then "None" + else "Some(${toString cfg.config.maxEntries})" + }, + plugins: ${toJSON (parsedPlugins cfg.config.plugins cfg.package)}, + provider: "${lib.getExe cfg.config.provider}", + ${optionalString (cfg.config.extraLines != null) cfg.config.extraLines} + ${keybinds cfg.config.keybinds} + ) + ''; + } + + { + "anyrun/style.css" = mkIf (cfg.extraCss != null) { + text = cfg.extraCss; + }; + } + ]; + }; +} diff --git a/nix/modules/home-manager.nix b/nix/modules/home-manager.nix index c98cbd4..fa497d0 100644 --- a/nix/modules/home-manager.nix +++ b/nix/modules/home-manager.nix @@ -1,432 +1,105 @@ -self: -{ +self: { config, - pkgs, lib, ... -}: -let - inherit (builtins) - map +}: let + inherit + (builtins) toJSON toString - substring - stringLength ; + + anyrunLib = import ../lib.nix lib; + inherit (lib.modules) mkIf mkMerge; - inherit (lib.options) mkOption mkEnableOption literalExpression; inherit (lib.lists) optional; inherit (lib.attrsets) mapAttrs' nameValuePair; - inherit (lib.strings) - concatMapStringsSep - toLower - toUpper - replaceStrings + inherit + (lib.strings) optionalString ; inherit (lib.trivial) boolToString; - inherit (lib.types) - nullOr - package - submodule - int - float - listOf - either - str - enum - lines - bool - attrs - ; - defaultPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default; - defaultProvider = self.packages.${pkgs.stdenv.hostPlatform.system}.anyrun-provider; - cfg = config.programs.anyrun; -in -{ - meta.maintainers = with lib.maintainers; [ - n3oney - NotAShelf - ]; + inherit (anyrunLib) assertNumeric stringifyNumeric keyboardMode parsedPlugins capitalize keybinds; - options.programs.anyrun = { - enable = mkEnableOption "anyrun"; - daemon.enable = mkOption { - type = bool; - default = true; - description = '' - Enable running Anyrun as a daemon, allowing for faster startup speed. + cfg = config.programs.anyrun; +in { + imports = [(import ./options.nix self)]; + + config = mkIf cfg.enable { + assertions = [ + (assertNumeric cfg.config.width) + (assertNumeric cfg.config.height) + (assertNumeric cfg.config.x) + (assertNumeric cfg.config.y) + ]; + + warnings = + if cfg.config.plugins == null + then [ + '' + You haven't enabled any plugins. Anyrun will not show any results, unless you specify plugins with the --override-plugins flag. + Add plugins to programs.anyrun.config.plugins, or set it to [] to silence the warning. + '' + ] + else []; + + systemd.user.services.anyrun = mkIf cfg.daemon.enable { + Unit = { + Description = "Anyrun daemon"; + PartOf = "graphical-session.target"; + After = "graphical-session.target"; + }; - NOTE: This is required for clipboard functionality. - ''; - }; + Service = { + Type = "simple"; + ExecStart = "${lib.getExe cfg.package} daemon"; + Restart = "on-failure"; + KillMode = "process"; + }; - package = mkOption { - type = nullOr package; - default = defaultPackage; - defaultText = literalExpression '' - anyrun.packages.''${pkgs.stdenv.hostPlatform.system}.default - ''; - description = '' - Anyrun package to use. Defaults to the one provided by the flake. - ''; + Install = { + WantedBy = ["graphical-session.target"]; + }; }; - config = - let - mkNumericOption = - { - default, - description, - ... - }: - mkOption { - inherit default description; - example = '' - { absolute = 200; }; - or - { fraction = 0.4; }; - ''; - type = submodule { - options = { - absolute = mkOption { - type = nullOr int; - default = null; - }; - fraction = mkOption { - type = nullOr float; - default = null; - }; - }; - }; - }; - - numericInfo = '' - This is a numeric option - pass either `{ absolute = int; };` or `{ fraction = float; };`. - when using `absolute` it sets the absolute value in pixels, - when using `fraction`, it sets a fraction of the width or height of the full screen (depends on exclusive zones and the settings related to them) window - ''; - in - { - plugins = mkOption { - type = nullOr (listOf (either package str)); - default = null; - description = '' - List of anyrun plugins to use. Can either be packages, absolute plugin paths, or strings. - ''; - }; - - provider = mkOption { - type = package; - default = defaultProvider; - description = '' - The program that is used for loading the plugins, and for the communcation with them. - ''; - }; - - x = mkNumericOption { - default.fraction = 0.5; - description = '' - The horizontal position, adjusted so that { relative = 0.5; } always centers the runner. - - ${numericInfo} - ''; - }; - y = mkNumericOption { - default.fraction = 0.0; - description = '' - The vertical position, works the same as x. + home.packages = optional (cfg.package != null) cfg.package; - ${numericInfo} - ''; - }; - - width = mkNumericOption { - default.absolute = 800; - description = '' - The width of the runner. - - ${numericInfo} - ''; - }; - - height = mkNumericOption { - default.absolute = 0; - description = '' - The minimum height of the runner, the runner will expand to fit all the entries. - - ${numericInfo} - ''; - }; - - hideIcons = mkOption { - type = bool; - default = false; - description = "Hide match and plugin info icons"; - }; + xdg.configFile = mkMerge [ + (mapAttrs' (name: value: nameValuePair ("anyrun/" + name) value) cfg.extraConfigFiles) - ignoreExclusiveZones = mkOption { - type = bool; - default = false; - description = "ignore exclusive zones, eg. Waybar"; - }; - - layer = mkOption { - type = enum [ - "background" - "bottom" - "top" - "overlay" - ]; - default = "overlay"; - description = "Layer shell layer (background, bottom, top or overlay)"; - }; - - keyboardMode = mkOption { - type = enum [ - "exclusive" - "on-demand" - ]; - default = "exclusive"; - description = "Layer shell keyboard mode"; - }; - - hidePluginInfo = mkOption { - type = bool; - default = false; - description = "Hide the plugin info panel"; - }; - - closeOnClick = mkOption { - type = bool; - default = false; - description = "Close window when a click outside the main box is received"; - }; - - showResultsImmediately = mkOption { - type = bool; - default = false; - description = "Show search results immediately when Anyrun starts"; - }; - - maxEntries = mkOption { - type = nullOr int; - default = null; - description = "Limit amount of entries shown in total"; - }; - - keybinds = mkOption { - type = nullOr ( - listOf (submodule { - options = { - ctrl = mkOption { - type = bool; - default = false; - }; - alt = mkOption { - type = bool; - default = false; - }; - key = mkOption { - type = str; - description = '' - Name of the GDK keysym. - - A list of possible values can be found at [https://gitlab.gnome.org/GNOME/gtk/-/blob/main/gdk/gdkkeysyms.h] - ''; - }; - action = mkOption { - type = enum [ - "close" - "select" - "up" - "down" - ]; - }; - }; - }) - ); - default = null; - }; - - extraLines = mkOption { - type = nullOr lines; - default = null; - description = "Extra lines to add inside the `Config()` object"; - }; - }; - - extraCss = mkOption { - type = nullOr lines; - default = null; - description = '' - Extra CSS lines to add to {file}`~/.config/anyrun/style.css`. - ''; - }; - - extraConfigFiles = mkOption { - # unfortunately HM doesn't really export the type for files, but hopefully - # hm will throw errors if the options are wrong here, so I'm being *very* loose - type = attrs; - default = { }; - description = '' - Extra files to put in {file}`~/.config/anyrun`, a wrapper over {option}`xdg.configFile`. - ''; - example = '' - programs.anyrun.extraConfigFiles."plugin-name.ron".text = ''' + { + "anyrun/config.ron".text = '' Config( - some_option: true, + x: ${stringifyNumeric cfg.config.x}, + y: ${stringifyNumeric cfg.config.y}, + width: ${stringifyNumeric cfg.config.width}, + height: ${stringifyNumeric cfg.config.height}, + hide_icons: ${boolToString cfg.config.hideIcons}, + ignore_exclusive_zones: ${boolToString cfg.config.ignoreExclusiveZones}, + layer: ${capitalize cfg.config.layer}, + keyboard_mode: ${keyboardMode cfg.config.keyboardMode}, + hide_plugin_info: ${boolToString cfg.config.hidePluginInfo}, + close_on_click: ${boolToString cfg.config.closeOnClick}, + show_results_immediately: ${boolToString cfg.config.showResultsImmediately}, + max_entries: ${ + if cfg.config.maxEntries == null + then "None" + else "Some(${toString cfg.config.maxEntries})" + }, + plugins: ${toJSON (parsedPlugins cfg.config.plugins cfg.package)}, + provider: "${lib.getExe cfg.config.provider}", + ${optionalString (cfg.config.extraLines != null) cfg.config.extraLines} + ${keybinds cfg.config.keybinds} ) - ''' - ''; - }; - }; - - config = mkIf cfg.enable ( - let - assertNumeric = numeric: { - assertion = - !( - (numeric ? absolute && numeric.absolute != null) && (numeric ? fraction && numeric.fraction != null) - ); - message = "Invalid numeric definition, you can only specify one of absolute or fraction."; - }; - - stringifyNumeric = - numeric: - if (numeric ? absolute && numeric.absolute != null) then - "Absolute(${toString numeric.absolute})" - else - "Fraction(${toString numeric.fraction})"; - - capitalize = - string: toUpper (substring 0 1 string) + toLower (substring 1 ((stringLength string) - 1) string); - - parsedPlugins = - if cfg.config.plugins == null then - [ ] - else - map ( - entry: - if lib.types.package.check entry then - "${entry}/lib/lib${replaceStrings [ "-" ] [ "_" ] entry.pname}.so" - else - let - path = "${cfg.package}/lib/${entry}"; - in - if builtins.pathExists path then - path - else - let - path = "${cfg.package}/lib/lib${replaceStrings [ "-" ] [ "_" ] entry}.so"; - in - if builtins.pathExists path then - path - else if lib.strings.hasPrefix "/" entry then - entry - else - throw "Anyrun: Plugin ${entry} does not exist" - ) cfg.config.plugins; - - keybinds = - if cfg.config.keybinds == null then - "" - else - '' - keybinds: [ - ${ - concatMapStringsSep "\n" (x: '' - Keybind( - ${optionalString x.ctrl "ctrl: true,"} - ${optionalString x.alt "alt: true,"} - key: "${x.key}", - action: ${capitalize x.action}, - ), - '') cfg.config.keybinds - }], - ''; - keyboardMode = - { - "exclusive" = "Exclusive"; - "on-demand" = "OnDemand"; - } - .${cfg.config.keyboardMode}; - in - { - assertions = [ - (assertNumeric cfg.config.width) - (assertNumeric cfg.config.height) - (assertNumeric cfg.config.x) - (assertNumeric cfg.config.y) - ]; - - warnings = - if cfg.config.plugins == null then - [ - '' - You haven't enabled any plugins. Anyrun will not show any results, unless you specify plugins with the --override-plugins flag. - Add plugins to programs.anyrun.config.plugins, or set it to [] to silence the warning. - '' - ] - else - [ ]; - - systemd.user.services.anyrun = mkIf cfg.daemon.enable { - Unit = { - Description = "Anyrun daemon"; - PartOf = "graphical-session.target"; - After = "graphical-session.target"; - }; - - Service = { - Type = "simple"; - ExecStart = "${lib.getExe cfg.package} daemon"; - Restart = "on-failure"; - KillMode = "process"; - }; + ''; + } - Install = { - WantedBy = [ "graphical-session.target" ]; + { + "anyrun/style.css" = mkIf (cfg.extraCss != null) { + text = cfg.extraCss; }; - }; - - home.packages = optional (cfg.package != null) cfg.package; - - xdg.configFile = mkMerge [ - (mapAttrs' (name: value: nameValuePair ("anyrun/" + name) value) cfg.extraConfigFiles) - - { - "anyrun/config.ron".text = '' - Config( - x: ${stringifyNumeric cfg.config.x}, - y: ${stringifyNumeric cfg.config.y}, - width: ${stringifyNumeric cfg.config.width}, - height: ${stringifyNumeric cfg.config.height}, - hide_icons: ${boolToString cfg.config.hideIcons}, - ignore_exclusive_zones: ${boolToString cfg.config.ignoreExclusiveZones}, - layer: ${capitalize cfg.config.layer}, - keyboard_mode: ${keyboardMode}, - hide_plugin_info: ${boolToString cfg.config.hidePluginInfo}, - close_on_click: ${boolToString cfg.config.closeOnClick}, - show_results_immediately: ${boolToString cfg.config.showResultsImmediately}, - max_entries: ${ - if cfg.config.maxEntries == null then "None" else "Some(${toString cfg.config.maxEntries})" - }, - plugins: ${toJSON parsedPlugins}, - provider: "${lib.getExe cfg.config.provider}", - ${optionalString (cfg.config.extraLines != null) cfg.config.extraLines} - ${keybinds} - ) - ''; - } - - { - "anyrun/style.css" = mkIf (cfg.extraCss != null) { - text = cfg.extraCss; - }; - } - ]; - } - ); + } + ]; + }; } diff --git a/nix/modules/options.nix b/nix/modules/options.nix new file mode 100644 index 0000000..1835f1d --- /dev/null +++ b/nix/modules/options.nix @@ -0,0 +1,255 @@ +self: { + pkgs, + lib, + ... +}: let + inherit (lib.options) mkOption mkEnableOption literalExpression; + inherit + (lib.types) + nullOr + package + submodule + int + float + listOf + either + str + enum + lines + bool + attrs + ; + + defaultPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default; + defaultProvider = self.packages.${pkgs.stdenv.hostPlatform.system}.anyrun-provider; +in { + options.programs.anyrun = { + enable = mkEnableOption "anyrun"; + daemon.enable = mkOption { + type = bool; + default = true; + description = '' + Enable running Anyrun as a daemon, allowing for faster startup speed. + + NOTE: This is required for clipboard functionality. + ''; + }; + + package = mkOption { + type = nullOr package; + default = defaultPackage; + defaultText = literalExpression '' + anyrun.packages.''${pkgs.stdenv.hostPlatform.system}.default + ''; + description = '' + Anyrun package to use. Defaults to the one provided by the flake. + ''; + }; + config = let + mkNumericOption = { + default, + description, + ... + }: + mkOption { + inherit default description; + example = '' + { absolute = 200; }; + or + { fraction = 0.4; }; + ''; + type = submodule { + options = { + absolute = mkOption { + type = nullOr int; + default = null; + }; + fraction = mkOption { + type = nullOr float; + default = null; + }; + }; + }; + }; + + numericInfo = '' + This is a numeric option - pass either `{ absolute = int; };` or `{ fraction = float; };`. + when using `absolute` it sets the absolute value in pixels, + when using `fraction`, it sets a fraction of the width or height of the full screen (depends on exclusive zones and the settings related to them) window + ''; + in { + plugins = mkOption { + type = nullOr (listOf (either package str)); + default = null; + description = '' + List of anyrun plugins to use. Can either be packages, absolute plugin paths, or strings. + ''; + }; + + provider = mkOption { + type = package; + default = defaultProvider; + description = '' + The program that is used for loading the plugins, and for the communcation with them. + ''; + }; + + x = mkNumericOption { + default.fraction = 0.5; + description = '' + The horizontal position, adjusted so that { relative = 0.5; } always centers the runner. + + ${numericInfo} + ''; + }; + + y = mkNumericOption { + default.fraction = 0.0; + description = '' + The vertical position, works the same as x. + + ${numericInfo} + ''; + }; + + width = mkNumericOption { + default.absolute = 800; + description = '' + The width of the runner. + + ${numericInfo} + ''; + }; + + height = mkNumericOption { + default.absolute = 0; + description = '' + The minimum height of the runner, the runner will expand to fit all the entries. + + ${numericInfo} + ''; + }; + + hideIcons = mkOption { + type = bool; + default = false; + description = "Hide match and plugin info icons"; + }; + + ignoreExclusiveZones = mkOption { + type = bool; + default = false; + description = "ignore exclusive zones, eg. Waybar"; + }; + + layer = mkOption { + type = enum [ + "background" + "bottom" + "top" + "overlay" + ]; + default = "overlay"; + description = "Layer shell layer (background, bottom, top or overlay)"; + }; + + keyboardMode = mkOption { + type = enum [ + "exclusive" + "on-demand" + ]; + default = "exclusive"; + description = "Layer shell keyboard mode"; + }; + + hidePluginInfo = mkOption { + type = bool; + default = false; + description = "Hide the plugin info panel"; + }; + + closeOnClick = mkOption { + type = bool; + default = false; + description = "Close window when a click outside the main box is received"; + }; + + showResultsImmediately = mkOption { + type = bool; + default = false; + description = "Show search results immediately when Anyrun starts"; + }; + + maxEntries = mkOption { + type = nullOr int; + default = null; + description = "Limit amount of entries shown in total"; + }; + + keybinds = mkOption { + type = nullOr ( + listOf (submodule { + options = { + ctrl = mkOption { + type = bool; + default = false; + }; + alt = mkOption { + type = bool; + default = false; + }; + key = mkOption { + type = str; + description = '' + Name of the GDK keysym. + + A list of possible values can be found at [https://gitlab.gnome.org/GNOME/gtk/-/blob/main/gdk/gdkkeysyms.h] + ''; + }; + action = mkOption { + type = enum [ + "close" + "select" + "up" + "down" + ]; + }; + }; + }) + ); + default = null; + }; + + extraLines = mkOption { + type = nullOr lines; + default = null; + description = "Extra lines to add inside the `Config()` object"; + }; + }; + + extraCss = mkOption { + type = nullOr lines; + default = null; + description = '' + Extra CSS lines to add to {file}`~/.config/anyrun/style.css`. + ''; + }; + + extraConfigFiles = mkOption { + # unfortunately HM doesn't really export the type for files, but hopefully + # hm will throw errors if the options are wrong here, so I'm being *very* loose + type = attrs; + default = {}; + description = '' + Extra files to put in {file}`~/.config/anyrun`, a wrapper over {option}`xdg.configFile`. + ''; + example = '' + programs.anyrun.extraConfigFiles."plugin-name.ron".text = ''' + Config( + some_option: true, + ) + ''' + ''; + }; + }; +} diff --git a/nix/packages/anyrun.nix b/nix/packages/anyrun.nix index dc14fa6..8a35b3e 100644 --- a/nix/packages/anyrun.nix +++ b/nix/packages/anyrun.nix @@ -18,73 +18,78 @@ dontBuildPlugins ? true, lockFile, ... -}: -let +}: let inherit (builtins) fromTOML readFile; cargoToml = fromTOML (readFile ../../anyrun/Cargo.toml); pname = cargoToml.package.name; version = cargoToml.package.version; in -rustPlatform.buildRustPackage { - inherit pname version; - src = builtins.path { - path = lib.sources.cleanSource inputs.self; - name = "${pname}-${version}"; - }; + rustPlatform.buildRustPackage { + inherit pname version; + src = builtins.path { + path = lib.sources.cleanSource inputs.self; + name = "${pname}-${version}"; + }; - strictDeps = true; + strictDeps = true; - cargoLock = { - inherit lockFile; - # Temporary while packages aren't yet stabilized - allowBuiltinFetchGit = true; - }; + cargoLock = { + inherit lockFile; + # Temporary while packages aren't yet stabilized + allowBuiltinFetchGit = true; + }; - nativeBuildInputs = [ - pkg-config - makeWrapper - rustfmt - rustc - cargo - ]; + nativeBuildInputs = [ + pkg-config + makeWrapper + rustfmt + rustc + cargo + ]; - buildInputs = [ - glib - gtk4 - librsvg - gtk4-layer-shell - ]; + buildInputs = [ + glib + gtk4 + librsvg + gtk4-layer-shell + ]; - cargoBuildFlags = if dontBuildPlugins then [ "-p ${pname}" ] else [ ]; + cargoBuildFlags = + if dontBuildPlugins + then ["-p ${pname}"] + else []; - doCheck = true; - checkInputs = [ - cargo - rustc - ]; + doCheck = true; + checkInputs = [ + cargo + rustc + ]; - copyLibs = true; + copyLibs = true; - buildAndTestSubdir = if dontBuildPlugins then pname else null; + buildAndTestSubdir = + if dontBuildPlugins + then pname + else null; - CARGO_BUILD_INCREMENTAL = "false"; - RUST_BACKTRACE = "full"; + CARGO_BUILD_INCREMENTAL = "false"; + RUST_BACKTRACE = "full"; - postInstall = '' - wrapProgram $out/bin/anyrun \ - --set GDK_PIXBUF_MODULE_FILE "$(echo ${librsvg.out}/lib/gdk-pixbuf-2.0/*/loaders.cache)" \ - --prefix ANYRUN_PLUGINS : $out/lib - ''; + postInstall = '' + wrapProgram $out/bin/anyrun \ + --set GDK_PIXBUF_MODULE_FILE "$(echo ${librsvg.out}/lib/gdk-pixbuf-2.0/*/loaders.cache)" \ + --prefix ANYRUN_PLUGINS : $out/lib + ''; - meta = { - description = "A wayland native, highly customizable runner."; - homepage = "https://github.com/anyrun-org/anyrun"; - license = [ lib.licenses.gpl3 ]; - mainProgram = "anyrun"; - maintainers = with lib.maintainers; [ - NotAShelf - n3oney - ]; - }; -} + meta = { + description = "A wayland native, highly customizable runner."; + homepage = "https://github.com/anyrun-org/anyrun"; + license = [lib.licenses.gpl3]; + mainProgram = "anyrun"; + maintainers = with lib.maintainers; [ + NotAShelf + n3oney + ]; + }; + } diff --git a/nix/packages/plugin.nix b/nix/packages/plugin.nix index ac71bf4..9b281c2 100644 --- a/nix/packages/plugin.nix +++ b/nix/packages/plugin.nix @@ -15,63 +15,63 @@ # Generic args name, lockFile, - extraInputs ? [ ], # allow appending buildInputs + extraInputs ? [], # allow appending buildInputs ... -}: -let +}: let cargoToml = builtins.fromTOML (builtins.readFile ../../plugins/${name}/Cargo.toml); pname = cargoToml.package.name; version = cargoToml.package.version; in -rustPlatform.buildRustPackage { - inherit pname version; + rustPlatform.buildRustPackage { + inherit pname version; - src = builtins.path { - path = lib.sources.cleanSource inputs.self; - name = "${pname}-${version}"; - }; - cargoLock = { - inherit lockFile; - # Temporary while packages aren't yet stabilized - allowBuiltinFetchGit = true; - }; + src = builtins.path { + path = lib.sources.cleanSource inputs.self; + name = "${pname}-${version}"; + }; + cargoLock = { + inherit lockFile; + # Temporary while packages aren't yet stabilized + allowBuiltinFetchGit = true; + }; - strictDeps = true; + strictDeps = true; - nativeBuildInputs = [ - pkg-config - makeWrapper - ]; + nativeBuildInputs = [ + pkg-config + makeWrapper + ]; - buildInputs = [ - glib - atk - gtk3 - librsvg - gtk-layer-shell - ] - ++ extraInputs; + buildInputs = + [ + glib + atk + gtk3 + librsvg + gtk-layer-shell + ] + ++ extraInputs; - doCheck = true; - checkInputs = [ - cargo - rustc - ]; + doCheck = true; + checkInputs = [ + cargo + rustc + ]; - copyLibs = true; - cargoBuildFlags = [ "-p ${name}" ]; - buildAndTestSubdir = "plugins/${name}"; + copyLibs = true; + cargoBuildFlags = ["-p ${name}"]; + buildAndTestSubdir = "plugins/${name}"; - CARGO_BUILD_INCREMENTAL = "false"; - RUST_BACKTRACE = "full"; + CARGO_BUILD_INCREMENTAL = "false"; + RUST_BACKTRACE = "full"; - meta = { - description = "The ${name} plugin for Anyrun"; - homepage = "https://github.com/anyrun-org/anyrun"; - license = [ lib.licenses.gpl3 ]; - maintainers = with lib.maintainers; [ - NotAShelf - n3oney - ]; - }; -} + meta = { + description = "The ${name} plugin for Anyrun"; + homepage = "https://github.com/anyrun-org/anyrun"; + license = [lib.licenses.gpl3]; + maintainers = with lib.maintainers; [ + NotAShelf + n3oney + ]; + }; + }