📖   Chapter 6

Monitor input switcher

Use the ddcctl monitor protocol to create a display input switcher hotkey.

Next Chapter
APIs used today Description
hs.executeAllows you to call shell commands from Hammerspoon.
hs.hostQuery the OS for hostname information.
hs.hotkeyEverything you need to bind functions to hotkeys.

What you’re building #

In this chapter, you’re going to build a hotkey that seamlessly switches between your display inputs.

I have a Dell 3419UW monitor, with my work Macbook plugged into the USB-C input, and my personal desktop Mac plugged into DisplayPort 1. For a while, I was manually switching between computers dozens of times a day by menu diving on the actual monitor.

If you have a multiple computer setup like me, I’ll show you how to set up a single hotkey with Hammerspoon to seamlessly switch your display inputs, without having to mess with your monitor’s controls.

Switch inputs with a single key press.

Install ddcctl on both computers #

The first thing we need to do is install ddcctl. ddcctl is an open-source command line program that lets you talk to your monitor using the Display Data Channel protocol. Among other things, it lets you switch inputs!

Run this command on both of your computers:

copy
brew install ddcctl

If you run ddcctl, you should see output like this:

D: NSScreen #724072469 (3440x1440 0°) 109.00 DPI
I: found 1 external display
2020-09-03 00:10:24.436 ddcctl[33078:42148351] Usage:
ddcctl  -d <1-..>  [display#]
        -w 100000  [delay usecs between settings]

----- Basic settings -----
        -b <1-..>  [brightness]
        -c <1-..>  [contrast]
        -rbc       [reset brightness and contrast]

----- Settings that don't always work -----
        -m <1|2>   [mute speaker OFF/ON]
        -v <1-254> [speaker volume]
        -i <1-18>  [select input source]
        -p <1|2-5> [power on | standby/off]
        -o         [read-only orientation]

----- Settings (testing) -----
        -rg <1-..>  [red gain]
        -gg <1-..>  [green gain]
        -bg <1-..>  [blue gain]
        -rrgb       [reset color]

----- Setting grammar -----
        -X ?       (query value of setting X)
        -X NN      (put setting X to NN)
        -X <NN>-   (decrease setting X by NN)
        -X <NN>+   (increase setting X by NN)

Figure out the monitor input number #

Each input source (USB-C, HDMI, DisplayPort 1, etc) has an input source number. There is a table of input sources on ddcctl’s README. However, I found it to be incomplete–for example, my monitor’s USB-C input source wasn’t in the table.

Just to be safe, let’s use ddcctl to figure out exactly what the input source number is. Run this command on your first computer:

copy
ddcctl -d 1 -i ?

When I ran this command on my desktop, I got the following output:

I: VCP control #96 (0x60) = current: 15, max: 18

The current: 15 corresponds to my input number, which is the DisplayPort connection for my desktop.

Repeat the ddcctl -d 1 -i ? command on your second computer. When I ran it on my laptop, I go the following output:

I: VCP control #96 (0x60) = current: 27, max: 35

Your numbers will probably be different! Make a note of them, though. My numbers are as follows:

Source Input number
Desktop (DisplayPort) 15
Laptop (USB-C) 27

Figure out your hostnames #

Before moving onto the next step, run the hostname command on each of your computers and make a note of it. Here’s what I got when I ran those commands:

Computer Hostname
Desktop sorny
Laptop st-dbalatero1

We’ll use the hostname in our Hammerspoon script to correctly toggle the opposite monitor input when pressing the hotkey.

Create a new config file #

First, make a config file to hold all your audio switcher code.

copy
touch ~/.hammerspoon/monitor-switcher.lua

And require it in your main config:

copy
require("monitor-switcher")

Detect which computer you’re on #

First, create a function that detects which computer you’re on. We’ll return "laptop" or "desktop", depending on the hostname. hs.host provides a handy way to get the current computer’s hostname:

copy
desktopHostname = "sorny"

local function currentComputer()
  if hs.host.localizedName() == desktopHostname then
    return "desktop"
  else
    return "laptop"
  end
end

Bind a key to switch monitor inputs #

copy
hs.hotkey.bind(hyper, 'm', function()
  -- FIXME: Change these input numbers to the ones you found in the previous
  -- step.
  local desktopInput = 15
  local laptopInput = 27

  -- Figure out which input number we should switch to, based on the current
  -- computer we're on.
  local inputNumber = nil

  if currentComputer() == "desktop" then
    -- We're on the desktop, we want to switch to the laptop
    inputNumber = laptopInput
  else
    -- Otherwise, we switch from the laptop -> desktop
    inputNumber = desktopInput
  end

  -- Issue an input switch command with `ddcctl`.
  hs.execute("/usr/local/bin/ddcctl -d 1 -i " .. inputNumber)
end)

Once you’ve added this, reload your Hammerspoon config and press ⌘⇧⌥⌃M give it a try! Your monitor should switch to your other computer.

Automatically mute your audio on wake

Get the entire script #

Want to just paste in this whole project to your monitor-switcher.lua file?

copy
TK fill this in at the very end once we're sure all the code is solid
Automatically mute your audio on wake