1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.triggerhappy;
8
9 socket = "/run/thd.socket";
10
11 configFile = pkgs.writeText "triggerhappy.conf" ''
12 ${concatMapStringsSep "\n"
13 ({ keys, event, cmd, ... }:
14 ''${concatMapStringsSep "+" (x: "KEY_" + x) keys} ${toString { press = 1; hold = 2; release = 0; }.${event}} ${cmd}''
15 )
16 cfg.bindings}
17 ${cfg.extraConfig}
18 '';
19
20 bindingCfg = { ... }: {
21 options = {
22
23 keys = mkOption {
24 type = types.listOf types.str;
25 description = lib.mdDoc "List of keys to match. Key names as defined in linux/input-event-codes.h";
26 };
27
28 event = mkOption {
29 type = types.enum ["press" "hold" "release"];
30 default = "press";
31 description = lib.mdDoc "Event to match.";
32 };
33
34 cmd = mkOption {
35 type = types.str;
36 description = lib.mdDoc "What to run.";
37 };
38
39 };
40 };
41
42in
43
44{
45
46 ###### interface
47
48 options = {
49
50 services.triggerhappy = {
51
52 enable = mkOption {
53 type = types.bool;
54 default = false;
55 description = lib.mdDoc ''
56 Whether to enable the {command}`triggerhappy` hotkey daemon.
57 '';
58 };
59
60 user = mkOption {
61 type = types.str;
62 default = "nobody";
63 example = "root";
64 description = lib.mdDoc ''
65 User account under which {command}`triggerhappy` runs.
66 '';
67 };
68
69 bindings = mkOption {
70 type = types.listOf (types.submodule bindingCfg);
71 default = [];
72 example = lib.literalExpression ''
73 [ { keys = ["PLAYPAUSE"]; cmd = "''${pkgs.mpc-cli}/bin/mpc -q toggle"; } ]
74 '';
75 description = lib.mdDoc ''
76 Key bindings for {command}`triggerhappy`.
77 '';
78 };
79
80 extraConfig = mkOption {
81 type = types.lines;
82 default = "";
83 description = lib.mdDoc ''
84 Literal contents to append to the end of {command}`triggerhappy` configuration file.
85 '';
86 };
87
88 };
89
90 };
91
92
93 ###### implementation
94
95 config = mkIf cfg.enable {
96
97 systemd.sockets.triggerhappy = {
98 description = "Triggerhappy Socket";
99 wantedBy = [ "sockets.target" ];
100 socketConfig.ListenDatagram = socket;
101 };
102
103 systemd.services.triggerhappy = {
104 wantedBy = [ "multi-user.target" ];
105 description = "Global hotkey daemon";
106 serviceConfig = {
107 ExecStart = "${pkgs.triggerhappy}/bin/thd ${optionalString (cfg.user != "root") "--user ${cfg.user}"} --socket ${socket} --triggers ${configFile} --deviceglob /dev/input/event*";
108 };
109 };
110
111 services.udev.packages = lib.singleton (pkgs.writeTextFile {
112 name = "triggerhappy-udev-rules";
113 destination = "/etc/udev/rules.d/61-triggerhappy.rules";
114 text = ''
115 ACTION=="add", SUBSYSTEM=="input", KERNEL=="event[0-9]*", ATTRS{name}!="triggerhappy", \
116 RUN+="${pkgs.triggerhappy}/bin/th-cmd --socket ${socket} --passfd --udev"
117 '';
118 });
119
120 };
121
122}