2.1 KiB
2.1 KiB
In-Process Native HID Remapping
User contract
The app installs physical Caps->F19 mappings and requests any required input privileges under Capswitch's own identity. Never request that the user grant Input Monitoring or Accessibility to a system binary or helper. Do not launch /usr/bin/hidutil or other executables.
Apple-supported mechanism
Technical Note TN2450 documents:
- IOHIDEventSystemClientCreateSimpleClient
- IOHIDEventSystemClientCopyServices
- IOHIDServiceClientConformsTo(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)
- IOHIDServiceClientCopyProperty/SetProperty with UserKeyMapping.
The IOKit adapter is compiled inside the existing CapswitchHID SwiftPM C target, linked to IOKit in the Capswitch executable.
Safety and lifecycle
- Set up active Quartz event tap before changing HID keyboard mappings.
- Enumerate connected keyboard services, read each current UserKeyMapping, and fail closed if malformed or conflicting with Caps/F19.
- Preserve other mappings while appending Caps->F19 only when absent.
- Verify the result per service, do not trust a successful setter alone.
- Persist owned service registry IDs under Capswitch's own preferences so a crash/restart can restore exactly Capswitch-owned rules.
- On Pause/Quit, remove only the exact pair for those owned services.
- A disconnected service's mapping is discarded by macOS.
- On wake/reconnect, re-enumerate and reconcile.
- No privileged helpers, keyboard drivers, or external subprocesses.
- Both Accessibility and Input Monitoring requests target Capswitch; the OS remains responsible for user consent.
Acceptance tests
- Build without Xcode IDE using Taskfile and Command Line Tools on macOS 14.8.9.
- First launch creates permission prompts where appropriate.
- Native --doctor reports keyboard services and applied pair counts.
- Short Caps taps between typed letters register as F19 down/up and switch exactly once on release.
- Option+Caps toggles real Caps Lock including actual typing state.
- Pause/Quit preserves foreign HID remappings.
- Keyboard reconnect / sleep recovery works.