macoc/README.md
root 009c7e5a33
Some checks failed
macoc macOS / build (push) Has been cancelled
feat: brand macoc add icon stable restart scripts and Forgejo CI
2026-10-26 00:22:35 +03:00

128 lines
5.3 KiB
Markdown

# macoc
Native macOS menu-bar layout switcher for macOS Sonoma (14+) without
the stock Caps Lock delay. Plain **Caps** switches to the next configured
input source on **release**, including during fast typing. **Option+Caps**
in either order toggles the real system Caps Lock state.
The icon is a SFW cartoon of someone thoroughly fed up with keyboard
layout switching, complete with a literally hot seat.
## Architecture
Caps Lock is remapped to F19 via in-process Apple IOKit APIs, then suppressed
by a CoreGraphics event tap. Layouts switch through Carbon TIS. No drivers,
root daemons, third-party input libraries or hidutil permission required.
Apple technical note: https://developer.apple.com/library/archive/technotes/tn2450/
## Build and install
Requires Apple's Command Line Tools (not the Xcode IDE). Task is optional.
Quit any running **macoc** or legacy **Capswitch** before rebuilding.
From this repository:
git pull origin main
bash scripts/test.sh
bash scripts/install.sh
With Task installed:
task test
task install
Installation creates a signed macoc.app with the .icns icon in the stable
user-owned location ~/Applications/macoc.app, then starts it through
LaunchServices. No sudo is needed.
For development without installing: bash scripts/bundle.sh or task bundle.
The menu offers **Restart macoc**, **Install to ~/Applications…** and
**Launch at Login**, registered via Apple's SMAppService.mainApp. If macOS
requests approval, open System Settings -> General -> Login Items.
### Migrating from Capswitch
The name and executable are now macoc; the existing bundle identifier
remains dev.local.Capswitch to preserve old Accessibility grants and HID
ownership data where macOS permits. Do not run the old Capswitch and macoc
simultaneously. If the old ~/Applications/Capswitch.app is installed, quit
it and disable its Login Item before installing macoc. Then you may delete
the old app bundle.
## Code signing and repeat Accessibility prompts
Restart now uses LaunchServices (open -n -a) instead of executing a raw
binary under Contents/MacOS. That preserves the macOS .app attribution.
For identical app binaries at the same path, Accessibility approval normally
persists through restarts. But ad-hoc signing does **not** provide a stable
designated requirement across code changes: each rebuild may require
approval again.
For stable identity across **rebuilt versions**, create one persistent
local Code Signing certificate in Keychain Access named
**macoc Local Signing** (Certificate Assistant -> Create a Certificate ->
Self Signed Root -> Code Signing). Run bash scripts/setup-signing.sh or
task signing-setup for the instructions. The build auto-detects that identity,
or accepts MACOC_CODESIGN_IDENTITY for an existing signing certificate.
It falls back to ad-hoc signing with an explicit warning otherwise.
Moving to the installed path or changing the signer can still require
**one new approval**. Keep the signing identity and app path stable.
Reference: https://developer.apple.com/documentation/technotes/tn3127-inside-code-signing-requirements
macoc cannot silently grant itself TCC permissions. macOS still asks the
user to allow Accessibility (and optionally Input Monitoring) for the app,
not for a system binary.
Disable macOS's own "Use Caps Lock to switch to/from ABC" under
System Settings -> Keyboard -> Text Input -> Edit. Keep physical Caps
assigned its normal modifier role rather than No Action.
## Taskfile commands and shell equivalents
| Taskfile | No Task required |
| --- | --- |
| task lint | bash scripts/lint.sh |
| task test | bash scripts/test.sh |
| task verify | bash scripts/verify.sh |
| task build | bash scripts/build.sh |
| task icon | bash scripts/icon.sh |
| task sign | bash scripts/sign.sh |
| task bundle | bash scripts/bundle.sh |
| task install | bash scripts/install.sh |
| task doctor | bash scripts/doctor.sh |
| task clean | bash scripts/clean.sh |
| task package | bash scripts/package.sh |
| task publish | bash scripts/publish-release.sh |
| task signing-setup | bash scripts/setup-signing.sh |
## Forgejo CI and releases
The workflow at .forgejo/workflows/macos.yml builds and tests on pushes to
main. A pushed tag such as v0.3.0 also creates a Forgejo release with a
macOS .app ZIP and SHA-256 checksum, through the built-in scoped token.
**A real Mac Forgejo runner labelled macos is required.** The Linux server
cannot build AppKit or IOKit. See [CI setup](docs/ci.md). Until a Mac runner
is registered and its job succeeds, no release build has been produced.
A release archive signed ad-hoc is intended for local evaluation.
To distribute broadly without Gatekeeper warnings, use Developer ID
signing and Apple's notarization service (not yet configured here).
## Diagnostics and recovery
The status-bar menu shows layout, keyboard HID mapping count, event-tap state,
permission grants, and F19 down/up counts (no typed characters are recorded).
Run bash scripts/doctor.sh for read-only native HID diagnostics.
Pause or Quit normally restores Caps and preserves unrelated remappings.
If the process is killed during a remap, restart and Quit normally; macOS
usually resets mappings when a keyboard service disappears or after reboot.
The Linux development VM verifies shell syntax and static source checks,
not macOS compilation or live TCC behavior.