I’m not *completely* certain that this handles user agents correctly. There is a deprecated command, `launchctl asuser`, that executes a command in the Mach bootstrap context of another user`. <https://scriptingosx.com/2020/08/running-a-command-as-another-user/> claims that this is required when loading and unloading user agents, but I haven’t tested this. Our current launchd agent logic is pretty weird and broken already anyway, so unless this actively regresses things I’d lean towards keeping it like this until we can move over entirely to `launchctl bootstrap`/`launchctl kickstart`, which aren’t deprecated and can address individual users directly. Someone should definitely test it more extensively than I have, though.
116 lines
5.4 KiB
Nix
116 lines
5.4 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.karabiner-elements;
|
|
|
|
parentAppDir = "/Applications/.Nix-Karabiner";
|
|
in
|
|
|
|
{
|
|
options.services.karabiner-elements = {
|
|
enable = mkEnableOption "Karabiner-Elements";
|
|
package = mkPackageOption pkgs "karabiner-elements" { };
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
environment.systemPackages = [ cfg.package ];
|
|
|
|
system.activationScripts.preActivation.text = ''
|
|
rm -rf ${parentAppDir}
|
|
mkdir -p ${parentAppDir}
|
|
# Kernel extensions must reside inside of /Applications, they cannot be symlinks
|
|
cp -r ${cfg.package.driver}/Applications/.Karabiner-VirtualHIDDevice-Manager.app ${parentAppDir}
|
|
'';
|
|
|
|
system.activationScripts.postActivation.text = ''
|
|
echo "attempt to activate karabiner system extension and start daemons" >&2
|
|
launchctl unload /Library/LaunchDaemons/org.nixos.start_karabiner_daemons.plist
|
|
launchctl load -w /Library/LaunchDaemons/org.nixos.start_karabiner_daemons.plist
|
|
'';
|
|
|
|
# We need the karabiner_grabber and karabiner_observer daemons to run after the
|
|
# Nix Store has been mounted, but we can't use wait4path as they need to be
|
|
# executed directly for the Input Monitoring permission. We also want these
|
|
# daemons to auto restart but if they start up without the Nix Store they will
|
|
# refuse to run again until they've been unloaded and loaded back in so we can
|
|
# use a helper daemon to start them. We also only want to run the daemons after
|
|
# the system extension is activated, so we can call activate from the manager
|
|
# which will block until the system extension is activated.
|
|
launchd.daemons.start_karabiner_daemons = {
|
|
script = ''
|
|
${parentAppDir}/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager activate
|
|
launchctl kickstart system/org.pqrs.karabiner.karabiner_grabber
|
|
launchctl kickstart system/org.pqrs.karabiner.karabiner_observer
|
|
'';
|
|
serviceConfig.Label = "org.nixos.start_karabiner_daemons";
|
|
serviceConfig.RunAtLoad = true;
|
|
};
|
|
|
|
launchd.daemons.karabiner_grabber = {
|
|
serviceConfig.ProgramArguments = [
|
|
"${cfg.package}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_grabber"
|
|
];
|
|
serviceConfig.ProcessType = "Interactive";
|
|
serviceConfig.Label = "org.pqrs.karabiner.karabiner_grabber";
|
|
serviceConfig.KeepAlive.SuccessfulExit = true;
|
|
serviceConfig.KeepAlive.Crashed = true;
|
|
serviceConfig.KeepAlive.AfterInitialDemand = true;
|
|
};
|
|
|
|
launchd.daemons.karabiner_observer = {
|
|
serviceConfig.ProgramArguments = [
|
|
"${cfg.package}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_observer"
|
|
];
|
|
|
|
serviceConfig.Label = "org.pqrs.karabiner.karabiner_observer";
|
|
serviceConfig.KeepAlive.SuccessfulExit = true;
|
|
serviceConfig.KeepAlive.Crashed = true;
|
|
serviceConfig.KeepAlive.AfterInitialDemand = true;
|
|
};
|
|
|
|
launchd.daemons.Karabiner-DriverKit-VirtualHIDDeviceClient = {
|
|
command = "\"${cfg.package.driver}/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-DriverKit-VirtualHIDDeviceClient.app/Contents/MacOS/Karabiner-DriverKit-VirtualHIDDeviceClient\"";
|
|
serviceConfig.ProcessType = "Interactive";
|
|
serviceConfig.Label = "org.pqrs.Karabiner-DriverKit-VirtualHIDDeviceClient";
|
|
serviceConfig.KeepAlive = true;
|
|
};
|
|
|
|
# Normally karabiner_console_user_server calls activate on the manager but
|
|
# because we use a custom location we need to call activate manually.
|
|
launchd.user.agents.activate_karabiner_system_ext = {
|
|
serviceConfig.ProgramArguments = [
|
|
"${parentAppDir}/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager" "activate"
|
|
];
|
|
serviceConfig.RunAtLoad = true;
|
|
managedBy = "services.karabiner-elements.enable";
|
|
};
|
|
|
|
# We need this to run every reboot as /run gets nuked so we can't put this
|
|
# inside the preActivation script as it only gets run on darwin-rebuild switch.
|
|
launchd.daemons.setsuid_karabiner_session_monitor = {
|
|
script = ''
|
|
rm -rf /run/wrappers
|
|
mkdir -p /run/wrappers/bin
|
|
install -m4555 "${cfg.package}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_session_monitor" /run/wrappers/bin
|
|
'';
|
|
serviceConfig.RunAtLoad = true;
|
|
serviceConfig.KeepAlive.SuccessfulExit = false;
|
|
};
|
|
|
|
launchd.user.agents.karabiner_session_monitor = {
|
|
serviceConfig.ProgramArguments = [
|
|
"/bin/sh" "-c"
|
|
"/bin/wait4path /run/wrappers/bin && /run/wrappers/bin/karabiner_session_monitor"
|
|
];
|
|
serviceConfig.Label = "org.pqrs.karabiner.karabiner_session_monitor";
|
|
serviceConfig.KeepAlive = true;
|
|
managedBy = "services.karabiner-elements.enable";
|
|
};
|
|
|
|
environment.userLaunchAgents."org.pqrs.karabiner.agent.karabiner_grabber.plist".source = "${cfg.package}/Library/LaunchAgents/org.pqrs.karabiner.agent.karabiner_grabber.plist";
|
|
environment.userLaunchAgents."org.pqrs.karabiner.agent.karabiner_observer.plist".source = "${cfg.package}/Library/LaunchAgents/org.pqrs.karabiner.agent.karabiner_observer.plist";
|
|
environment.userLaunchAgents."org.pqrs.karabiner.karabiner_console_user_server.plist".source = "${cfg.package}/Library/LaunchAgents/org.pqrs.karabiner.karabiner_console_user_server.plist";
|
|
};
|
|
}
|