Parcourir la source

feat(kanata): Add support for Kanata and Karabiner DriverKit

Zander Hawke il y a 10 mois
Parent
commit
5658e5741a

+ 1 - 0
hosts/meili/default.nix

@@ -3,6 +3,7 @@
   imports =
     [
       outputs.modules.global.nix-config
+      ./kanata.nix
       ./system.nix
       ./software.nix
     ]

+ 75 - 0
hosts/meili/kanata.kbd

@@ -0,0 +1,75 @@
+;; Kanata configuration for MacBook Air M2 with advanced home row modifiers
+;; Uses tap-hold-release-keys with bilateral combinations for better accuracy
+
+(defcfg
+  process-unmapped-keys yes
+  ;; Adjust these paths based on your system
+  ;; macOS typically uses something like this:
+  ;; linux-dev /dev/input/by-path/your-keyboard-path
+  ;; For macOS, you might need to specify the input device differently
+)
+
+(defsrc
+  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
+  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
+  caps a    s    d    f    g    h    j    k    l    ;    '    ret
+  lsft z    x    c    v    b    n    m    ,    .    /    rsft
+  fn   lctl lalt lmet           spc           rmet ralt left down up   rght
+)
+
+(defvar
+  ;; Timing values - adjust based on your typing speed
+  tap-time 200
+  hold-time 150
+
+  ;; Define which keys are on each hand for bilateral combinations
+  left-hand-keys (q w e r t a s d f g z x c v b)
+  right-hand-keys (y u i o p h j k l ; n m , . /)
+)
+
+;; Base layer with home row modifiers and proper media keys
+(deflayer base
+  esc  brdn brup mctl @spotlight f5 f6 prev pp   next mute vold volu
+  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
+  caps @a   @s   @d   @f   g    h    @j   @k   @l   @;   '    ret
+  lsft z    x    c    v    b    n    m    ,    .    /    rsft
+  fn   lctl lalt lmet           spc           rmet ralt left down up   rght
+)
+
+;; Layer without modifiers - used temporarily when tapping home row keys
+(deflayer nomods
+  esc  brdn brup mctl @spotlight f5 f6 prev pp   next mute vold volu
+  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
+  caps a    s    d    f    g    h    j    k    l    ;    '    ret
+  lsft z    x    c    v    b    n    m    ,    .    /    rsft
+  fn   lctl lalt lmet           spc           rmet ralt left down up   rght
+)
+
+(deffakekeys
+  to-base (layer-switch base)
+)
+
+(defalias
+  spotlight (multi lmet spc)  ;; Command + Space
+  ;; Tap behavior: switch to nomods layer temporarily, then back to base
+  tap (multi
+    (layer-switch nomods)
+    (on-idle-fakekey to-base tap 20)
+  )
+
+  ;; Home row modifiers with bilateral combinations
+  ;; Left hand keys with LEFT-side modifiers
+  a (tap-hold-release-keys $tap-time $hold-time (multi a @tap) lctl $right-hand-keys)
+  s (tap-hold-release-keys $tap-time $hold-time (multi s @tap) lalt $right-hand-keys) 
+  d (tap-hold-release-keys $tap-time $hold-time (multi d @tap) lmet $right-hand-keys)
+  f (tap-hold-release-keys $tap-time $hold-time (multi f @tap) lsft $right-hand-keys)
+  
+  ;; Right hand keys with RIGHT-side modifiers
+  j (tap-hold-release-keys $tap-time $hold-time (multi j @tap) rsft $left-hand-keys)
+  k (tap-hold-release-keys $tap-time $hold-time (multi k @tap) rmet $left-hand-keys)
+  l (tap-hold-release-keys $tap-time $hold-time (multi l @tap) ralt $left-hand-keys)
+  ; (tap-hold-release-keys $tap-time $hold-time (multi ; @tap) rctl $left-hand-keys)
+)

+ 6 - 0
hosts/meili/kanata.nix

@@ -0,0 +1,6 @@
+{
+  services.kanata = {
+    enable = true;
+    config = builtins.readFile ./kanata.kbd;
+  };
+}

+ 1 - 0
modules/darwin/default.nix

