I already knew that I could get a USB switch pretty cheaply. For switching both the keyboard and the mouse, I was even willing to have two extremely cheap USB switches. Luckily, I found a very nice-looking USB switch with 4 input ports switchable between 2 host computers. You can find these with a variety of brand names for the same hardware on auction sites. For $10-12, you can get the USB 2.0 version. I splurged and spent $20 or so and got the USB 3.0 version (because you never know what the future will bring).

For tidiness, I used some double-sided tape and mounted this lengthwise to the pedestal arm of the monitor. (I also found a couple of other devices to share between the 2 computers.)

For a while, I operated by hitting the USB switch for the keyboard and mouse and then using my monitors built-in controls for switching between the 2 video inputs. That worked fine, but it's amazing how lazy you can become, and then you start thinking that doing two things to switch between computers is just an agonizing exertion.

Around that time, something drew my attention to an article about a project with the title Turn a $30 USB switch into a full-featured KVM. (See the links section for a URL). There were a couple of reasons that I was reluctant to use that project directly. I won't bore you with those reasons, but I credit that project with getting me thinking about what I ultimately used. In particular, it alerted me to the fact that there are wire protocols for switching video monitor inputs. Who knew? (Well, I guess a lot of people knew.)

After some searching around, I found the ddcutil (see the links section for a URL) application that knows how to speak that wire protocol. The web site has a nice write-up on how to discover the specific codes your monitor understands. For example, my monitor wants to receive the code "0x11" a certain way to switch to the HDMI-1 input and code "0x12" for HDMI-2. The code dumping didn't actually say what it wanted for switching to the HDMI-3 input, but I used my vast cerebral capacity to guess that it wanted "0x13", and I turned out to have guessed correctly.

The next thing to figure out was how to get those codes sent to my monitor. My personal machine runs Linux, so the trigger there would be hooking into the udev system to detect the presence of the keyboard or mouse. The work computer runs Windows. I decided that I would be happy with doing all of the reacting on the Linux computer. I would detect both the add and remove events via the udev system. When the usb devices showed up, I would switch to the Linux computer's video input on the monitor. When the usb devices went away, I would switch to the Windows computer's video input on the monitor. That system would not scale very well beyond 2 computers, but it works fine for exactly 2 computers.

I've included a couple of links in the links section to articles describing how to identify USB devices and hook into the udev system for custom activities.

Here is what I have in /etc/udev/rules.d/80-wjc.rules to notice and fire events when udev sees the keyboard coming and going. It's actually the USB hub inside the USB switch that is noticed by udev.

SUBSYSTEM=="usb", ENV{PRODUCT}=="5e3/612/9226" ENV{DEVTYPE}=="usb_device" ACTION=="add", RUN+="/home/wjc/bin/usbswitch.sh ADD"

SUBSYSTEM=="usb", ENV{PRODUCT}=="5e3/612/9226" ENV{DEVTYPE}=="usb_device" ACTION=="remove", RUN+="/home/wjc/bin/usbswitch.sh REMOVE"

Both the add and remove events run a short shell script, usbswitch.sh, with a single command line argument to indicate which way the wind is blowing. Here is that shell script:


# Run by a udev rule when my USB switch it switched away from this machine.
# Looks for the USB hub "remove" command.

/bin/echo `date` $1 >> /tmp/usbswitch.log

if [ "x$1" == "xADD" ]
    # HDMI-2
elif [ "x$1" == "xREMOVE" ]
    # HDMI-3
    /bin/echo `date` "unknown operation: " $1 >> /tmp/usbswitch.log
    exit 1

/usr/bin/ddcutil setvcp 60 ${VID}
exit $?

When the USB switch selects the Linux computer, ddcutil is used to switch the video input to HDMI-2. When the USB switch deselects the Linux computer (and does select the Windows computer), ddcutil is used to switch the video input to HDMI-3.


I've been using this setup for a couple of months now, and it works pretty well. There are a couple of minor annoyances that I find tolerable:

  1. It takes a few seconds for my monitor to actually switch inputs. I'm not sure where the delay comes from, so I don't know if it's the monitor's slow reaction to the wire protocol or if it's somewhere in the software lash-up.
  2. The monitor is not interested in switching to a video input that doesn't have an actual video signal. When I'm using one of these computers long enough for the other one to blank its display, the video switching doesn't happen. To work around that, after hitting the USB switch I do some keyboard or mouse activity to wake up the video signal (hitting the shift key is sufficient to wake things up, so no worries about blindly typing some characters). The video signal waking up is not enough to get the monitor to switch inputs (and I might not want that, even if it were sufficient). Instead, I just toggle the USB switch 2 times, bringing me back around to where I want to be, by which time the video signal should be present.
  3. That last sentence is a little white lie. It can take several seconds for my Linux computer to decide to turn its video signal back on, so I sometimes have to toggle the USB switch multiple extra times until the monitor senses it.


Well, that was weird. After changing out the motherboard and case of my Linux computer, but using the same disk drive and connecting all the same peripherals, the ID of the switch's USB hub changed. The video switching stopped working, causing me to resort to the exhausting process of pressing buttons on my monitor. When I got around to investigating, I found that my usbswitch.sh script was not running at all. A perusal of system log files showed a device with a model number of 610 instead of the 612 that I was using. 

That's the model number provided by the manufacturer (AFAIK) and I don't see any reason why it should change. However, the hub is created by a company called Genesys Logic and seems to be the actual part inside tons and tons of USB devices, including lots of computers.  (In fact, my Linux computer has a Genesys Logic hub on the motherboard, though with a different vendor ID from this switch.) This lengthy review of another hub (and the comments to it) using the same part gave me a vital clue. If I plug the hub into one of my computer's USB 2.0 ports, it identifies as 610. If I plug it into a USB 3.0 port, it identifies as both 610 and 612. Before my Linux computer equipment change, I must have had it plugged into a USB 3.0 port and simply didn't notice that it also reported as 610. After the equipment change, I intentionally plugged it into a USB 2.0 port to save my USB 3.0 ports for something that might actually need the higher speed.

Since it reports 610 for both cases, my udev rule file now uses that value. As soon as I changed it and restarted the udev service, things went back to normal.

SUBSYSTEM=="usb", ENV{PRODUCT}=="5e3/610/9226" ENV{DEVTYPE}=="usb_device" ACTION=="add", RUN+="/home/wjc/bin/usbswitch.sh ADD"
SUBSYSTEM=="usb", ENV{PRODUCT}=="5e3/610/9226" ENV{DEVTYPE}=="usb_device" ACTION=="remove", RUN+="/home/wjc/bin/usbswitch.sh REMOVE"