DEV Community

Cover image for Change screen input programatically on Linux
CodeCadim by Brahim Hamdouni
CodeCadim by Brahim Hamdouni

Posted on

Change screen input programatically on Linux

I have a keyboard/mouse/screen switch to connect 2 PCs: one running Linux and one running Windows.

When I connect all the ports, it works, but every time I switch to the Windows laptop, the screen reconfigures, my windows are moved, and it annoys me. The switch behaves as if I had physically unplugged/replugged the screen, and Windows feels compelled to adjust the entire layout!

To fix this issue, I removed the screen connection from the switch and connected both computers to the screen's 2 sources (HDMI 1 and 2): okay, Windows doesn't rearrange all my windows when I switch to it, but now I have to switch both on the switch to get my keyboard and mouse, and I also have to manually change the screen source from HDMI 1 to 2. And vice versa when I go back to my Linux!

I needed a way to switch the screen source at the same time as I switched the switch. Like, something that detects that I'm switching and voila, changes the screen source for me.

Is that possible on Linux?

Well yes, it is.

Under Linux, we have a utility called ddcutil which allows us to "talk" to the screen and send it commands (like changing the source).

Here's the command to change the source:

sudo ddcutil setvcp 60 x
Enter fullscreen mode Exit fullscreen mode

where x corresponds to the hexadecimal value of the source, and 60 corresponds to the source change functionality.

According to the results of ddcutil capabilities, I was supposed to use values 11 for HDMI 1, 12 for HDMI 2, and 0F for the display port.

sudo ddcutil capabilities
...
   Feature: 60 (Input Source)
      Values:
         11: HDMI-1
         12: HDMI-2
         0f: DisplayPort-1
...
Enter fullscreen mode Exit fullscreen mode

(I omitted other information provided by the command to focus only on the screen source)

In reality, values 11 and 12 didn't do anything. I tested all the x0f values and down, until I got a reaction from the screen.

And for me, it was respectively 5 and 6 for HDMI 1 and HDMI 2.

So, to switch to HDMI 1, I had to do

sudo ddcutil setvcp 60 x5
Enter fullscreen mode Exit fullscreen mode

and for HDMI 2:

sudo ddcutil setvcp 60 x6
Enter fullscreen mode Exit fullscreen mode

To prepare for the next steps, I create 2 script files with these commands.

The first one, which I name set-hdmi1.sh, is the script that changes the screen source to HDMI 1:

#!/bin/sh
# set input source to HDMI 1
sudo ddcutil setvcp 60 x5 --display 2
Enter fullscreen mode Exit fullscreen mode

I don't forget to make this script executable:

chmod a+x /usr/local/bin/set-hdmi1.sh
Enter fullscreen mode Exit fullscreen mode

The second one, set-hdmi2.sh, is the script that changes the screen source to HDMI 2:

#!/bin/sh
# set input source to HDMI 2
sudo ddcutil setvcp 60 x6 --display 2
Enter fullscreen mode Exit fullscreen mode

And I make it executable:

chmod a+x /usr/local/bin/set-hdmi2.sh
Enter fullscreen mode Exit fullscreen mode

Okay, now that it works, I need to detect the keyboard switch.

Linux allows reacting to hardware changes and launching its own actions, thanks to udev. For example, we can take action if we plug or unplug a keyboard.

For this, we need to target the right hardware, and that involves finding its identifiers. The command lsusb lists all devices connected via USB to my Linux, but I'm only interested in keyboards, hence the filter by the grep command.

lsusb | grep -i key
Bus 001 Device 039: ID 04d9:0295 Holtek Semiconductor, Inc. USB-HID Keyboard
Enter fullscreen mode Exit fullscreen mode

The important values are right after the ID:

  • 04d9 identifies the vendor
  • 0295 the product

Then, I create a new udev rules file, for example, 99-kvm.rules in the folder /etc/udev/rules.d with the following content:

ACTION=="remove", ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="0295", RUN+="/usr/local/bin/set-hdmi2.sh"
ACTION=="add", ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="0295", RUN+="/usr/local/bin/set-hdmi1.sh"
Enter fullscreen mode Exit fullscreen mode
  • ACTION allows me to react, either to the removal of the keyboard (action 'remove', I switch to HDMI 2), or to its insertion (action 'add', I switch to HDMI 1).

  • Then, I identify my hardware using the values noted above, for the attributes idVendor and idProduct.

  • Finally, I execute the right script.

It's almost done: if we want these rules to take effect immediately (rather than at the next reboot), we can run the following commands:

sudo udevadm control --reload-rules && sudo udevadm trigger
Enter fullscreen mode Exit fullscreen mode

And there you go! Now, I press the keyboard switch to change the screen source, and Windows stays calm! Thank you, Linux!

Note: Next time I change the KVM, I'll get a basic one that only handles keyboard and mouse :-)

Top comments (0)