1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.hidutil;
# convert hex to int with nix!
hexToDec = hex: (builtins.fromTOML "a = 0x${removePrefix "0x" hex}").a;
in {
###### interface
options = {
hidutil = {
enable = mkEnableOption "Enable key remapping powered by hidutil";
remapKeys = mkOption {
type = types.listOf (types.submodule {
options = {
VendorID = mkOption {
type = types.nullOr types.str;
default = null;
};
ProductID = mkOption {
type = types.nullOr types.str;
default = null;
};
LocationID = mkOption {
type = types.nullOr types.str;
default = null;
};
UserKeyMapping = mkOption {
type = types.listOf (types.submodule {
options = {
HIDKeyboardModifierMappingSrc = mkOption {
type = types.int;
};
HIDKeyboardModifierMappingDst = mkOption {
type = types.int;
};
};
});
};
};
});
default = [];
};
};
};
###### implementation
config = mkIf cfg.enable {
launchd.user.agents = builtins.listToAttrs (
lib.remove "skip" (
lib.forEach cfg.remapKeys (
entry: (if (entry.LocationID != null) then "skip" else {
name = "activateUserKeyMapping-${entry.VendorID}:${entry.ProductID}";
value.serviceConfig = {
Disabled = if (entry.VendorID != "5ac" && entry.ProductID != "281") then false else true;
Label = "org.nixos.activateUserKeyMapping-${entry.VendorID}:${entry.ProductID}";
ProgramArguments = [
"${pkgs.XPCEventStreamHandler}/bin/xpc_set_event_stream_handler"
"${pkgs.writeScript "hidutil" ''
#!/usr/bin/env bash
osascript -e 'display notification "Load UserKeyMapping for ${entry.VendorID}:${entry.ProductID}" with title "hidutil"'
hidutil property --matching '{"VendorID":0x${entry.VendorID},"ProductID":0x${entry.ProductID}' --set '{"UserKeyMapping":${builtins.toJSON entry.UserKeyMapping}}' > /dev/null
''}"
];
LaunchEvents = {
"com.apple.iokit.matching" = {
"com.apple.device-attach" = {
IOMatchLaunchStream = true;
IOProviderClass = "IOUSBDevice";
idVendor = hexToDec entry.VendorID;
idProduct = hexToDec entry.ProductID;
};
};
};
};
})
)
)
);
system.activationScripts.keyboard.text = ''
# Configuring keyboard
echo "configuring keyboard..." >&2
${lib.concatStringsSep "\n" (lib.forEach cfg.remapKeys (entry: (if entry.LocationID != null then
"hidutil property --matching '{\"LocationID\":0x${entry.LocationID}}' --set '{\"UserKeyMapping\":${builtins.toJSON entry.UserKeyMapping}}' > /dev/null"
else
"hidutil property --matching '{\"VendorID\":0x${entry.VendorID},\"ProductID\":0x${entry.ProductID}}' --set '{\"UserKeyMapping\":${builtins.toJSON entry.UserKeyMapping}}' > /dev/null"
)))}
'';
};
}