Virtual KVM

November 15th 2017

Tags: utilities

My current setup includes a desktop computer running Windows, and a work laptop running MacOS. In order to keep my desktop uncluttered, I have devised a virtual KVM using Synergy (1.8) (Though I have now switched to Barrier, an open-source fork of Synergy, see 2020-09-06 update below) and AutoHotKey. The aim was to use a single monitor, mouse and keyboard with these two systems.

Sure, I could have sprung for two monitors, though I have a nice Asus 27" 2k and little room (or budget) for much else.

First, let's look at how things are connected:

Virtual KVM setup diagram showing a mac laptop connected to displayport and a pc (host) computer connected to DVI

The desktop can actually send commands to the monitor over the DVI connection using something called "Display Data Channel". This capability lets us tell our monitor to switch to another input. Some googling around on how to script this, lead me to this post in the AutoHotKey forums. From there, I was able to figure out the input "source numbers" through trial and error (3: DVI and 15: Displayport for my monitor). Though, I have found you can see exactly which input sources are available by using ControlMyMonitor (Windows):

ControlMyMonitor for Windows showing "Input Select" values of 1, 3, 15 and 17

Finally, I bind the hotkeys to Ctrl+Alt+[ and Ctrl+Alt+], which are the same hotkeys I used to switch computers in Synergy.

The final AutoHotKey script:

; From: https://autohotkey.com/board/topic/96884-change-monitor-input-source/

; Finds monitor handle
getMonitorHandle()
{
  ; Initialize Monitor handle
  hMon := DllCall("MonitorFromPoint"
    , "int64", 0 ; point on monitor
    , "uint", 1) ; flag to return primary monitor on failure


  ; Get Physical Monitor from handle
  VarSetCapacity(Physical_Monitor, 8 + 256, 0)

  DllCall("dxva2\GetPhysicalMonitorsFromHMONITOR"
    , "int", hMon   ; monitor handle
    , "uint", 1   ; monitor array size
    , "int", &Physical_Monitor)   ; point to array with monitor

  return hPhysMon := NumGet(Physical_Monitor)
}

destroyMonitorHandle(handle)
{
  DllCall("dxva2\DestroyPhysicalMonitor", "int", handle)
}

; Used to change the monitor source
; DVI = 3
; HDMI = 4
; DisplayPort = 15
setMonitorInputSource(source)
{
  handle := getMonitorHandle()
  DllCall("dxva2\SetVCPFeature"
    , "int", handle
    , "char", 0x60 ;VCP code for Input Source Select
    , "uint", source)
  destroyMonitorHandle(handle)
}

; Gets Monitor source
getMonitorInputSource()
{
  handle := getMonitorHandle()
  DllCall("dxva2\GetVCPFeatureAndVCPFeatureReply"
    , "int", handle
    , "char", 0x60 ;VCP code for Input Source Select
    , "Ptr", 0
    , "uint*", currentValue
    , "uint*", maximumValue)
  destroyMonitorHandle(handle)
  return currentValue
}

; Map monitor switch to same keys as synergy keyboard switch
$^![::
  setMonitorInputSource(15)
  Send ^!{[}
return
$^!]::
  setMonitorInputSource(3)
  Send ^!{]}
return

Note the input source numbers referenced: 15 (Displayport) and 3 (DVI).

My Synergy config contains this section for setting up the same machine switching hotkeys:

section: options
    switchCorners = none 
    switchCornerSize = 0
    keystroke(Alt+Control+[) = ;switchToScreen(laptop-alias)
    keystroke(Alt+Control+]) = ;switchToScreen(desktop-alias)
end

Caveats

Since I'm running Synergy 1.8, this config may not apply to Synergy 2 users. I haven't seen a reason to switch yet, but will post an update to this if I decide to.

DDC capabilities are not universal. Some monitor manufacturers may not let you do this.

While running the Synergy daemon and AutoHotKey script on the Windows desktop, startup order matters! We need to start AutoHotKey before Synergy or the AutoHotKey keybinding will never fire.

I also need to press Ctrl+Alt+] multiple times to get back to my Windows desktop. I think this is because one press triggers Synergy to switch, the next triggers the monitor switch.

Does it work?

After all of those caveats... Yes. It does. I use it every day, and I'm using it right now. If Synergy included a monitor switch capability in version 2, I would upgrade in a heartbeat.

Edit

Added notes on finding input source numbers.

Update

After learning about an open-source fork of Synergy called Barrier in a recent Hackernews thread, I have fully switched over. I didn't have to change anything in my configuration, I simply had to point Barrier at my old Synergy 1 configuration file.

In the years since I originally wrote this article, Synergy 2 never materiallized. It appears the project has since been cancelled and will be superceded by Synergy 3. It is unfortunate that the product was never extended to include the DDC switching functionality that I was looking for.

However, how that I am using an actively maintained open-source fork, I think I might take a stab at adding the functionality myself.

By Colin Kennedy