Skip to content

Latest commit

ย 

History

History
593 lines (419 loc) ยท 16.5 KB

File metadata and controls

593 lines (419 loc) ยท 16.5 KB

macime

A Blazingly fast IME switching tool for macOS,
built with Swift and powered by launchd,
delivering near-native IME switching speed.

๐Ÿ“– Story

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.

โšก Why itโ€™s fast

  1. Sets and gets the IME in a single operation
  2. Works as a daemon or launchd(brew service)
  3. The daemon handles IME switching internally
  4. Written in native Swift
  5. Optimized code

โœจ๏ธ Features

  • 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
  • Compatibility:
    • Fallback to im-select style command usage
  • Others:
    • Stable switching for CJK input methods (Experimental)

โš ๏ธ Breaking Changes

  • v4.4.2:
    • Remove legacy code for checking macime executable path. (macimed works without macime from v4.x.)
  • v4.4.0:
    • Add pre-compiled binaries (macime, macimed) - no xcode or build required.
  • v4.0.0:
    • Switching speed is extremelly accelarated via internal IME.swift calling.
  • v3.6.0:
    • Deprecate --status --sock-path --macime-path options from macimed (They don't reflect environmental variable)
    • Deprecate --launchd option from macime (Doesn't work in some cases)
    • Allow macimed socket command to handle both ime and daemon methods (e.g. ime set com.apple..., daemon get sock-path)
    • Upgrade macOS version (10.13 -> 10.15)
  • v3.5.0: Add CJK refreshing (Experimental)
  • v3.4.0: Revive $MACIME_TEMP_DIR env and Add $MACIME_SOCK_PATH
  • v3.3.3: Deprecate --json option (Use --detail option instead)
  • v3.0.0: Deprecate $MACIME_TEMP_DIR environmental value

๐Ÿ“ฆ Requirements

  • macOS (>=10.15)
  • com.apple.keylayout.ABC is installed and enabled (macOS's default. Used in load sub-command)

๐Ÿ›  Install

๐Ÿบ Homebrew

brew tap riodelphino/tap
brew install macime

โš™๏ธ Manual

Select 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 ~/bin

for 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 ~/bin

To 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.2 to your desired version tag.
  • Replace ~/bin and /tmp/riodelphino.macime to your desired directory.
  • Relpace /tmp/riodelphino.macimed.sock to your desired path.
  • Execute permission has already been granted.

๐Ÿ—‘๏ธ Uninstall

๐Ÿบ Homebrew

brew uninstall macime
brew untap riodelphino/tap

โš™๏ธ Manual

  • Remove installed binaries.
  • Remove the environmental variables.

โฌ†๏ธ Upgrade

๐Ÿบ Homebrew

brew update
brew upgrade macime

If launchd service is enabled, ensure to restart it:

brew services restart macime

โš™๏ธ Manual

Just overwrite the binaries.

๐Ÿ“˜ Usage

โšก macime

Show macime version:

macime --version
macime -v

Show macime help:

macime --help
macime -h

Sub 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.

โš™๏ธ Fallback behavior (im-select compatible)

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

# 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

# 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 IME

# 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

# 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.

๐Ÿ“‹ List IME

# Show IME ID list
macime list

# Show IME detailed list as JSON
macime list --detail

# Show only selectable IME
macime list --select-capable

Note

--detail and --select-capable can be mixtured

โš™๏ธ Options

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

โš™๏ธ CJK refreshing

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.05

โšก macimed

macimed 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 --debug

Show the macimed version:

macimed --version
macimed -v

Show the macimed help:

macimed --help
macimed -h

Set log level:

macimed --log-level info # Use: debug|info|warn|error (Default: info)
macimed -l info

โš™๏ธ Send Commands

macimed 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.

Socket test

A test via bash or zsh

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 Japanese

Or test socket interactively:

nc -U /tmp/riodelphino.macimed.sock
A test via macime.nvim

To 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)

๐Ÿ”Œ Sock path

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/

๐Ÿ“ Temp dir

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 manually macimed)
  • /private/tmp/riodelphino.macime/ (When running macimed via brew 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.

โš ๏ธ macime path

These legacy code have been deprecated in v4.4.2:

  • MACIME_PATH environmental variable
  • Checking macime executable

Since macimed does not require macime executable outside anymore in v4.x. They are sharing IME switching class/functions internally.

โ–ถ๏ธ Run daemon

๐Ÿบ Homebrew

macimed can be managed by launchd via Homebrew.

# Start `macimed` service
brew services start macime

# Stop `macimed` service
brew services stop macime

Note

Although the service name is macime, it runs macimed internally.

๐Ÿ“ Manual

Or, you can also start macimed manually:

macimed

Useful for debuging. You can observe the behaviour immediately.

(Almost same performance with brew services.)

โ›๏ธ Technical Information

๐Ÿบ Homebrew

๐Ÿ“ plist path

plist path:

  • ~/Library/LaunchAgents/homebrew.mxcl.macime.plist

To check the plist paths, run :checkhealth macime in neovim. (Requires macime.nvim)

๐Ÿ“ Logs

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, find StandardErrorPath item.
  • or Run :checkhealth macime in neovim. (Requires macime.nvim)

๐Ÿ”— Integration

Neovim

(Recommended) Install wrapper plugin: riodelphino/macime.nvim

It enables macime, macimed and Homebrew service without extra codings.

โš ๏ธ Issues

โš ๏ธ Unstable Switching

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 macimed or CJK IME. It probably reduces the CJK switching error rate.

See further information: Unstable CJK Switching #2

โš ๏ธ azookey prevents macime to change IME

azookey | azookey-Desktop prevents macime set command to work. I guess it's because they are still alpha version.

Solutions for now:

  • Uninstal azookey

๐Ÿ“ TODO

โšก๏ธ macimed

  • Stable IME switching with CJK
  • Make the server restartable (for daemon set sock-path xxx)

๐Ÿค Contribution

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

๐Ÿ™ Thanks To

๐Ÿค– AI Assistance

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)

๐Ÿ“œ Changelog

See CHANGELOG

๐Ÿ“„ License

MIT License. See LICENSE

๐Ÿ”— Related