Skip to content

⚡️ 进阶:输入法自动切换 (Hammerspoon)

💡 痛点解决

你是否遇到过:鼠标移到 Windows 后,打字却没反应(因为 Mac 还是中文输入法)? 本方案利用 Hammerspoon 脚本,实现去 Win 自动切英文,回 Mac 自动恢复的完美体验。

1. 原理 (The Magic)

DeskFlow 在控制 Windows 时,会将 Mac 鼠标光标瞬移并锁定在屏幕中心。 我们利用这一特征编写了“瞬移检测脚本”:

  • 检测去程:当鼠标在 0.2秒内从边缘瞬移到中心 -> 判定为去 Win -> 强制切 ABC
  • 检测回程:当鼠标离开中心区域 -> 判定为回 Mac -> 恢复之前的输入法

2. 安装与配置

  1. 安装 Hammerspoon

    bash
    brew install --cask hammerspoon

    (或者去 官网下载)

  2. 配置脚本

    • 运行 Hammerspoon,点击菜单栏图标 -> Open Config
    • 将下方代码粘贴到 init.lua 文件中。
    • 修改 winPosition 变量(你的 Win 在左边还是右边)。
  3. 生效

    • 点击菜单栏图标 -> Reload Config

3. 脚本代码

点击复制 Lua 脚本代码
lua
-- ============================================
-- 🌉 DeskFlow 输入法智能记忆与切换脚本 
-- 功能:去 Win 自动切 ABC,回 Mac 自动恢复之前的状态
-- ============================================

-- ⚙️ 配置区域
local winPosition = "right" -- Windows 在哪边?
local abcID = "com.apple.keylayout.ABC" 

-- 📏 参数微调
local edgeZone = 10        -- 边缘判定宽度
local centerZone = 500     -- 中心判定半径 (在这个圈内都算中心)
local jumpTimeLimit = 0.2  -- 瞬移时间限制 (0.2秒内从边缘到中心才算瞬移)

-- 🔒 状态变量
local savedInputMethod = nil
local isFocusOnWin = false
local lastAtEdgeTime = 0   -- 上次出现在边缘的时间戳

-- 缓存屏幕数据
local screen = hs.screen.mainScreen()
local frame = screen:frame()
local centerX = frame.x + (frame.w / 2)
local centerY = frame.y + (frame.h / 2)

function mouseHandler()
    local pos = hs.mouse.getAbsolutePosition()
    local now = hs.timer.secondsSinceEpoch()
    
    -- 1. 检测是否在边缘
    local atEdge = false
    if winPosition == "right" then
        if pos.x >= (frame.x + frame.w - edgeZone) then atEdge = true end
    else
        if pos.x <= (frame.x + edgeZone) then atEdge = true end
    end
    
    -- 如果在边缘,更新“目击时间”
    if atEdge then
        lastAtEdgeTime = now
    end

    -- 2. 检测是否在中心
    local distToCenter = math.sqrt((pos.x - centerX)^2 + (pos.y - centerY)^2)
    local atCenter = (distToCenter < centerZone)

    -- 🚀 [去程判定]:刚才在边缘,现在突然在中心 (瞬移)
    if not isFocusOnWin and atCenter then
        -- 核心逻辑:如果从边缘到中心的时间极短,说明是 DeskFlow 干的
        if (now - lastAtEdgeTime) < jumpTimeLimit then
            -- 记录并切换
            local current = hs.keycodes.currentSourceID()
            savedInputMethod = current
            
            if current ~= abcID then
                hs.keycodes.currentSourceID(abcID)
                hs.alert.show("🚀 Win Mode", 0.5)
            end
            
            isFocusOnWin = true
        end
    end

    -- 🏠 [回程判定]:虽然在 Win 模式,但鼠标逃离了中心
    -- 逻辑:DeskFlow 锁定时会一直把鼠标按在中心,如果你能把鼠标移开,说明你回 Mac 了
    if isFocusOnWin and not atCenter then
        if savedInputMethod and savedInputMethod ~= abcID then
            hs.keycodes.currentSourceID(savedInputMethod)
            hs.alert.show("🏠 Mac Mode", 0.5)
        end
        isFocusOnWin = false
        savedInputMethod = nil
    end
end

-- 高频监听 (0.05秒一次,为了捕捉瞬移)
mouseWatcher = hs.timer.doEvery(0.05, mouseHandler)

⚠️ 关于“极速移动”的提示

由于脚本采用 0.05秒 轮询机制,如果鼠标划过屏幕边缘的速度极快(瞬间甩过去),脚本可能无法捕捉到“边缘状态”,导致切换失效。

如果你发现切换不灵敏,请尝试修改脚本中的以下参数:

  1. 增大捕捉范围(推荐): 找到 local edgeZone = 10,将其修改为更大的数值(例如 50100)。这相当于把“球门”变大,更容易接住鼠标。
  2. 提高检测频率: 找到底部的 hs.timer.doEvery(0.05, ...),将 0.05 修改为 0.020.01。这会略微增加 CPU 消耗,但捕捉更精准。

➡️ 下一步

解决了输入问题后,我们来打通数据传输通道。

请前往下一节:📂 极速文件传输 (File Tunnel)

Released under the MIT License.