Skip to content

Commit

Permalink
nixos/kmonad: misc improvements (NixOS#369837)
Browse files Browse the repository at this point in the history
  • Loading branch information
r-vdp authored Jan 1, 2025
2 parents bcaae99 + 8463300 commit b62fd09
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 19 deletions.
42 changes: 24 additions & 18 deletions nixos/modules/services/hardware/kmonad.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
example = "laptop-internal";
description = "Keyboard name.";
};
Expand All @@ -35,16 +36,16 @@ let
Since KMonad runs as an unprivileged user, it may sometimes
need extra permissions in order to read the keyboard device
file. If your keyboard's device file isn't in the input
group you'll need to list its group in this option.
group, you'll need to list its group in this option.
'';
};

defcfg = {
enable = lib.mkEnableOption ''
Automatically generate the defcfg block.
automatic generation of the defcfg block.
When this is option is set to true the config option for
this keyboard should not include a defcfg block.
When this option is set to true, the config option for
this keyboard should not include a defcfg block
'';

compose = {
Expand All @@ -55,36 +56,34 @@ let
};

delay = lib.mkOption {
type = lib.types.int;
type = lib.types.ints.unsigned;
default = 5;
description = "The delay (in milliseconds) between compose key sequences.";
};
};

fallthrough = lib.mkEnableOption "Re-emit unhandled key events.";
fallthrough = lib.mkEnableOption "re-emitting unhandled key events";

allowCommands = lib.mkEnableOption "Allow keys to run shell commands.";
allowCommands = lib.mkEnableOption "keys to run shell commands";
};

config = lib.mkOption {
type = lib.types.lines;
description = "Keyboard configuration.";
};
};

config = {
name = lib.mkDefault name;
};
};

mkName = name: "kmonad-" + name;

# Create a complete KMonad configuration file:
mkCfg =
keyboard:
let
defcfg = ''
(defcfg
input (device-file "${keyboard.device}")
output (uinput-sink "kmonad-${keyboard.name}")
output (uinput-sink "${mkName keyboard.name}")
${lib.optionalString (keyboard.defcfg.compose.key != null) ''
cmp-seq ${keyboard.defcfg.compose.key}
cmp-seq-delay ${toString keyboard.defcfg.compose.delay}
Expand All @@ -95,17 +94,17 @@ let
'';
in
pkgs.writeTextFile {
name = "kmonad-${keyboard.name}.cfg";
name = "${mkName keyboard.name}.kbd";
text = lib.optionalString keyboard.defcfg.enable (defcfg + "\n") + keyboard.config;
checkPhase = "${cfg.package}/bin/kmonad -d $out";
checkPhase = "${lib.getExe cfg.package} -d $out";
};

# Build a systemd path config that starts the service below when a
# keyboard device appears:
mkPath =
keyboard:
let
name = "kmonad-${keyboard.name}";
name = mkName keyboard.name;
in
lib.nameValuePair name {
description = "KMonad trigger for ${keyboard.device}";
Expand All @@ -129,7 +128,7 @@ let
++ cfg.extraArgs
++ [ "${mkCfg keyboard}" ];
in
lib.nameValuePair "kmonad-${keyboard.name}" {
lib.nameValuePair (mkName keyboard.name) {
description = "KMonad for ${keyboard.device}";
script = lib.escapeShellArgs cmd;
unitConfig = {
Expand Down Expand Up @@ -159,9 +158,9 @@ let
in
{
options.services.kmonad = {
enable = lib.mkEnableOption "KMonad: An advanced keyboard manager.";
enable = lib.mkEnableOption "KMonad: an advanced keyboard manager";

package = lib.mkPackageOption pkgs "kmonad" { };
package = lib.mkPackageOption pkgs "KMonad" { default = "kmonad"; };

keyboards = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule keyboard);
Expand All @@ -188,4 +187,11 @@ in
services = lib.mapAttrs' (_: mkService) cfg.keyboards;
};
};

meta = {
maintainers = with lib.maintainers; [
linj
rvdp
];
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ in {
keyd = handleTest ./keyd.nix {};
keymap = handleTest ./keymap.nix {};
kimai = handleTest ./kimai.nix {};
kmonad = runTest ./kmonad.nix;
knot = handleTest ./knot.nix {};
komga = handleTest ./komga.nix {};
krb5 = discoverTests (import ./krb5);
Expand Down
47 changes: 47 additions & 0 deletions nixos/tests/kmonad.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{ lib, ... }:

{
name = "kmonad";

meta = {
maintainers = with lib.maintainers; [ linj ];
};

nodes = {
machine = {
services.kmonad = {
enable = true;
keyboards = {
defaultKbd = {
device = "/dev/input/by-id/vm-default-kbd";
defcfg = {
enable = true;
fallthrough = true;
};
config = ''
(defsrc :name default-src
1)
(deflayer default-layer :source default-src
@T2)
(defalias
T2 2)
'';
};
};
};

# make a determinate symlink to the default vm keyboard for kmonad to use
services.udev.extraRules = ''
ACTION=="add", KERNEL=="event*", SUBSYSTEM=="input", ATTRS{name}=="QEMU Virtio Keyboard", ATTRS{id/product}=="0001", ATTRS{id/vendor}=="0627", SYMLINK+="input/by-id/vm-default-kbd"
'';
};
};

testScript = ''
service_name = "kmonad-defaultKbd"
machine.wait_for_unit(f"{service_name}.service")
with subtest("kmonad is running"):
machine.succeed(f"systemctl status {service_name}")
'';
}
9 changes: 8 additions & 1 deletion pkgs/development/haskell-modules/configuration-nix.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1478,7 +1478,14 @@ self: super: builtins.intersectAttrs super {
# cause actual problems in dependent packages, see https://github.com/lehins/pvar/issues/4
pvar = dontCheck super.pvar;

kmonad = enableSeparateBinOutput super.kmonad;
kmonad = lib.pipe super.kmonad [
enableSeparateBinOutput
(overrideCabal (drv: {
passthru = lib.recursiveUpdate
drv.passthru or { }
{ tests.nixos = pkgs.nixosTests.kmonad; };
}))
];

xmobar = enableSeparateBinOutput super.xmobar;

Expand Down

0 comments on commit b62fd09

Please sign in to comment.