通过USB扩展坞代替KVM切换显示器输入源

通过USB扩展坞代替KVM切换显示器输入源

orzlee
2025-03-04 / 1 评论 / 47 阅读 / 正在检测是否收录...

switch-monitors-with-usb-docking-station-instead-of-kvm.png

前言

入了台新电脑,揪心事情就来了。两台显示器、一套键鼠怎么在都开机的情况共用两台主机呢?而且还有来回切换的场景。

上网查,发现有东西叫KVM切换器,原理就是两台主机显示接口接入KVM的显示输入接口,USB输入接口,然后KVM在出两根视频线给显示器。切换的时候应把A主机视频信号切断,通过硬件让显示器接入B主机输出的视频信号。线多点无所谓,切换过程耗时也能接受,继续查各网友使用反馈。得到的结论就是国内这东西无异于工业垃圾...

二进二出KVM对高帧率支持有限,EDID(在Windows下切换屏幕后系统桌面程序窗口还是之前一样,没有EDID切回就乱了,mac就算拔掉一个显示器,插回去还会恢复...)寥寥无几,更别提各种黑屏闪屏乱七八糟的问题,倒是价格三五百也还好。反正网上没有说非常满意的,最多也就是个能用。

灵机一动,我买个USB切换器,写个程序监测鼠标键盘插入拔出,通过DDC/CI直接切换显示器输入源不久OJBK了吗?买个KVM我实在都没真心满意的。两台4K显示器,LG的144HZ,另一台60HZ。要放弃144HZ选择还多点,不然没办法。而且支持EDID只有一款双HDMI的支持,其他不支持。

搞起

显示器必须支持DDC/CI,不支持都不用搞了。大厂应该都支持,小作坊就不保证了。Dell、LG两个在查资料过程还是看到不少关于这两显示器问题的。下个control_my_monitor打开看看吧。

我本来想用go来写的,结果go在winodws监听USB输入输出事件没有很简单的方法,不成熟。不过找了这么多资料总有点结果,nodejs有usb包支持usb热插拔事件。既然能支持,还管它什么语言,能解决问题就是好语言。

当USB切换器切换当前主机的键盘和鼠标USB会拔出,反之另一台会插入。基本逻辑就是插入鼠标键盘,DDC/CI切换显示器输入源为当前主机视频接口,两台主机一边一套,这样理论上支持无限台主机数量。其实只要有切换器,不一定要鼠标或者键盘,随便插个什么USB都行。

LG显示器DDC/CI切换输入源比较麻烦,可以看我上一篇文章解决LG显示器无法通过DDC/CL切换输入源,其他显示器可以看看输入源端口代码。

windows这边好弄,但是mac有点麻烦。因为macBetterDisplay只能登陆后启动,不能开机直接启动(可能有办法解决,没深入研究)。然后通过USB扩展坞扩展的视频接口是个残缺DDC/CI接,没法切换输入源,调节他亮度声音倒没什么问题。难道又买一根type-C to HDMI的线?另一台直连HDMI接口的显示没有问题。

干脆就到windows上跑nodejs好了,反正只有两台主机,插入就切windows主机,拔出切回去。

我用到了winddcutilcontrol_my_monitor也可以,无非就是换下命令)。nodejs需要安装usb包。

直接上代码:

const usb = require('usb').usb;
const { exec } = require('child_process');
const os = require('os');
// const fs = require('fs');
// const path = require('path');

const platform = os.platform();
// const LOG_FILE = path.join(__dirname, 'usb_switch_monitor.log');

const DEVICE_VENDOR_ID = 1507;
const DEVICE_PRODUCT_ID = 1552;
const WINDOWS_DIR = 'D:\\xxx\\xxx\\'; //

console.log('USB hot plug switch monitor service started...');
// console.log(usb.getDeviceList());

// 写入日志函数
// function writeLog(message) {
//     const timestamp = new Date().toISOString();
//     const logMessage = `[${timestamp}] ${message}\n`;
//     fs.appendFile(LOG_FILE, logMessage, (err) => {
//         if (err) {
//             console.error(`无法写入日志: ${err}`);
//         }
//     });
// }

//执行命令函数
async function executeCommand(command) {
    exec(command, (error, stdout, stderr) => {
        if (error) {
            console.error(`执行错误: ${error}`);
            // writeLog(`执行错误: ${error}`);
            // return;
        }
        console.log(`输出: ${stdout}`);
        // writeLog(`执行命令: ${command}`);
        // writeLog(`输出: ${stdout}`);
    });
}

