macoc/README.md

133 lines
5.9 KiB
Markdown

# Capswitch
Small native macOS menu-bar app. Physical Caps Lock is HID-remapped to F19, which Capswitch intercepts to select the next input source **on release**, including during continuous fast typing. Caps+Option retains the system Caps Lock function.
## Requirements
- macOS 14 or newer and Apple Command Line Tools (install with xcode-select --install).
- Task (Taskfile runner).
- Grant **Accessibility** under **System Settings -> Privacy & Security** to
let the app suppress F19. On some macOS setups you may also need
**Input Monitoring**. The menu shows both permissions separately and
provides a grant action; active CGEventTap may work with Accessibility alone.
- **Important for users coming from the stock Caps switch:** Disable
**Keyboard -> Text Input -> Edit -> Use Caps Lock to switch to/from ABC**
while using Capswitch. That macOS shortcut must not compete with our own
input-source selector. Capswitch does not change this setting automatically.
- Keep the Caps Lock key set to its normal role in **Keyboard -> Keyboard Shortcuts -> Modifier Keys**, not "No Action".
- No Xcode project, root helper, Karabiner or third-party driver is needed.
## Build and test on your Mac
**Quit the currently running Capswitch from its menu first.** Opening an already running app may activate an old process even if a new bundle was created. The Taskfile refuses to replace the app bundle while a Capswitch process is running.
~~~bash
git pull origin main
task test
task bundle
open build/Capswitch.app
~~~
After opening Capswitch, grant **Accessibility** from the status-bar menu.
If the event tap is refused or no F19 events are received, also grant
**Input Monitoring** from the menu. Restart Capswitch after granting
permissions if macOS prompts you to do so.
### macOS Sonoma: grant Input Monitoring to hidutil itself
On macOS 14, an Input Monitoring grant for Capswitch.app may not be
sufficient for its child process /usr/bin/hidutil, which is used to set
the actual global HID mapping. If task doctor reports (null), Caps->F19
is not installed (regardless of whether the app process is running).
Open **System Settings -> Privacy & Security -> Input Monitoring**, click
the + button, then press **Command+Shift+G** in the file picker and enter
**/usr/bin/hidutil**. Select hidutil and enable its Input Monitoring
toggle. This is separate from Capswitch.app. Quit and reopen Capswitch,
then run task doctor again. See also the Sonoma notes for
mvp/mac-remap-keys.
If the mapping remains null, inspect the **Capswitch menu** and capture
the Accessibility / Event tap / HID / Error entries. The application may
be running without having created its event tap at all; Input Monitoring
for hidutil cannot fix missing Accessibility permission.
If you previously used macOS's built-in Caps-to-input-source switching, open
System Settings -> Keyboard -> Text Input -> Edit and uncheck
"Use Caps Lock to switch to and from [last used Latin input source]".
Keep Caps assigned to the normal Caps Lock action in Modifier Keys.
Open the Capswitch menu and confirm:
- Accessibility: Granted
- Input Monitoring: Granted (or not granted if the active tap works)
- Event tap: Enabled
- HID Caps -> F19: configured
These confirm permissions and the global configuration, **not** yet that the
physical keyboard is actually sending F19. Use the counters to verify it.
## Behavior
- Plain physical Caps emits F19 keyDown/keyUp. Capswitch swallows both events and on F19 keyUp selects the next enabled keyboard input source.
- Caps+Option in either order toggles the actual system Caps Lock state through IOKit after Option has been released.
- Pause from the menu disables the event tap and restores the Caps key to its original purpose.
- Quit normally restores only Capswitch's Caps->F19 mapping, leaving unrelated mappings alone.
- On wake or keyboard reconnection, Capswitch checks whether its HID mapping is still present.
## Diagnostics
Open the status-bar menu while testing quick typing. It displays:
F19 ↓N ↑N · switches N
No typed text is recorded. If the F19 counts do not change, the HID remap is not producing F19 events
or the event tap is not receiving them. First check both permissions, the
event tap status and the native Caps switch preference. Then use the
"Reapply Caps -> F19 mapping" menu action and repeat the test.
If F19 counts increase but switches do not, investigate TIS input-source
selection. If the stock Caps input switch still operates while Capswitch
is active, the HID remap may not be applied to your physical keyboard.
To gather diagnostics without logging any typed text:
~~~bash
task doctor
~~~
This prints the native Caps preference, global UserKeyMapping and the keyboard
service-specific mapping; these can differ even when hidutil exits successfully.
Caps+Option through IOKit and its LED behavior still require verification on an actual Mac.
## HID remapping safety
HID key mapping is global. Capswitch first reads existing mappings, refuses collisions involving Caps or F19, and preserves unrelated remaps. It removes only its own rule on normal Quit/Pause. A small ownership marker allows a later launch to restore a rule that Capswitch left behind after a crash.
If the app is forcibly terminated, Caps may remain mapped to F19 until the app is relaunched and Quit normally, or until reboot. To inspect mappings:
~~~bash
hidutil property --get UserKeyMapping
~~~
**Do not clear all UserKeyMapping rules if you use other remappers.** Only if the sole entry is the one Capswitch inserted may you reset mappings manually:
~~~bash
hidutil property --set '{"UserKeyMapping":[]}'
~~~
hidutil remapping normally resets after restart or after the last keyboard service disappears.
## Development
~~~bash
task lint
task test
task build
task bundle
task clean
task verify
task doctor
~~~
Linux RSB runs host-independent static checks only. SwiftPM builds, XCTest, IOKit and actual keyboard input behavior must be verified on macOS with the Apple SDK.