diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index e19853efd73..2fc5e274906 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -274,6 +274,7 @@
./services/hardware/tlp.nix
./services/hardware/thinkfan.nix
./services/hardware/trezord.nix
+ ./services/hardware/triggerhappy.nix
./services/hardware/u2f.nix
./services/hardware/udev.nix
./services/hardware/udisks2.nix
diff --git a/nixos/modules/services/hardware/triggerhappy.nix b/nixos/modules/services/hardware/triggerhappy.nix
new file mode 100644
index 00000000000..81d4a1ae65b
--- /dev/null
+++ b/nixos/modules/services/hardware/triggerhappy.nix
@@ -0,0 +1,114 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.triggerhappy;
+
+ socket = "/run/thd.socket";
+
+ configFile = pkgs.writeText "triggerhappy.conf" ''
+ ${concatMapStringsSep "\n"
+ ({ keys, event, cmd, ... }:
+ ''${concatMapStringsSep "+" (x: "KEY_" + x) keys} ${toString { press = 1; hold = 2; release = 0; }.${event}} ${cmd}''
+ )
+ cfg.bindings}
+ ${cfg.extraConfig}
+ '';
+
+ bindingCfg = { config, ... }: {
+ options = {
+
+ keys = mkOption {
+ type = types.listOf types.str;
+ description = "List of keys to match. Key names as defined in linux/input-event-codes.h";
+ };
+
+ event = mkOption {
+ type = types.enum ["press" "hold" "release"];
+ default = "press";
+ description = "Event to match.";
+ };
+
+ cmd = mkOption {
+ type = types.str;
+ description = "What to run.";
+ };
+
+ };
+ };
+
+in
+
+{
+
+ ###### interface
+
+ options = {
+
+ services.triggerhappy = {
+
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to enable the triggerhappy hotkey daemon.
+ '';
+ };
+
+ bindings = mkOption {
+ type = types.listOf (types.submodule bindingCfg);
+ default = [];
+ example = lib.literalExample ''
+ [ { keys = ["PLAYPAUSE"]; cmd = "''${pkgs.mpc_cli}/bin/mpc -q toggle"; } ]
+ '';
+ description = ''
+ Key bindings for triggerhappy.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Literal contents to append to the end of triggerhappy configuration file.
+ '';
+ };
+
+ };
+
+ };
+
+
+ ###### implementation
+
+ config = mkIf cfg.enable {
+
+ systemd.sockets.triggerhappy = {
+ description = "Triggerhappy Socket";
+ wantedBy = [ "sockets.target" ];
+ socketConfig.ListenDatagram = socket;
+ };
+
+ systemd.services.triggerhappy = {
+ wantedBy = [ "multi-user.target" ];
+ after = [ "local-fs.target" ];
+ description = "Global hotkey daemon";
+ serviceConfig = {
+ ExecStart = "${pkgs.triggerhappy}/bin/thd --user nobody --socket ${socket} --triggers ${configFile} --deviceglob /dev/input/event*";
+ };
+ };
+
+ services.udev.packages = lib.singleton (pkgs.writeTextFile {
+ name = "triggerhappy-udev-rules";
+ destination = "/etc/udev/rules.d/61-triggerhappy.rules";
+ text = ''
+ ACTION=="add", SUBSYSTEM=="input", KERNEL=="event[0-9]*", ATTRS{name}!="triggerhappy", \
+ RUN+="${pkgs.triggerhappy}/bin/th-cmd --socket ${socket} --passfd --udev"
+ '';
+ });
+
+ };
+
+}