Have you ever tried to change your display brightness only to find yourself frustrated by your display's unintuitive controls? Well, you're in luck because in this post, I'm going to show you how to do it from the comfort of your terminal.
To do that, we'll use the Display Data Channel Command Interface (DDC/CI) protocol which should be implemented on most displays today. Indeed, this is not a new standard, its first specification document dates back to 1998 and has support for CTR displays. It's built on top of I2C and works with HDMI, DisplayPort, DVI, and even VGA.
One way to take advantage of this protocol on Linux is to use the
We first need to install it. I've made instructions for Archlinux and Ubuntu, since I use the former on my personnel computer and the latter at work.
pacman -Sy ddcutil i2c-tools
You will need to load the
i2c-dev module manually since it is not loaded automatically:
To load it upon boot, you can create the
You should now find
/dev/i2c-* files. Note that by default, these device files are owned by the user and group root, so you will need to be root to interact with your displays through
To be able to change your display settings with a regular user, you can change these files' permissions. You first need to create the
i2c group and add your user to it:
groupadd i2c usermod -aG i2c your-username
Then create an udev rule by creating a
/etc/udev/rules.d/45-ddcutil-i2c.rules file containing:
KERNEL=="i2c-[0-9]*", GROUP="i2c", MODE="0660"
This will change the group of the device file to
i2c and make it readable and writable by users of this group. You will need to reboot to test that it works.
apt get install ddcutil
You should not need to load the
i2c-dev module yourself since it's either compiled with the kernel or loaded upon boot by default.
If you want to be able to interact with your displays without switching to root, you can add your user to the
If things go well you should see your displays by running:
Display 1 I2C bus: /dev/i2c-4 EDID synopsis: Mfg id: ACI Model: ASUS MG24U Product code: 9389 Serial number: REDACTED Binary serial number: REDACTED Manufacture year: 2016, Week: 29 VCP version: 2.2
To query your display settings, run:
ddcutil getvcp all
VCP code 0x02 (New control value ): No new control values (0x01) VCP code 0x0b (Color temperature increment ): 2100 degree(s) Kelvin VCP code 0x0c (Color temperature request ): 3000 + 11 * (feature 0B color temp increment) degree(s) Kelvin VCP code 0x10 (Brightness ): current value = 50, max value = 100 VCP code 0x12 (Contrast ): current value = 54, max value = 100 VCP code 0x14 (Select color preset ): User 1 (0x0b), Tolerance: Unspecified (0x00) VCP code 0x16 (Video gain: Red ): current value = 100, max value = 100 VCP code 0x18 (Video gain: Green ): current value = 100, max value = 100 VCP code 0x1a (Video gain: Blue ): current value = 100, max value = 100 VCP code 0x52 (Active control ): Value: 0x00 VCP code 0x60 (Input Source ): DisplayPort-1 (sl=0x0f) VCP code 0x62 (Audio speaker volume ): Volume level: 50 (00x32) VCP code 0x6c (Video black level: Red ): current value = 50, max value = 100 VCP code 0x6e (Video black level: Green ): current value = 50, max value = 100 VCP code 0x70 (Video black level: Blue ): current value = 50, max value = 100 VCP code 0x86 (Display Scaling ): Max image, no aspect ration distortion (sl=0x02) VCP code 0x87 (Sharpness ): current value = 50, max value = 100 VCP code 0x8a (Color Saturation ): current value = 50, max value = 100 VCP code 0x8d (Audio mute/Screen blank ): Unmute the audio (sl=0x02), Invalid value (sh=0x00) VCP code 0xac (Horizontal frequency ): 2228 hz VCP code 0xae (Vertical frequency ): 60.00 hz VCP code 0xb6 (Display technology type ): LCD (active matrix) (sl=0x03) VCP code 0xc6 (Application enable key ): 0x0083 VCP code 0xc8 (Display controller type ): Mfg: Mstar (sl=0x05), controller number: mh=0xff, ml=0x16, sh=0x00 VCP code 0xc9 (Display firmware level ): 0.0 VCP code 0xcc (OSD Language ): English (sl=0x02) VCP code 0xd6 (Power mode ): DPM: On, DPMS: Off (sl=0x01) VCP code 0xdc (Display Mode ): Invalid value (sl=0x0e) VCP code 0xdf (VCP Version ): 2.2
Each value is associated with a VCP code (Virtual Control Panel). For example, here we have the brightness controls which VCP code is
0x10 and which values can go from 0 to 100:
VCP code 0x10 (Brightness ): current value = 50, max value = 100
ddcutil getvcp all tells
ddcutil to query all the known settings. Other values are
color to get all the color related features,
scan to go through all the possible VCP codes from
0xff or a VCP code.
If we want to modify the brightness level to 55%, we would run the command:
ddcutil setvcp 10 55
Note that we lose the
0x in the VCP code. Some displays might use different values too. In that case, you would see a different max value.
If you have more than one display, you can select your display with the
ddcutil --display 1 setvcp 10 55
You can also specify relative values by adding a
- argument before the value.
ddcutil setvcp 10 + 5 ddcutil setvcp 10 - 5
Now, let's say now that we want to modify the input source. In this case, you would need to use the VCP code
VCP code 0x60 (Input Source ): DisplayPort-1 (sl=0x0f)
Its value is not a range, so to get the valid values you can run:
This command shows all the capabilities and features of your monitor. Here, we have the different values for our VCP:
Feature: 60 (Input Source) Values (unparsed): 11 12 13 0F Values ( parsed): 11: HDMI-1 12: HDMI-2 13: Unrecognized value 0f: DisplayPort-1
You can then change the display source to
ddcutil setvcp 60 0x11
Of course, controlling your display from the command line is probably not the most efficient way to go about it (okay... on some displays with terrible OSD interface it might be a step-up already) but this enables to script and define interesting key bindings.
For example, here's a script I use to change the brightness of display which is focused:
#!/bin/bash # Modify the brightness level of the display which has # the focus. # # Usage: ./ddc-brightness [+-] <level> # Examples: # ./ddc-brightness + 10 # increase brightness level by 10 # ./ddc-brightness 30 # set brightness level to 30 # First we identify and retrieve the serial number of # the display which has the focus. display_serial_num=$( sway-msg -t get_outputs | jq '. | select(.focused) | .serial' --raw-output) # Then we change its brightness level. ddcutil --sn "$display_serial_num" setvcp 10 $@
The script is then run with this Sway key binding configuration:
bindsym --locked Shift+XF86MonBrightnessUp exec ~/.bin/ddc-brightness + 5 bindsym --locked Shift+XF86MonBrightnessDown exec ~/.bin/ddc-brightness - 5
There are many other things you can do with
ddcutil. To learn more about it, I encourage you to read its documentation if you are interested. You can also search for the "VESA Monitor Control Command Set Standard" specifications to read about the standard VCP codes.