Using a USB switch as a full KVM

6 min read Original article ↗

I just found a brilliantly easy quality-of-life improvement I needed to share: display-switch.

Context

I’m the type of person who invests in interfaces. Consequently, I generally like to work at my desk, with my chosen peripherals and tools. Typically, the two most common computers I use there are a work-issued MacBook, and a personal workstation running Linux. I want to switch between them without dealing with a lot of plugging and unplugging of cables.

For years, my solution was to use a USB switch with my monitor plugged into both computers simultaneously (e.g., one via USB-C, one via DisplayPort). When I’d switch, I’d click my USB switch to flip my five peripherals (mouse, keyboard, webcam, USB audio interface, YubiKey). Then, I’d toggle my monitor’s menu to switch inputs. This is fine.

The USB switch is nice because even if a monitor has built-in KVM functionality, it rarely has enough ports for my use case. They also often have fatal design flaws that make them not very functional.

Today, my USB switch broke. Naturally, I took it as an opportunity to revisit the space and see how I could improve my setup.

What I found

First, I discovered UGREEN makes 2-in, 7-out USB 3.2 switches now. I picked one up and it’s been great. I even have two free USB ports now.

Second, and the motivation for this blog post, there are cool software solutions for switching display inputs without touching your monitor settings!

If your monitor supports DDC/CI (display data channel/command interface), then it supports being sent commands via software to switch inputs. With that, you can wire up display-switch, and now your cheap (relative to full-fledged KVMs) USB switch can effectively function as a full KVM, switching your monitor input, too, at the press of a single button!

Simply follow the guide on the display-switch repository README.

That said, I will leave you with a few tips not covered by the README.

Tip: use display-switch for monitors normal KVMs don’t support

Most KVMs, even higher-end ones, do not support very high resolution displays. For example, most will do 4K@60Hz or similar. If you have a higher resolution or refresh-rate monitor, you might have thought that you can’t enjoy the KVM convenience. display-switch works by assuming you are just directly wired into your monitor on both computers, so this isn’t a problem!

Tip: use the brew path for the service on macOS

On macOS, it’s simpler to just brew install haimgel/tools/display_switch. But, if you do, the README leaves you with a footgun when setting it up to run at startup. Specifically, the example dev.haim.display-switch.daemon.plist assumes you have installed it manually and moved it to /usr/local/bin/display_switch. Instead, just install it with brew and update that path to be accurate:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>dev.haim.display-switch.daemon</string>
        <key>Program</key>
        <string>/opt/homebrew/bin/display_switch</string>
        <key>RunAtLoad</key>
        <true/>
    </dict>
</plist>

Tip: find USB device IDs without lsusb

For unknown reasons, I couldn’t get lsusb to work on macOS. If you find yourself in the same boat, here’s a simple way to get the same information.

I suggest uv so you don’t need to deal with Python virtualenvs manually:

uv run --with pyusb python

Then, paste this into your interpreter:

import usb.core

for dev in usb.core.find(find_all=True):
    try:
        name = f"{dev.manufacturer}{dev.product}"
    except Exception:
        name = ""
    print(f"{dev.idVendor:04x}:{dev.idProduct:04x}  {name}")

This should get you lsusb-like output:

046d:0892  None — HD Pro Webcam C920
1532:0085  Razer — Razer Basilisk V2
07fd:000b  MOTU — M2
05e3:0610  GenesysLogic — USB2.1 Hub
04fe:0021  PFU Limited — HHKB-Hybrid
05e3:0625  GenesysLogic — USB3.2 Hub
1050:0406  Yubico — YubiKey FIDO+CCID
05e3:0625  GenesysLogic — USB3.2 Hub
05e3:0610  GenesysLogic — USB2.1 Hub
0b95:1790  ASIX — AX88179A
0b05:0411  Generic — USB3.2 Hub
0b05:0c23  Generic — USB2.1 Hub
0b05:1c55  Cypress Semiconductor — DMC Device
8087:0b40  Intel Corporation. — USB3.0 Hub
1d5c:5801  Fresco Logic, Inc. — USB2.0 Hub

Tip: use cargo to install on Linux

For Linux, rather than downloading releases or building from source manually, use cargo. Doing so will let you use tools like cargo-update to easily keep it up to date with minimal work (via cargo install-update --all --git).

cargo install --locked --git https://github.com/haimgel/display-switch

Like the brew case for macOS, you will now need to change the default $XDG_CONFIG_HOME/systemd/user/display-switch.service example to have the right path. By default it will look like $HOME/.cargo/bin/display_switch.

[Unit]
Description=Display switch via USB switch

[Service]
# update this path
ExecStart=/home/youruser/.cargo/bin/display_switch
Type=simple
StandardOutput=journal
Restart=always

[Install]
WantedBy=default.target

Tip: use on_usb_disconnect to reduce latency

Finally, display-switch supports an on_usb_disconnect configuration:

usb_device = "1050:0407"
on_usb_connect = "Hdmi1"
on_usb_disconnect = "Hdmi2"

But the README notes:

The optional on_usb_disconnect settings allows to switch in the other direction when the USB device is disconnected. Note that the preferred way is to have this app installed on both computers. Switching “away” is problematic: if the other computer has put the monitors to sleep, they will switch immediately back to the original input.

While this is true, if your monitor and setup support it, using on_usb_disconnect can significantly reduce the latency of the switch.

Without it, the chain of delays is:

Push button

USB devices switch

2nd computer detects new USB devices

2nd computer runs display-switch

Monitor switches input

2nd computer ready to use

With it, the monitor input switch and the USB handoff happen in parallel.

 Push button

      ├─────────────────────────────────┐
      ↓                                 ↓
  USB devices switch         1st computer runs display-switch
      ↓                                 ↓
  2nd computer detects           Monitor switches input
  new USB devices                       │
      │                                 │
      └──────────────┬──────────────────┘

             2nd computer ready to use

Summary

If you frequently switch between two computers at your desk, you don’t need an expensive KVM. A USB switch paired with display-switch gets you the same convenience for a fraction of the price. It’s a $500+ KVM-like experience for the cost of an $80 USB switch and a free tool. I’m all about software that reduces friction and makes computing more delightful, and display-switch is a great example of that: switching my monitor and full peripheral set is now a single button press, and meaningfully faster than before.