at 23.11-pre 5.3 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with lib; 4 5let 6 cfg = config.services.kanata; 7 8 keyboard = { 9 options = { 10 devices = mkOption { 11 type = types.listOf types.str; 12 example = [ "/dev/input/by-id/usb-0000_0000-event-kbd" ]; 13 description = mdDoc "Paths to keyboard devices."; 14 }; 15 config = mkOption { 16 type = types.lines; 17 example = '' 18 (defsrc 19 grv 1 2 3 4 5 6 7 8 9 0 - = bspc 20 tab q w e r t y u i o p [ ] \ 21 caps a s d f g h j k l ; ' ret 22 lsft z x c v b n m , . / rsft 23 lctl lmet lalt spc ralt rmet rctl) 24 25 (deflayer qwerty 26 grv 1 2 3 4 5 6 7 8 9 0 - = bspc 27 tab q w e r t y u i o p [ ] \ 28 @cap a s d f g h j k l ; ' ret 29 lsft z x c v b n m , . / rsft 30 lctl lmet lalt spc ralt rmet rctl) 31 32 (defalias 33 ;; tap within 100ms for capslk, hold more than 100ms for lctl 34 cap (tap-hold 100 100 caps lctl)) 35 ''; 36 description = mdDoc '' 37 Configuration other than `defcfg`. 38 39 See [example config files](https://github.com/jtroo/kanata) 40 for more information. 41 ''; 42 }; 43 extraDefCfg = mkOption { 44 type = types.lines; 45 default = ""; 46 example = "danger-enable-cmd yes"; 47 description = mdDoc '' 48 Configuration of `defcfg` other than `linux-dev` (generated 49 from the devices option) and 50 `linux-continue-if-no-devs-found` (hardcoded to be yes). 51 52 See [example config files](https://github.com/jtroo/kanata) 53 for more information. 54 ''; 55 }; 56 extraArgs = mkOption { 57 type = types.listOf types.str; 58 default = [ ]; 59 description = mdDoc "Extra command line arguments passed to kanata."; 60 }; 61 port = mkOption { 62 type = types.nullOr types.port; 63 default = null; 64 example = 6666; 65 description = mdDoc '' 66 Port to run the TCP server on. `null` will not run the server. 67 ''; 68 }; 69 }; 70 }; 71 72 mkName = name: "kanata-${name}"; 73 74 mkDevices = devices: 75 optionalString ((length devices) > 0) "linux-dev ${concatStringsSep ":" devices}"; 76 77 mkConfig = name: keyboard: pkgs.writeText "${mkName name}-config.kdb" '' 78 (defcfg 79 ${keyboard.extraDefCfg} 80 ${mkDevices keyboard.devices} 81 linux-continue-if-no-devs-found yes) 82 83 ${keyboard.config} 84 ''; 85 86 mkService = name: keyboard: nameValuePair (mkName name) { 87 wantedBy = [ "multi-user.target" ]; 88 serviceConfig = { 89 Type = "notify"; 90 ExecStart = '' 91 ${getExe cfg.package} \ 92 --cfg ${mkConfig name keyboard} \ 93 --symlink-path ''${RUNTIME_DIRECTORY}/${name} \ 94 ${optionalString (keyboard.port != null) "--port ${toString keyboard.port}"} \ 95 ${utils.escapeSystemdExecArgs keyboard.extraArgs} 96 ''; 97 98 DynamicUser = true; 99 RuntimeDirectory = mkName name; 100 SupplementaryGroups = with config.users.groups; [ 101 input.name 102 uinput.name 103 ]; 104 105 # hardening 106 DeviceAllow = [ 107 "/dev/uinput rw" 108 "char-input r" 109 ]; 110 CapabilityBoundingSet = [ "" ]; 111 DevicePolicy = "closed"; 112 IPAddressAllow = optional (keyboard.port != null) "localhost"; 113 IPAddressDeny = [ "any" ]; 114 LockPersonality = true; 115 MemoryDenyWriteExecute = true; 116 PrivateNetwork = keyboard.port == null; 117 PrivateUsers = true; 118 ProcSubset = "pid"; 119 ProtectClock = true; 120 ProtectControlGroups = true; 121 ProtectHome = true; 122 ProtectHostname = true; 123 ProtectKernelLogs = true; 124 ProtectKernelModules = true; 125 ProtectKernelTunables = true; 126 ProtectProc = "invisible"; 127 RestrictAddressFamilies = [ "AF_UNIX" ] ++ optional (keyboard.port != null) "AF_INET"; 128 RestrictNamespaces = true; 129 RestrictRealtime = true; 130 SystemCallArchitectures = [ "native" ]; 131 SystemCallFilter = [ 132 "@system-service" 133 "~@privileged" 134 "~@resources" 135 ]; 136 UMask = "0077"; 137 }; 138 }; 139in 140{ 141 options.services.kanata = { 142 enable = mkEnableOption (mdDoc "kanata"); 143 package = mkOption { 144 type = types.package; 145 default = pkgs.kanata; 146 defaultText = literalExpression "pkgs.kanata"; 147 example = literalExpression "pkgs.kanata-with-cmd"; 148 description = mdDoc '' 149 The kanata package to use. 150 151 ::: {.note} 152 If `danger-enable-cmd` is enabled in any of the keyboards, the 153 `kanata-with-cmd` package should be used. 154 ::: 155 ''; 156 }; 157 keyboards = mkOption { 158 type = types.attrsOf (types.submodule keyboard); 159 default = { }; 160 description = mdDoc "Keyboard configurations."; 161 }; 162 }; 163 164 config = mkIf cfg.enable { 165 hardware.uinput.enable = true; 166 167 systemd.services = mapAttrs' mkService cfg.keyboards; 168 }; 169 170 meta.maintainers = with maintainers; [ linj ]; 171}