背景
http://qiita.com/naoya@github/items/56a34be85710f4ed5531 でも書いたとおり Karabiner が使えない現状、修飾キーつきのホットキーのカスタマイズをどうするかという問題がある。記事を書いた時点では macOS の DefaultKeybidings.dict でショートカットを定義していたが、Hammerspoon を使うことでやりたいことが実現できたので、こちらの方法に移行した。
課題に感じてたこと
こちらのコメントに書いたとおり、DefaultKeyBiding.dict では特定のキーイベントを別のキーイベントに変換するという指定はできない。おかげで Karabiner のときにカスタマイズしていた挙動を再現できなかった。
それ Hammerspoon でできるよ
Hammerspoon は macOS の挙動を自動化するためのツール。自動化の処理を Lua で書けるフレームワークみたいなもの。キーリマップだけが目的のソフトウエアではないが、それにも使える代物。
Hammerspoon で、やっと Karabiner と同じ挙動を再現できた。keyhac のときのように iTerm2 のキーリマップ機能とコンフリクトを起こしたりといった不具合も今のところ起きていない。
init.lua
試行錯誤の末、~/.hammerspoon/init.lua
を以下のように書いた。
local function keyCode(key, modifiers)
modifiers = modifiers or {}
return function()
hs.eventtap.event.newKeyEvent(modifiers, string.lower(key), true):post()
hs.timer.usleep(1000)
hs.eventtap.event.newKeyEvent(modifiers, string.lower(key), false):post()
end
end
local function remapKey(modifiers, key, keyCode)
hs.hotkey.bind(modifiers, key, keyCode, nil, keyCode)
end
local function disableAllHotkeys()
for k, v in pairs(hs.hotkey.getHotkeys()) do
v['_hk']:disable()
end
end
local function enableAllHotkeys()
for k, v in pairs(hs.hotkey.getHotkeys()) do
v['_hk']:enable()
end
end
local function handleGlobalAppEvent(name, event, app)
if event == hs.application.watcher.activated then
-- hs.alert.show(name)
if name ~= "iTerm2" then
enableAllHotkeys()
else
disableAllHotkeys()
end
end
end
appsWatcher = hs.application.watcher.new(handleGlobalAppEvent)
appsWatcher:start()
-- カーソル移動
remapKey({'ctrl'}, 'f', keyCode('right'))
remapKey({'ctrl'}, 'b', keyCode('left'))
remapKey({'ctrl'}, 'n', keyCode('down'))
remapKey({'ctrl'}, 'p', keyCode('up'))
-- テキスト編集
remapKey({'ctrl'}, 'w', keyCode('x', {'cmd'}))
remapKey({'ctrl'}, 'y', keyCode('v', {'cmd'}))
-- コマンド
remapKey({'ctrl'}, 's', keyCode('f', {'cmd'}))
remapKey({'ctrl'}, '/', keyCode('z', {'cmd'}))
remapKey({'ctrl'}, 'g', keyCode('escape'))
-- ページスクロール
remapKey({'ctrl'}, 'v', keyCode('pagedown'))
remapKey({'alt'}, 'v', keyCode('pageup'))
remapKey({'cmd', 'shift'}, ',', keyCode('home'))
remapKey({'cmd', 'shift'}, '.', keyCode('end'))
- キーリマップ
- アプリの切り替えを監視して、対象が iTerm2 だったらキーリマップを無効にする
という二点をやっている。後者を入れないと iTerm2 上でもキーリマップが作用してシェルや Emacs を使ってるときに不都合がありまくったので、こうした。
参考情報
- http://qiita.com/deg84/items/792bf6b2adf467df9bdc ・・・ 導入方法。(このエントリの init.lua だとキーリピートが効かないので注意)
- https://github.com/Hammerspoon/hammerspoon/wiki/Sample-Configurations ・・・ Hammerspoon のスクリプトの例がたくさん載ってる
- https://github.com/kkamdooong/hammerspoon-control-hjkl-to-arrow/blob/master/init.lua ・・・ キーリマップのシンプルな例 (iTerm2 の考慮とかなし)
- http://www.hammerspoon.org/docs/index.html ・・・ Hammerspoon の API ドキュメント
- http://www.hammerspoon.org/docs/hs.application.watcher.html ・・・ アプリケーションイベントをウォッチする API
- http://www.hammerspoon.org/docs/hs.hotkey.html ・・・ hotkey 周りの API
Hammerspoon のバージョン
Version 0.9.50