Virtual KVM

November 15th 2017

Tags: utilites

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) 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

The desktop can actually send commands to the monitor over the DVI connection using something called "Display Data Channel". This capability lets 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. 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

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.

By Colin Kennedy