{ config, lib, pkgs, ... }: let cfg = config.services.actkbd; configFile = pkgs.writeText "actkbd.conf" '' ${lib.concatMapStringsSep "\n" ( { keys, events, attributes, command, ... }: ''${ lib.concatMapStringsSep "+" toString keys }:${lib.concatStringsSep "," events}:${lib.concatStringsSep "," attributes}:${command}'' ) cfg.bindings} ${cfg.extraConfig} ''; bindingCfg = { ... }: { options = { keys = lib.mkOption { type = lib.types.listOf lib.types.int; description = "List of keycodes to match."; }; events = lib.mkOption { type = lib.types.listOf ( lib.types.enum [ "key" "rep" "rel" ] ); default = [ "key" ]; description = "List of events to match."; }; attributes = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "exec" ]; description = "List of attributes."; }; command = lib.mkOption { type = lib.types.str; default = ""; description = "What to run."; }; }; }; in { ###### interface options = { services.actkbd = { enable = lib.mkOption { type = lib.types.bool; default = false; description = '' Whether to enable the {command}`actkbd` key mapping daemon. Turning this on will start an {command}`actkbd` instance for every evdev input that has at least one key (which is okay even for systems with tiny memory footprint, since actkbd normally uses \<100 bytes of memory per instance). This allows binding keys globally without the need for e.g. X11. ''; }; bindings = lib.mkOption { type = lib.types.listOf (lib.types.submodule bindingCfg); default = [ ]; example = lib.literalExpression '' [ { keys = [ 113 ]; events = [ "key" ]; command = "''${pkgs.alsa-utils}/bin/amixer -q set Master toggle"; } ] ''; description = '' Key bindings for {command}`actkbd`. See {command}`actkbd` {file}`README` for documentation. The example shows a piece of what {option}`sound.mediaKeys.enable` does when enabled. ''; }; extraConfig = lib.mkOption { type = lib.types.lines; default = ""; description = '' Literal contents to append to the end of actkbd configuration file. ''; }; }; }; ###### implementation config = lib.mkIf cfg.enable { services.udev.packages = lib.singleton ( pkgs.writeTextFile { name = "actkbd-udev-rules"; destination = "/etc/udev/rules.d/61-actkbd.rules"; text = '' ACTION=="add", SUBSYSTEM=="input", KERNEL=="event[0-9]*", ENV{ID_INPUT_KEY}=="1", TAG+="systemd", ENV{SYSTEMD_WANTS}+="actkbd@$env{DEVNAME}.service" ''; } ); systemd.services."actkbd@" = { enable = true; restartIfChanged = true; unitConfig = { Description = "actkbd on %I"; ConditionPathExists = "%I"; }; serviceConfig = { Type = "forking"; ExecStart = "${pkgs.actkbd}/bin/actkbd -D -c ${configFile} -d %I"; }; }; # For testing environment.systemPackages = [ pkgs.actkbd ]; }; }