kanata.nix 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. { config
  2. , lib
  3. , pkgs
  4. , ...
  5. }:
  6. let
  7. cfg = config.services.kanata;
  8. driverPkg = pkgs.fetchurl {
  9. url = "https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice/releases/download/v6.0.0/Karabiner-DriverKit-VirtualHIDDevice-6.0.0.pkg";
  10. sha256 = "sha256-S14c06v/L/PraLekzIroG6FQnV5dpx0cyJNb9ylB458=";
  11. };
  12. configFile =
  13. lib.mkIf (cfg.config != "")
  14. "${pkgs.writeScript "kanata.kbd" ("\n" + cfg.config + "\n")}";
  15. in
  16. {
  17. options.services.kanata = {
  18. enable = lib.mkEnableOption "kanata, a tool to improve keyboard comfort and usability with advanced customization";
  19. package = lib.mkPackageOption pkgs "kanata" {
  20. example = [ "kanata-with-cmd" ];
  21. extraDescription = ''
  22. ::: {.note}
  23. If {option}`danger-enable-cmd` is enabled in any of the keyboards, the
  24. `kanata-with-cmd` package should be used.
  25. :::
  26. '';
  27. };
  28. errorLogFile = lib.mkOption {
  29. type = lib.types.path;
  30. default = "/Library/Logs/Kanata/kanata.err.log";
  31. example = "/Users/jsmith/Library/Logs/kanata.err.log";
  32. description = "Path to the Kanata error log file";
  33. };
  34. outLogFile = lib.mkOption {
  35. type = lib.types.path;
  36. default = "/Library/Logs/Kanata/kanata.out.log";
  37. example = "/Users/jsmith/Library/Logs/kanata.out.log";
  38. description = "Path to the Kanata stdout log file";
  39. };
  40. config = lib.mkOption {
  41. type = lib.types.str;
  42. default = "";
  43. description = "";
  44. };
  45. };
  46. config = lib.mkIf cfg.enable {
  47. environment.systemPackages = [ cfg.package ];
  48. # Install the driver during activation
  49. system.activationScripts.preActivation.text = ''
  50. echo "Installing Karabiner DriverKit VirtualHIDDevice..." >&2
  51. # Check if already installed by looking for the system extension
  52. if ! systemextensionsctl list 2>/dev/null | grep -q "org.pqrs.Karabiner-DriverKit-VirtualHIDDevice"; then
  53. echo "Installing driver package..." >&2
  54. installer -pkg ${driverPkg} -target /
  55. # Give it a moment to install
  56. sleep 2
  57. # Try to activate the system extension
  58. if [ -f "/Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager" ]; then
  59. echo "Activating system extension..." >&2
  60. /Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager activate || {
  61. echo "System extension activation requires user approval in System Settings > Privacy & Security" >&2
  62. }
  63. fi
  64. else
  65. echo "Karabiner DriverKit already installed" >&2
  66. fi
  67. '';
  68. # Cleanup when disabled
  69. system.activationScripts.postActivation.text = lib.mkIf (!cfg.enable) ''
  70. echo "Cleaning up Karabiner DriverKit..." >&2
  71. # Stop and remove launchd services
  72. for service in org.nixos.karabiner-vhiddaemon org.nixos.karabiner-vhidmanager; do
  73. launchctl unload "/Library/LaunchDaemons/$service.plist" 2>/dev/null || true
  74. rm -f "/Library/LaunchDaemons/$service.plist" 2>/dev/null || true
  75. done
  76. # Remove the manager app copy
  77. rm -rf /Applications/.Nix-Karabiner-DriverKit/ 2>/dev/null || true
  78. # Note: We don't uninstall the system extension as that requires manual user action
  79. # and the extension can be shared with other apps
  80. echo "Note: Karabiner system extension remains installed (can be removed manually in System Settings)" >&2
  81. '';
  82. launchd.daemons.karabiner-vhiddaemon.serviceConfig = {
  83. Label = "org.nixos.karabiner-vhiddaemon";
  84. ProgramArguments = [
  85. "/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-VirtualHIDDevice-Daemon.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Daemon"
  86. ];
  87. RunAtLoad = true;
  88. KeepAlive = true;
  89. StandardOutPath = "/Library/Logs/Karabiner/karabiner-vhiddaemon.out.log";
  90. StandardErrorPath = "/Library/Logs/Karabiner/karabiner-vhiddaemon.err.log";
  91. };
  92. launchd.daemons.karabiner-vhidmanager.serviceConfig = {
  93. Label = "org.nixos.karabiner-vhidmanager";
  94. ProgramArguments = [
  95. "/Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager"
  96. "activate"
  97. ];
  98. RunAtLoad = true;
  99. StandardOutPath = "/Library/Logs/Karabiner/karabiner-vhidmanager.out.log";
  100. StandardErrorPath = "/Library/Logs/Karabiner/karabiner-vhidmanager.err.log";
  101. };
  102. launchd.daemons.kanata.serviceConfig = {
  103. Label = "org.nixos.kanata";
  104. ProgramArguments = [
  105. "${lib.getExe cfg.package}"
  106. "--port"
  107. "10000"
  108. "--debug"
  109. ]
  110. ++ lib.optionals (cfg.config != "") [
  111. "-c"
  112. configFile
  113. ];
  114. RunAtLoad = true;
  115. KeepAlive = true;
  116. StandardOutPath = cfg.outLogFile;
  117. StandardErrorPath = cfg.errorLogFile;
  118. };
  119. };
  120. meta.maintainers = [ ];
  121. }