Problem

Usually I have several computers on, under and around my desk. Most of them need keyboards, monitors and mice.

Solution -1

Don't have more than one computer.

Yeah right. That's not gonna happen. ;-)

Solution 0

Have separate keyboards, monitors and mice for each computer that needs it. But it's difficult to attached keyboards, monitors, and mice (and expensive!) to all of them. All that equipment takes up a lot of space and isn't ergonomic.

Solution 1

Have one main computer and run the rest headless with Remote PC, VNC/NX or SSH.

I have to support a variety systems and don't necessarily want to figure out how to install and configure software on each. Performance over network sessions usually lags a bit (although my new Gbit network should help). Virtual connections like this don't give you a fallback if something goes wrong which happens often enough in a home lab environment that I wouldn't seriously consider running headless unless the device offered a serial console.

Solution 2

KVM (Keyboard, Video, Mouse) switches allow you to plug in one set of peripherals and switch them between N computers.

I've used a variety of these over the years from cheapo two port models to commercial grade 8 or 16 port. There tend to be a couple issues.

  1. They're pretty expensive if they're any good.
  2. Everything must have a common video output, usually VGA or sometimes DVI. HDMI versions tend to much more expensive.
  3. They're glitchy. The good ones all simulate HID devices so that the computer thinks a mouse and keyboard are connected all the time but I've never used one that was reliable 100% of the time. The computer will disconnect once in a while, you'll have to power cycle the KVM switch, a keypress will get "stuck" and repeat a zillion times. Once recently the delete key got sent over and over and a bunch of my email disappeared. Yikes!
  4. Actually switching can be pain. I like it when the KVM switch allows you to use some keyboard shortcut to switch and if it has an onscreen display that's great too. But even on the ones that do this it sometimes doesn't work and you have to resort to pressing the on-device buttons.
  5. Resolution is bad. Since you're usually stuck with a lowest common denominator for video output, you're often getting less than the best that each computer can output or that your monitor could accept.

Solution 3-ish

Use the multiple video inputs on the monitor and a usb switch.

I recently learned (don't remember where) about DDC/CI. It turns out that most video outputs on computers (from VGA all the way up to DisplayPort) include an I2C bus that allows the computer to control the monitor, including, brightness, contrast and most importantly, video source. Since my main monitor has 4 video ports I can support up to 4 computers without any special hardware. Bonus: the fact that all 4 ports are different (DVI, VGA, HDMI, and DP) is actually a benefit because between the four computers I'd like to connect right now, no more than 2 of them have the same kind of port.

(https://en.wikipedia.org/wiki/Monitor_Control_Command_Set)

(https://en.wikipedia.org/wiki/Display_Data_Channel)

I still need a way to switch usb for the keyboard and mouse. Right now I'm using the KVM I was using just without any video attached. It mostly works, except the stuck keystroke issue mentioned above. Grrrr.

Linux software for DDC/CI

DDCUtil

Is quite cool. Add i2c_dev to /etc/modules-load.d/modules.conf and add your user to i2c group.

$ sudo modprobe i2c_dev
$ ddcutil detect
$ ddcutil --display 1 probe
$ ddcutil -d 1 setvcp 0x60 0x03

VCP feature 60 is the command for change input source. Possible values on my computer are:

 01: VGA-1
 03: DVI-1
 0f: DisplayPort-1
 11: HDMI-1

Windows

One option I haven't tried.

At the moment I'm using a python script cribbed from here. Further reading here.

from ctypes import windll, byref, Structure, WinError, POINTER, WINFUNCTYPE
from ctypes.wintypes import BOOL, HMONITOR, HDC, RECT, LPARAM, DWORD, BYTE, WCHAR, HANDLE


_MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM)


class _PHYSICAL_MONITOR(Structure):
    _fields_ = [('handle', HANDLE),
                ('description', WCHAR * 128)]


def _iter_physical_monitors(close_handles=True):
    """Iterates physical monitors.

    The handles are closed automatically whenever the iterator is advanced.
    This means that the iterator should always be fully exhausted!

    If you want to keep handles e.g. because you need to store all of them and
    use them later, set `close_handles` to False and close them manually."""

    def callback(hmonitor, hdc, lprect, lparam):
        monitors.append(HMONITOR(hmonitor))
        return True

    monitors = []
    if not windll.user32.EnumDisplayMonitors(None, None, _MONITORENUMPROC(callback), None):
        raise WinError('EnumDisplayMonitors failed')

    for monitor in monitors:
        # Get physical monitor count
        count = DWORD()
        if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)):
            raise WinError()
        # Get physical monitor handles
        physical_array = (_PHYSICAL_MONITOR * count.value)()
        if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array):
            raise WinError()
        for physical in physical_array:
            yield physical.handle
            if close_handles:
                if not windll.dxva2.DestroyPhysicalMonitor(physical.handle):
                    raise WinError()


def set_vcp_feature(monitor, code, value):
    """Sends a DDC command to the specified monitor.

    See this link for a list of commands:
    ftp://ftp.cis.nctu.edu.tw/pub/csie/Software/X11/private/VeSaSpEcS/VESA_Document_Center_Monitor_Interface/mccsV3.pdf
    """
    if not windll.dxva2.SetVCPFeature(HANDLE(monitor), BYTE(code), DWORD(value)):
        raise WinError()


# Switch video source (0x60) to HDMI (0x11 on my ASUS)
for n, handle in enumerate(_iter_physical_monitors()):
    if n == 0:
        continue
    set_vcp_feature(handle, 0x60, 0x11)

Future Solutions

At some point I'd like to build a USB keyboard that could be connected to multiple computers (based on ProMicros or similar) and also could send I2C commands to the monitor.