// Listen for attach event
usb.on('attach', (device) => {
    // console.log('USB attached:', device);
    console.log('attach Vendor ID:', device.deviceDescriptor.idVendor);
    console.log('attach Product ID:', device.deviceDescriptor.idProduct);
    if (device.deviceDescriptor.idVendor === DEVICE_VENDOR_ID && device.deviceDescriptor.idProduct === DEVICE_PRODUCT_ID) {
        console.log('检测到设备插入');
        // writeLog('检测到设备插入');
        if (platform === 'win32') {
            // 模拟鼠标移动唤醒屏幕
            executeCommand('cmd /c '+ WINDOWS_DIR + 'nircmd-x64\\nircmd.exe sendmouse move 10 10');
            // writeLog('移动鼠标唤醒屏幕');
            //切换LG显示器 DP端口
            executeCommand('cmd /c '+ WINDOWS_DIR +'amdddc-windows.exe --i2c-source-addr 0x50 setvcp 5 0 0xD0');
            // writeLog('切换LG屏幕已执行');
            // 查看所有显示器
            // executeCommand('cmd /c '+ WINDOWS_DIR +'winddcutil.exe detect');
            executeCommand('cmd /c '+ WINDOWS_DIR +'winddcutil.exe setvcp 1 0x60 15');
            // writeLog('切换屏幕已执行');
        }
        // else if (platform === 'darwin') {
        //     // macOS 的唤醒命令
        //     executeCommand('caffeinate -u -t 1');
        //     //切换HDMI-2
        //     executeCommand('ddcutil -d 1 setvcp xF4 x0091 --i2c-source-addr=x50 --noverify');
        //     //切换HDMI-1
        //     executeCommand('ddcutil -d 2 setvcp 60 0x11');
        //     // macOS 的熄屏命令
        //     // executeCommand('pmset displaysleepnow');
        // }
        else {
            console.log('不支持的操作系统');
        }
    }
});

// Listen for detach event
usb.on('detach', (device) => {
    // console.log('USB detached:', device);
    console.log('detach Vendor ID:', device.deviceDescriptor.idVendor);
    console.log('detach Product ID:', device.deviceDescriptor.idProduct);
    if (device.deviceDescriptor.idVendor === DEVICE_VENDOR_ID && device.deviceDescriptor.idProduct === DEVICE_PRODUCT_ID) {
        console.log('检测到设备拔出');
        // writeLog('检测到设备插入');
        if (platform === 'win32') {
            // writeLog('移动鼠标唤醒屏幕');
            //切换DP
            executeCommand('cmd /c '+ WINDOWS_DIR +'amdddc-windows.exe --i2c-source-addr 0x50 setvcp 5 0 0x91');
            // writeLog('切换LG屏幕已执行');
            // executeCommand('cmd /c '+ WINDOWS_DIR +'winddcutil.exe detect');
            executeCommand('cmd /c '+ WINDOWS_DIR +'winddcutil.exe setvcp 1 0x60 17');
            // writeLog('切换屏幕已执行');
        }
            // else if (platform === 'darwin') {
            //     // macOS 的唤醒命令
            //     executeCommand('caffeinate -u -t 1');
            //     //切换HDMI-2
            //     executeCommand('ddcutil -d 1 setvcp xF4 x0091 --i2c-source-addr=x50 --noverify');
            //     //切换HDMI-1
            //     executeCommand('ddcutil -d 2 setvcp 60 0x11');
            //     // macOS 的熄屏命令
            //     // executeCommand('pmset displaysleepnow');
        // }
        else {
            console.log('不支持的操作系统');
        }
    }
});

开机启动

接下来就是开机启动脚本了。windows自定义开机启动还是比较方便的,有很多种方法。注册服务、任务调度、放到启动目录,很可惜都不行。前两个开机启动后后台运行,没有黑窗口,但是检测不到显示器了...放到启动目录只能开机登陆后启动,万一在另一台主机上,这台才开机切回来屏幕还是上一台主机的。

新建个文本文档:

@echo off

start /min cmd /k "node D:\xx\xx\脚本名字.js"

改名为xxx.bat

找到了个改注册表开机启动的方法(原文):

  • 用户登陆时运行: 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
  • 不管用户是否登录都要运行: 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

两者配置方式相同,只是路径不同,以下是示例:

  1. 右键创建 "字符串值"
  2. 数值名称 随意
  3. 数值数据 指定文件路径(把bat路径填进去)

完美解决,就是会一直有一个黑窗口。不过也好,不用单独也日志,出现问题方便查看。

结语

终于都实现了,不用KVM,也不用手动切换显示器。总算完美了,切换过程丝和KVM一样会黑屏,全靠显示器切换速度。比KVM少两跟线,没有奇奇怪怪的显示问题,USB切换器也比KVM便宜,也支持高分辨率。windows屏幕没有断开,不需要EDID维持原窗口,随便怎么切都是原来的样子。缺点就是只支持两台主机,如果没有mac,那完全可以一台主机跑一套,只监听usb插入事件,支持<=USB扩展坞支持数量。

研究了好几天,中途都想放弃了,LG显示器太磨人了。不过好在还是找到了解决方法,简直大海捞针...

0
取消
扫码打赏
支付金额随意哦!

评论 (1)

取消
  1. 头像
    NicoCat
    Windows 10 · Google Chrome

    妹子威武!!!
    就喜欢有钱又能折腾的大妹子!!!

    回复