Mac 切换显示器输入源

由于只有 1 台显示器,经常需要切换不同的输入源,在显示器上切换很麻烦,于是开始研究是否可以实现自动切换。

由于本人只需要在 mac 下使用,故仅记录了 mac 的内容。

前提条件:

  • 显示器支持 DDC/CI 协议

命令行切换

Mac下有两个工具可以使用,详细的区别可参考这里

用法示例:

ddcctl -d 1 -i 15

m1ddc display {uuid} set input 15

快捷键切换

命令行足够轻量级,却不够方便,我们可以使用 mac 自带的 Automator 添加快捷键。步骤如下:

  1. 打开 Automator,选择「快速操作」 longshot20241128172303.png

  2. 填写切换显示器命令 longshot20241128172506.png=600x

  3. 保存 longshot20241128172544.png=600x

  4. 在系统中配置键盘快捷键 longshot20241128172625.png=600x

唤醒时自动切换输入源

唤醒时,自动切换显示器,需要用到 sleepwatcher

2026.04.07 更新: 建议使用 hammerspoon 了,事件更丰富,定制更灵活

brew install sleepwatcher
brew services start sleepwatcher

唤醒和睡眠时会分别执行 ~/.wakeup~/.sleep,新建文件如下

touch ~/.wakeup
chmod 755 ~/.wakeup

.wakeup 中写入要执行的命令即可。

这里尝试基于 m1ddc 写了脚本,供参考。ddcctl 获取当前输入源会失败,直接写入切换命令就好了。

#!/bin/bash

m1ddc() {
    /usr/local/bin/m1ddc $@
}

switch_input_source() {
    local monitor_uuid=$1
    local monitor_input=$2
    if [ $(m1ddc display list | grep -c $monitor_uuid) -eq 1 ]; then
        currentInput=$(m1ddc display $monitor_uuid get input)
        if [ $currentInput == $monitor_input ]; then
            echo "无需切换"
        else
            echo "需要切换"
            m1ddc display $monitor_uuid set input $monitor_input
        fi
    fi
}

# 指定显示器
switch_input_source "C315CEDD-8D5B-4F19-A7F0-A88CEB9CEC21" 15

遗留问题

1, 若当前输入源正确,重新执行切换会闪烁

m1ddc 可以获取当前输入源,可以写逻辑解决,ddcctl 则不行

2026.04.07 更新: 实际是因为 macOS 使用 HDMI 连接时,对 DCC 协议支持不完善导致,换成 DP 线则正常

2, 唤醒其实不是一个准确的事件,比如合盖然后开盖可能不会触发,目前没有找到轻量级的方案。

2026.04.07 更新: 可以通过 hammerspoon 实现开盖检测,参考 AI 给出的触发代码 ~/.hammerspoon/init.lua

local function triggerSwitch()
    -- hs.alert.show("✅ 硬件级判定:已开盖,执行切换!")
    -- hs.execute("/bin/bash switch-monitor.sh")
end

-- =====================================================================
-- 辅助函数:通过 ioreg 真正读取物理盖子的开合状态
-- 返回 true 表示盖子是开着的,false 表示合盖
-- =====================================================================
local function isLidOpen()
    -- 执行底层的 ioreg 命令
    local output, status, type, rc = hs.execute("ioreg -r -k AppleClamshellState -d 4 | grep AppleClamshellState")
    
    -- 如果输出里包含 "No",说明 AppleClamshellState = No (即没合盖)
    if string.find(output, "No") then
        return true
    else
        return false
    end
end

-- =====================================================================
-- 监听器:结合底层硬件状态的精确拦截
-- =====================================================================
-- 记录上一次探测到的物理状态
local wasLidOpen = isLidOpen()

local function exactScreenCallback()
    -- 屏幕状态发生任何变化时,去读一下真实的物理盖子传感器
    local currentlyOpen = isLidOpen()
    
    -- 核心逻辑:只有当“之前是合盖”且“现在是开盖”时,才触发!
    -- 这样插拔其他外接显示器绝对不会引起误触发。
    if (not wasLidOpen) and currentlyOpen then
        print("探测到物理开盖动作")
        triggerSwitch()
    end
    
    -- 更新状态记录
    wasLidOpen = currentlyOpen
end

-- screen.watcher 作为触发“钩子”,但用硬件状态做“校验”
screenWatcher = hs.screen.watcher.new(exactScreenCallback)
screenWatcher:start()

本文链接:参与评论 »

--EOF--

提醒:本文最后更新于 502 天前,文中所描述的信息可能已发生改变,请谨慎使用。

Comments