@@ -1,4 +1,5 @@
 {
+  kanata = import ./kanata.nix;
   pam-reattach = import ./security/pam-reattach.nix;
   remote-login = import ./networking/remote-login.nix;
   rosetta = import ./system/rosetta.nix;

+ 131 - 0
modules/darwin/kanata.nix

@@ -0,0 +1,131 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  cfg = config.services.kanata;
+  driverPkg = pkgs.fetchurl {
+    url = "https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice/releases/download/v6.0.0/Karabiner-DriverKit-VirtualHIDDevice-6.0.0.pkg";
+    sha256 = "sha256-S14c06v/L/PraLekzIroG6FQnV5dpx0cyJNb9ylB458=";
+  };
+
+  configFile = lib.mkIf (cfg.config != "")
+    "${pkgs.writeScript "kanata.kbd" ("\n" + cfg.config + "\n")}";
+in
+{
+  options.services.kanata = {
+    enable = lib.mkEnableOption "kanata, a tool to improve keyboard comfort and usability with advanced customization";
+
+    package = lib.mkPackageOption pkgs "kanata" {
+      example = [ "kanata-with-cmd" ];
+      extraDescription = ''
+        ::: {.note}
+        If {option}`danger-enable-cmd` is enabled in any of the keyboards, the
+        `kanata-with-cmd` package should be used.
+        :::
+      '';
+    };
+
+    errorLogFile = lib.mkOption {
+      type = lib.types.path;
+      default = "/Library/Logs/Kanata/kanata.err.log";
+      example = "/Users/jsmith/Library/Logs/kanata.err.log";
+      description = "Path to the Kanata error log file";
+    };
+
+    outLogFile = lib.mkOption {
+      type = lib.types.path;
+      default = "/Library/Logs/Kanata/kanata.out.log";
+      example = "/Users/jsmith/Library/Logs/kanata.out.log";
+      description = "Path to the Kanata stdout log file";
+    };
+
+    config = lib.mkOption {
+      type = lib.types.str;
+      default = "";
+      description = "";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    # Install the driver during activation
+    system.activationScripts.preActivation.text = ''
+      echo "Installing Karabiner DriverKit VirtualHIDDevice..." >&2
+      
+      # Check if already installed by looking for the system extension
+      if ! systemextensionsctl list 2>/dev/null | grep -q "org.pqrs.Karabiner-DriverKit-VirtualHIDDevice"; then
+        echo "Installing driver package..." >&2
+        installer -pkg ${driverPkg} -target /
+        
+        # Give it a moment to install
+        sleep 2
+        
+        # Try to activate the system extension
+        if [ -f "/Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager" ]; then
+          echo "Activating system extension..." >&2
+          /Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager activate || {
+            echo "System extension activation requires user approval in System Settings > Privacy & Security" >&2
+          }
+        fi
+      else
+        echo "Karabiner DriverKit already installed" >&2
+      fi
+    '';
+
+    # Cleanup when disabled
+    system.activationScripts.postActivation.text = lib.mkIf (!cfg.enable) ''
+      echo "Cleaning up Karabiner DriverKit..." >&2
+      
+      # Stop and remove launchd services
+      for service in org.nixos.karabiner-vhiddaemon org.nixos.karabiner-vhidmanager; do
+        launchctl unload "/Library/LaunchDaemons/$service.plist" 2>/dev/null || true
+        rm -f "/Library/LaunchDaemons/$service.plist" 2>/dev/null || true
+      done
+      
+      # Remove the manager app copy
+      rm -rf /Applications/.Nix-Karabiner-DriverKit/ 2>/dev/null || true
+      
+      # Note: We don't uninstall the system extension as that requires manual user action
+      # and the extension can be shared with other apps
+      echo "Note: Karabiner system extension remains installed (can be removed manually in System Settings)" >&2
+    '';
+
+    launchd.daemons.karabiner-vhiddaemon.serviceConfig = {
+      Label = "org.nixos.karabiner-vhiddaemon";
+      ProgramArguments = [
+        "/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-VirtualHIDDevice-Daemon.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Daemon"
+      ];
+      RunAtLoad = true;
+      KeepAlive = true;
+    };
+
+    launchd.daemons.karabiner-vhidmanager.serviceConfig = {
+      Label = "org.nixos.karabiner-vhidmanager";
+      ProgramArguments = [
+        "/Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager"
+        "activate"
+      ];
+      RunAtLoad = true;
+    };
+
+    launchd.daemons.kanata.serviceConfig = {
+      Label = "org.nixos.kanata";
+      ProgramArguments = [
+        "${lib.getExe cfg.package}"
+        "--port"
+        "10000"
+        # "--debug"
+      ] ++ lib.optionals (cfg.config != "") [ "-c" configFile ];
+
+      RunAtLoad = true;
+      KeepAlive = true;
+      StandardOutPath = cfg.outLogFile;
+      StandardErrorPath = cfg.errorLogFile;
+    };
+  };
+
+  meta.maintainers = [ ];
+}

+ 2 - 1
packages/default.nix

@@ -1,8 +1,9 @@
 { pkgs ? (import <nixpkgs>) { } }: {
+  aerospace-tmux-focus = pkgs.callPackage ./aerospace-tmux-focus.nix { };
   hello = pkgs.callPackage ./hello.nix { };
+  karabiner-driverkit = pkgs.callPackage ./karabiner-driverkit.nix { };
   opencode-nvim = pkgs.callPackage ./opencode-nvim.nix { };
   photo-cli = pkgs.callPackage ./photo-cli.nix { };
   tmux-select-pane-no-wrap = pkgs.callPackage ./tmux-select-pane-no-wrap.nix { };
   tmux-sessionizer = pkgs.callPackage ./tmux-sessionizer.nix { };
-  aerospace-tmux-focus = pkgs.callPackage ./aerospace-tmux-focus.nix { };
 }

+ 44 - 0
packages/karabiner-driverkit.nix

@@ -0,0 +1,44 @@
+{ lib
+, stdenv
+, fetchurl
+, xar
+, cpio
+}: stdenv.mkDerivation rec {
+  pname = "karabiner-driverkit";
+  version = "6.0.0";
+
+  src = fetchurl {
+    url = "https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice/releases/download/v${version}/Karabiner-DriverKit-VirtualHIDDevice-${version}.pkg";
+    sha256 = "sha256-S14c06v/L/PraLekzIroG6FQnV5dpx0cyJNb9ylB458=";
+  };
+
+  sourceRoot = ".";
+
+  nativeBuildInputs = [ cpio xar ];
+
+  unpackPhase = ''
+    runHook preUnpack
+
+    xar -xf $src
+    zcat Payload | cpio -id
+
+    runHook postUnpack
+  '';
+
+  installPhase = ''
+    runHook preInstall
+
+    mkdir -p $out
+    cp -r Applications $out/
+    cp -r Library $out/
+
+    runHook postInstall
+  '';
+
+  meta = with lib; {
+    description = "Virtual HID Device Driver for Karabiner-Elements";
+    homepage = "https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice";
+    platforms = platforms.darwin;
+    license = licenses.unlicense;
+  };
+}