Mac 教程:第三方輸入法下如何將大寫鎖定鍵改為輸入法切換鍵

大寫鎖定鍵是我的鍵盤上用的最少的鍵之一。一是因?yàn)槲业逆I盤上還有一個(gè)關(guān)機(jī)鍵使用頻率和它有的一拼,二是由于其地理位置優(yōu)越經(jīng)常會(huì)被誤按。實(shí)際上,在 Chromebook 上,大寫鎖定鍵就被 Google 換成了更為常用的“搜索鍵”;另外,也有 vimer 把大寫鎖定鍵用作ESC鍵,效果拔群。

根據(jù)個(gè)人習(xí)慣,我最終決定將大寫鎖定鍵更改為輸入法切換鍵,一是因?yàn)樽鳛橐粋€(gè)中國(guó)人輸入法切換是使用最多的一個(gè)快捷鍵之一;另一個(gè)原因是因?yàn)槿绱艘粊礞I上的指示燈還可作為輸入法指示燈,簡(jiǎn)直完美。

更改鍵綁定

  • 打開系統(tǒng)偏好設(shè)置-鍵盤-鍵盤-修飾鍵,將 Caps Lock 鍵設(shè)為“無操作”
  • 下載?Seil(良心軟件,良心作者),打開后將 Caps Lock 鍵映射為 Key Code 80(或者其他一個(gè)不存在的鍵,80代表F19
  • 打開系統(tǒng)偏好設(shè)置-鍵盤-快捷鍵-輸入源,將切換輸入法的快捷鍵設(shè)置為F19(通過選擇后按一下Caps Lock)

Done! 但是現(xiàn)在問題來了:大寫鎖定的燈永遠(yuǎn)不亮,這不優(yōu)美!我們希望指示燈也更改為能夠指示輸入法狀態(tài),即在英文狀態(tài)下不亮,在拼音/五筆等輸入法狀態(tài)下亮。經(jīng)過 Google 發(fā)現(xiàn),OS X 提供了控制鍵盤燈的底層 API,可以手動(dòng)控制其狀態(tài),詳見?MacLight。這就好辦了,于是我依次嘗試了以下幾種解決方案:

  • 寫一段 Shell 腳本來切換輸入法(通過 AppleScript 模擬 Keystroke)+切換指示燈狀態(tài),通過 Automator 新建一個(gè)“服務(wù)”然后將大寫鎖定鍵綁定為運(yùn)行該腳本。但是經(jīng)過測(cè)試發(fā)現(xiàn)延時(shí)太大(~200ms),放棄。
  • 用 Objective-C 寫一個(gè)調(diào)用底層 API 的程序來切換輸入法(通過TISSelectInputSource系列API)+切換指示燈狀態(tài),發(fā)現(xiàn)調(diào)用 API 切換輸入法后需要切換到下一個(gè)輸入窗口才會(huì)生效,并且延時(shí)依然很大,放棄。
  • 用 Objective-C 寫一個(gè)后臺(tái)應(yīng)用,通過NSDistributedNotificationCenter接收輸入法變更事件,根據(jù)狀態(tài)改變指示燈。科學(xué)!

最終采用了最后這種科學(xué)的方法。當(dāng)然,這個(gè)后臺(tái)應(yīng)用只需要是命令行應(yīng)用就可以了,通過launchctl等方式開機(jī)自動(dòng)啟動(dòng)即可。不過由于強(qiáng)迫癥什么的(方便啟動(dòng)、退出,方便加為登錄啟動(dòng)項(xiàng))還是寫成了占領(lǐng)在狀態(tài)欄的應(yīng)用,并取名為IMLight,如下圖:

下載鏈接 /?GitHub

Update for macOS Sierra

升級(jí) macOS Sierra 后,Seil 無法正常使用了(IMLight 不影響),詳見 Github 上的這個(gè)?issue,并且由于是系統(tǒng)接口的大改動(dòng),一時(shí)半會(huì)兒可能不會(huì)有修復(fù)更新。

Issue 中也有人提到,可以使用作者正在開發(fā)的另一個(gè)針對(duì) Sierra 的項(xiàng)目?Karabiner-Elements,但是這個(gè)項(xiàng)目對(duì)我來說有幾個(gè)問題:

  • 與 IMLight 沖突(雖然不一定是他的問題,但是我暫時(shí)也不知道怎么修復(fù)…)
  • 會(huì)使得系統(tǒng)偏好設(shè)置中的針對(duì)多個(gè)鍵盤的修飾鍵設(shè)置失效(比如無法把外接鍵盤的 alt 和 ?? 互換),作者表示無法修復(fù)

另外的解決方案是在系統(tǒng)偏好設(shè)置中把 Caps Lock 設(shè)置為 Ctrl(或者其他),然后用其他軟件重映射,比如 Keyboard Maestro(更改 Caps Lock 這件事情比較底層,需要內(nèi)核級(jí)別的修改,而監(jiān)聽 Ctrl 等鍵這件事情就很簡(jiǎn)單了)。我使用免費(fèi)的?hammerspoon?來實(shí)現(xiàn):

local M = {}

local events = hs.eventtap.event.types
M.log = hs.logger.new('caps_remap', 'info')

M.last_flags_1 = {}
M.last_flags_0 = {}
M.last_time_1 = 0
M.last_time_0 = 0

M.timeout = 0.15
M.key = "ctrl"
M.action = function() hs.eventtap.keyStroke({}, "f19") end

local function _dict_has_no_other_key(dic)
for k,v in pairs(dic) do
if k ~= M.key then
return false
end
end
return true
end

function M.event_callback(e)
local typ = e:getType()
local code = e:getKeyCode()
local flags = e:getFlags()
local now = hs.timer.secondsSinceEpoch()

if _dict_has_no_other_key(flags) and not flags[M.key]
and _dict_has_no_other_key(M.last_flags_0) and M.last_flags_0[M.key]
and _dict_has_no_other_key(M.last_flags_1) and not M.last_flags_1[M.key]
and now - M.last_time_0 < M.timeout
then
M.log.i("Fire caps action")
if M.action then
M.action()
end
end

M.last_flags_1 = M.last_flags_0
M.last_flags_0 = flags

M.last_time_1 = M.last_time_0
M.last_time_0 = now

return false
end


function M.init(options)
if options.key then
M.key = options.key
end
if options.timeout then
M.timeout = options.timeout
end
if options.action then
M.action = options.action
end
M.watcher = hs.eventtap.new({events.flagsChanged}, M.event_callback)
M.watcher:start()
end

return M

即快速按一下 ctrl(即 Caps Lock)會(huì)觸發(fā) F19,而其他包含 ctrl 的組合鍵并不會(huì),可以滿足要求。


原文發(fā)表自 《Mac:使用大寫鎖定鍵切換輸入法》,內(nèi)容版權(quán)及解釋權(quán)歸 Mac玩兒法內(nèi)容合作伙伴?BlahGeek 所有。

評(píng)論 7 條
  • Macie

    如何將系統(tǒng)偏好設(shè)置中的選擇輸入法快捷鍵改成control呢,我按下control沒反應(yīng)

    2016-11-14 00:30 回復(fù)

  • 這真的是吃飽了撐的沒事兒干啊

    2016-10-25 00:39 回復(fù)

  • Frank

    如果使用第三方輸入法就不行了

    2016-10-19 16:55 回復(fù)

  • gg

    升級(jí) macOS Sierra 后鍵盤設(shè)置里直接就有 caps lock 切換輸入法的選項(xiàng)了,不用這么折騰。

    2016-10-19 12:52 回復(fù)

    • 伊一

      有嗎?我怎么沒發(fā)現(xiàn)呢。

      2016-10-19 13:00 回復(fù)

      • gg

        系統(tǒng)偏好設(shè)置 - 鍵盤 - 輸入法 - 使用大寫鎖定鍵切換“ABC”輸入模式

        2016-10-19 13:49 回復(fù)

    • mimir

      在sierra之前也可以……一直這么用的,就是都沒有亮燈提示罷了

      2016-10-19 15:34 回復(fù)