{ pkgs, config, lib, ... }: let cfg = config.jade.input.remapping; evremap = pkgs.rustPlatform.buildRustPackage { pname = "evremap"; version = "0.1.0"; src = pkgs.fetchFromGitHub { owner = "wez"; repo = "evremap"; rev = "4480c4eda223b98899b0fbd926bc34f7bd0e1a18"; sha256 = "sha256-BxSrphgW1n465FX6bKVkq6O0XE2JqanfSYlsGwWUWkQ="; }; useFetchCargoVendor = true; cargoHash = "sha256-O1rJ48m8Q7kZABw/UNpmUT7FFBWQedo6CuV+NX9kDt8="; nativeBuildInputs = [ pkgs.pkg-config ]; buildInputs = [ pkgs.libevdev ]; }; toml = pkgs.formats.toml { }; in with lib; { options.jade.input.remapping = { enable = mkEnableOption "Enable evremap"; devices = mkOption { type = types.attrsOf ( types.submodule ( { name, ... }: { options = { device_name = mkOption { type = types.str; description = "The device name"; default = name; }; remap = mkOption { type = types.listOf ( types.submodule ( { ... }: { options.input = mkOption { type = types.listOf types.str; }; options.output = mkOption { type = types.listOf types.str; }; } ) ); default = [ ]; }; swapKeys = mkOption { description = "Lists with two keys to be swapped on the keyboard layout."; type = types.listOf ( # verify that each key swapping list contains two elements types.addCheck (types.listOf types.str) (v: builtins.length v == 2) ); default = [ ]; }; dual_role = mkOption { type = types.listOf ( types.submodule ( { ... }: { options.input = mkOption { type = types.str; }; options.hold = mkOption { type = types.listOf types.str; }; options.tap = mkOption { type = types.listOf types.str; }; } ) ); default = [ ]; }; }; } ) ); }; }; config = mkIf cfg.enable ( with builtins; let devs = map ( { device_name, remap, swapKeys, dual_role, }: { inherit device_name dual_role; # expand swapKeys to normal remaps remap = concatLists [ remap (lib.lists.flatten ( map (keys: [ { input = [ (head keys) ]; output = [ (lib.lists.last keys) ]; } { input = [ (lib.lists.last keys) ]; output = [ (head keys) ]; } ]) swapKeys )) ]; } ) (attrValues cfg.devices); in { # generate numbered systemd services for each device to be remapped # https://github.com/wez/evremap/issues/17 systemd.services = listToAttrs ( genList (i: { name = "evremap${toString i}"; value = let cfgFile = toml.generate "remaps-${toString i}.toml" (elemAt devs i); in { wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "-${evremap}/bin/evremap remap ${cfgFile}"; unitConfig = { Restart = "on-failure"; }; }; }) (length devs) ); environment.systemPackages = [ evremap ]; } ); }