diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 1a9cce76ee9..e5da4fb2fb4 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -149,6 +149,7 @@ ./services/games/minecraft-server.nix ./services/games/minetest-server.nix ./services/hardware/acpid.nix + ./services/hardware/actkbd.nix ./services/hardware/amd-hybrid-graphics.nix ./services/hardware/bluetooth.nix ./services/hardware/freefall.nix diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix index 653c0ed5d70..c63f4dc8d7f 100644 --- a/nixos/modules/services/audio/alsa.nix +++ b/nixos/modules/services/audio/alsa.nix @@ -33,6 +33,16 @@ in ''; }; + enableMediaKeys = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable volume and capture control with keyboard media keys. + + Enabling this will turn on . + ''; + }; + extraConfig = mkOption { type = types.lines; default = ""; @@ -80,6 +90,23 @@ in }; }; + services.actkbd = mkIf config.sound.enableMediaKeys { + enable = true; + bindings = [ + # "Mute" media key + { keys = [ 113 ]; events = [ "key" ]; command = "${alsaUtils}/bin/amixer -q set Master toggle"; } + + # "Lower Volume" media key + { keys = [ 114 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master 1- unmute"; } + + # "Raise Volume" media key + { keys = [ 115 ]; events = [ "key" "rep" ]; command = "${alsaUtils}/bin/amixer -q set Master 1+ unmute"; } + + # "Mic Mute" media key + { keys = [ 190 ]; events = [ "key" ]; command = "${alsaUtils}/bin/amixer -q set Capture toggle"; } + ]; + }; + }; } diff --git a/nixos/modules/services/hardware/actkbd.nix b/nixos/modules/services/hardware/actkbd.nix new file mode 100644 index 00000000000..82de362c371 --- /dev/null +++ b/nixos/modules/services/hardware/actkbd.nix @@ -0,0 +1,130 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.actkbd; + + configFile = pkgs.writeText "actkbd.conf" '' + ${concatMapStringsSep "\n" + ({ keys, events, attributes, command, ... }: + ''${concatMapStringsSep "+" toString keys}:${concatStringsSep "," events}:${concatStringsSep "," attributes}:${command}'' + ) + cfg.bindings} + ${cfg.extraConfig} + ''; + + bindingCfg = { config, ... }: { + options = { + + keys = mkOption { + type = types.listOf types.int; + description = "List of keycodes to match."; + }; + + events = mkOption { + type = types.listOf (types.enum ["key" "rep" "rel"]); + default = [ "key" ]; + description = "List of events to match."; + }; + + attributes = mkOption { + type = types.listOf types.str; + default = [ "exec" ]; + description = "List of attributes."; + }; + + command = mkOption { + type = types.str; + default = ""; + description = "What to run."; + }; + + }; + }; + +in + +{ + + ###### interface + + options = { + + services.actkbd = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the actkbd key mapping daemon. + + Turning this on will start an 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 = mkOption { + type = types.listOf (types.submodule bindingCfg); + default = []; + example = lib.literalExample '' + [ { keys = [ 113 ]; events = [ "key" ]; command = "''${pkgs.alsaUtils}/bin/amixer -q set Master toggle"; } + ] + ''; + description = '' + Key bindings for actkbd. + + See actkbd README for documentation. + + The example shows a piece of what does when enabled. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Literal contents to append to the end of actkbd configuration file. + ''; + }; + + }; + + }; + + + ###### implementation + + config = 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"; + }; + }; + + }; + +}