A Blazingly fast IME switching tool for macOS,
built with Swift and powered by launchd,
delivering near-native IME switching speed.
I've used macism and im-select before.
But on my older Macs, these tools always required a short wait to switch IME modes. It was an unacceptable delay for daily use. (approximately 400ms)
This tool reduces the delay by about 95% when running as a daemon or launchd โ down to 5โ20 ms (around 20ร faster than other tools), delivering near-native IME switching.
(I was already very satisfied with the switching speed since v3.x, but v4.x broke through that limit.)
If youโre a Mac user frustrated by slow IME switching, give it a try.
- Sets and gets the IME in a single operation
- Works as a daemon or launchd(brew service)
- The daemon handles IME switching internally
- Written in native Swift
- Optimized code
- Standalone (
macime):- Get, Set, Save current, Load previous, List all IMEs
- Switch IME while saving the previous one (in single step)
- Output detailed get|list results as JSON
- Daemon/launchd (
macimed):- Blazingly fast switching via daemon or
brew services
- Blazingly fast switching via daemon or
- Compatibility:
- Fallback to
im-selectstyle command usage
- Fallback to
- Others:
- Stable switching for CJK input methods (Experimental)
- v4.4.2:
- Remove legacy code for checking
macimeexecutable path. (macimedworks withoutmacimefromv4.x.)
- Remove legacy code for checking
- v4.4.0:
- Add pre-compiled binaries (
macime,macimed) - noxcodeor build required.
- Add pre-compiled binaries (
- v4.0.0:
- Switching speed is extremelly accelarated via internal
IME.swiftcalling.
- Switching speed is extremelly accelarated via internal
- v3.6.0:
- Deprecate
--status--sock-path--macime-pathoptions frommacimed(They don't reflect environmental variable) - Deprecate
--launchdoption frommacime(Doesn't work in some cases) - Allow
macimedsocket command to handle bothimeanddaemonmethods (e.g.ime set com.apple...,daemon get sock-path) - Upgrade macOS version (10.13 -> 10.15)
- Deprecate
- v3.5.0: Add CJK refreshing (Experimental)
- v3.4.0: Revive
$MACIME_TEMP_DIRenv and Add$MACIME_SOCK_PATH - v3.3.3: Deprecate
--jsonoption (Use--detailoption instead) - v3.0.0: Deprecate
$MACIME_TEMP_DIRenvironmental value
- macOS (>=10.15)
com.apple.keylayout.ABCis installed and enabled (macOS's default. Used inloadsub-command)
brew tap riodelphino/tap
brew install macimeSelect specific version and CPU architecture from: https://github.com/riodelphino/macime/releases
for Intel Mac:
curl -L -O https://github.com/riodelphino/macime/releases/download/v4.4.2/macime-v4.4.2-x86_64.tar.gz
tar -zxvf macime-v4.4.2-x86_64.tar.gz
mv macime macimed ~/binfor Apple Silicon:
curl -L -O https://github.com/riodelphino/macime/releases/download/v4.4.2/macime-v4.4.2-arm64.tar.gz
tar -zxvf macime-v4.4.2-arm64.tar.gz
mv macime macimed ~/binTo change default sock-path and temp-dir, set environmental variable to your ~/.zshrc or ~/.bashrc or ~/.profile:
export MACIME_SOCK_PATH="/tmp/riodelphino.macimed.sock"
export MACIME_TEMP_DIR="/tmp/riodelphino.macime"- Replace
v4.4.2to your desired version tag. - Replace
~/binand/tmp/riodelphino.macimeto your desired directory. - Relpace
/tmp/riodelphino.macimed.sockto your desired path. - Execute permission has already been granted.
brew uninstall macime
brew untap riodelphino/tap- Remove installed binaries.
- Remove the environmental variables.
brew update
brew upgrade macimeIf launchd service is enabled, ensure to restart it:
brew services restart macimeJust overwrite the binaries.
Show macime version:
macime --version
macime -vShow macime help:
macime --help
macime -hSub commands:
macime get [options]
macime set <IME_ID> [options]
macime list [options]
macime save [options]
macime load [options]get
Show the current IME.
set
Switch to the specified IME.
Optionally saves the previous IME.
list
List available IMEs.
save
Save current IME.
load
Restore the previous IME.
macime supports im-selectโstyle shortcuts.
# Falls back to `macime get`
macime
# Falls back to `macime set`
macime com.apple.keylayout.ABC# Get current IME ID
macime get
# com.apple.keylayout.ABC
# Get current IME detailed info as JSON
macime get --detail
# {
# "isSelectCapable" : true,
# "isSelected" : true,
# "localizedName" : "ABC",
# "id" : "com.apple.keylayout.ABC",
# "sourceLanguages" : [
# "en",
# ...
# "zu"
# ]
# }# Set IME
macime set com.apple.keylayout.ABC
# Set IME while saving current IME as `GLOBAL`
macime set com.apple.keylayout.ABC --save
# The IME ID is saved at `/tmp/riodelphino.macime/prev/GLOBAL`
# Set IME while saving current IME as <session_id>
macime set com.apple.keylayout.ABC --save --session-id nvim-1001
# The IME ID is saved at `/tmp/riodelphino.macime/prev/nvim-1001`--cjk-refresh option is also available.
Note
Using set with --save reduces execution time by 50%, compared to running get and set separately.
Other tools require two excutions. (e.g. im-select -> im-select set com.apple.keylayout.ABC)
# Save current IME to `GLOBAL`
macime save
# Current IME ID is set to `/tmp/riodelphino.macime/prev/GLOBAL`
# Save current IME to `<session_id>`
macime save --session-id nvim-1001
# Current IME ID is set to `/tmp/riodelphino.macime/prev/nvim-1001`# Load IME from `GLOBAL`
macime load
# Reads previous IME ID from `/tmp/riodelphino.macime/prev/GLOBAL`, then set it.
# Load IME from `<session_id>`
macime load --session-id nvim-1001
# Reads previous IME ID from `/tmp/riodelphino.macime/prev/nvim-1001`, then set it.--cjk-refresh option is also available.
# Show IME ID list
macime list
# Show IME detailed list as JSON
macime list --detail
# Show only selectable IME
macime list --select-capableNote
--detail and --select-capable can be mixtured
| Option | Available for | Description |
|---|---|---|
| --detail | get, list | Show detailed IME info as JSON |
| --select-capable | get, list | Show only selectable IME |
| --save | set | Save current IME (with macime set only) |
| --session-id | save, load | Specify the save / load session id (= filename in temp dir) |
| --cjk-refresh | set, load | Refresh IME for CJK input methods (Experimental) |
| --cjk-delay | set, load | Set CJK refreshing delay time as a number between 0 and 1 (Default: 0.05) (Experimental) |
| --debug | (all) | Show debug information |
Warning
Experimental. Please test in your environment, then report any issues or submit a PR.
Reduces the failure rate of CJK IME switching, by creating a hidden window and forcibly refreshing the IME.
It still fails sometimes (Not perfect).
e.g.
Googleๆฅๆฌ่ชๅ ฅๅ: Google Japanese Input็พๅบฆๆผ้ณ: Baidu Pinyinๆ็ๆผ้ณ: Sogou Pinyin
via --cjk-refresh and --cjk-delay option:
# Set
macime set com.baidu.inputmethod.BaiduPinyin --cjk-refresh --cjk-delay 0.05
macime set com.sogou.inputmethod.sogou --cjk-refresh --cjk-delay 0.05
# load
macime load --cjk-refresh --cjk-delay 0.05macimed is a daemon bundled with macime. It enables blazing faster IME switching.
It runs in the background and controls macime by receiving commands over a Unix domain socket.
Run macimed manually (for debugging):
# Show err/log
macimed
# Show more detailed err/log
macimed --debugShow the macimed version:
macimed --version
macimed -vShow the macimed help:
macimed --help
macimed -hSet log level:
macimed --log-level info # Use: debug|info|warn|error (Default: info)
macimed -l infomacimed recieves commands via socket as plain text.
Commands compliant to macime:
ime set com.apple.keylayout.ABC
ime set com.apple.keylayout.ABC --save
ime set com.apple.keylayout.ABC --save --session-id <session-id>
ime load
ime load --session-id <session-id>
# `--cjk-refresh` is also available for `set` and `load`When the socket recieves a command like above, macimed executes macime command with these args immediately.
Commands for macimed:
# Get all informations of `macimed`
daemon info
# Get
daemon get sock-path # Get sock path
daemon get macime-path # Get macime path
# Set
daemon set log-level info # Set log level. Use one of debug|info|warn|error (Default: info)
Note
Currently daemon set is disabled since it requires restarting server and much more modifications.
To test socket via bash or zsh command:
echo "ime get" | nc -U /tmp/riodelphino.macimed.sock
# com.google.inputmethod.Japanese.base
echo "ime set com.google.inputmethod.Japanese.base" | nc -U /tmp/riodelphino.macimed.sock
# IME is changed to Google JapaneseOr test socket interactively:
nc -U /tmp/riodelphino.macimed.sockTo test socket via macime.nvim (Ensure macimed is running):
(e.g.)
require("macime").send("ime set com.apple.keylayout.ABC")
require("macime").send("ime get", function(ok, data) if ok then print(data) end end)
require("macime").send("daemon info", function(ok, data) if ok then print(data) end end)
require("macime").send("daemon get sock-path", function(ok, data) if ok then print(data) end end)
require("macime").send("daemon set log-level debug", function(ok, data) if ok then print(data) end end)macimed listen to the socket for receiving/sending daemon commands.
The sock path is determined from one of the following paths:
MACIME_SOCK_PATH(Environment variable)/tmp/riodelphino.macimed.sock/
macimed stores previous IME IDs in temp dir.
The temp dir is determined from one of the following paths:
MACIME_TEMP_DIR(Environment variable)/tmp/riodelphino.macime/(When running manuallymacimed)/private/tmp/riodelphino.macime/(When running macimed viabrew services)
The previous IDs are stored in following files:
<temp_dir>/GLOBAL<temp_dir>/<session_id>(with--session-id <session_id>option)
These files are deleted automatically when you shutdown macOS.
These legacy code have been deprecated in v4.4.2:
MACIME_PATHenvironmental variable- Checking
macimeexecutable
Since macimed does not require macime executable outside anymore in v4.x. They are sharing IME switching class/functions internally.
macimed can be managed by launchd via Homebrew.
# Start `macimed` service
brew services start macime
# Stop `macimed` service
brew services stop macimeNote
Although the service name is macime, it runs macimed internally.
Or, you can also start macimed manually:
macimedUseful for debuging. You can observe the behaviour immediately.
(Almost same performance with brew services.)
plist path:
- ~/Library/LaunchAgents/homebrew.mxcl.macime.plist
To check the plist paths, run :checkhealth macime in neovim. (Requires macime.nvim)
macimed leaves stdout and stderr logs when it is running via Homebrew service.
With Apple Intel:
- /usr/local/var/log/riodelphino/macimed.out.log
- /usr/local/var/log/riodelphino/macimed.err.log
With Apple Silicon:
- /opt/homebrew/var/log/riodelphino/macimed.out.log
- /opt/homebrew/var/log/riodelphino.macimed.err.log
To check the log path:
- Run
cat ~/Library/LaunchAgents/homebrew.mxcl.macime.plist, findStandardErrorPathitem. - or Run
:checkhealth macimein neovim. (Requires macime.nvim)
(Recommended) Install wrapper plugin: riodelphino/macime.nvim
It enables macime, macimed and Homebrew service without extra codings.
Via macime.nvim, the IME switching is unstable in following cases:
- When have not run IME switching for a while.
Solution for now:
- Switch IME several times, and might warm up
macimedor CJK IME. It probably reduces the CJK switching error rate.
See further information: Unstable CJK Switching #2
azookey | azookey-Desktop prevents macime set command to work.
I guess it's because they are still alpha version.
Solutions for now:
- Uninstal
azookey
- Stable IME switching with CJK
- Make the server restartable (for
daemon set sock-path xxx)
Contributions are welcome:
# Clone
git clone https://github.com/riodelphino/macime
cd macime
# Build for debug
swift build
# Build for release
swift build -c release- The IME switching concept is inspired by im-select and macism
- The CJK IME workaround comes from ims-mac
- The idea that Swift can manipulate IME states was inspired by Neovim IMEใฎ็ถๆ ใใซใผใฝใซใฎ่ฒใซๅๆ ใใใ (Japanese)
This project is developed with the assistance of AI - though not vibe-coded.
The AI helped significantly with:
- Git workflow
- Daemon process (
IMED.swift) - CJK handling (
CJK.swift)
See CHANGELOG
MIT License. See LICENSE
