Results 1 to 5 of 5

Thread: Desktop control of external monitor settings with ddcutil and vdu_controls

  1. #1
    Join Date
    Feb 2009
    New Zealand

    Default Desktop control of external monitor settings with ddcutil and vdu_controls

    I recently sought a way to adjust the brightness of a desktop monitor without having to use its physical controls. This post describes what I discovered and introduces a system-tray GUI that I created to control my monitors. Those primarily interested in the GUI can skip over the first part of the post.

    A Brief Intro to DDC/MCCS for Desktop Control of Monitors

    Utilities such as xrandr and xset provide some control over display settings, but these utilities can't change the physical monitors settings such as brightness and contrast (DPMS settings excepted). For example, if a monitor's backlight brightness is currently set to 13%, using xrandr to increase brightness just squishes the darker parts of the range up toward 13%, which is not the same as using the monitor's physical controls to increase backlight brightness beyond 13%.

    There is a VESA standard for controlling monitor settings directly from a PC. The Data Channel Channel (DDC) standard is part of the spec for external-monitor connectivity. The standard includes the DDC Command Interface (DDC/CI) which is a means for PC's to pass commands to monitors. Actual DCC/CI commands are defined by the Monitor Control Command Set (MCCS) which includes a set of Virtual Control Panel (VCP) codes that provide read and write access to specific monitor settings. Most monitors made in the last decade have some level of support for DDC/MCCS. Onboard laptop displays don't implement DDC, so DDC/MCCS is only applicable for externally connected monitors. USB connected external monitors normally support MCCS, but over USB, not DDC.

    The ddcutil DDC/MSSC Command Line Tool

    There are a few open source DDC utilities. The most practical Linux DDC utility is the command line tool ddcutil. What makes ddcutil practical is that it copes well with the spectrum of DDC and USB MCCS implementations as well as the sometimes unreliable nature of DDC communications. As an added bonus, ddcutil comes pre-packaged for Tumbleweed.

    There is one core dependency for ddcutil, DDC is a i2c based protocol, so ddcutil requires the i2c-dev kernel-module. In addition to ensuring i2c-dev is loaded, those using Nvidia's GPU driver will need to follow some ddcutil documentation to set a reliable i2c-dev speed. Full configuration instructions can be found in the ddctuil man page, the packaged help files, and at

    When starting out with ddcutil, the first thing to do is to see if it can detect any monitors, for example:
    % ddcutil detect --terse
    Display 1
       I2C bus:             /dev/i2c-5
       Monitor:             HWP:HP ZR24w:CNT008XXXX
    Display 2
       I2C bus:             /dev/i2c-8
       Monitor:             GSM:LG HDR 4K:
    The reported display numbers do not correspond to X11 display numbers, they are monitor connection numbers. The numbering may change if a monitor is switched off or unplugged, if that's a concern, ddcutil may be passed other forms of identification such as the model or serial number.

    Once we know what displays are present we can send VCP codes to control the individual monitors. There are a huge number of VCP codes. Not all VCP codes are useful and not all are standardised. The ddcutil capabilities command can be used to discover what VCP codes a monitor claims to support. For example:
    % ddcutil --display 2 capabilities
    Model: Not specified
    MCCS version: 2.1
    VCP Features:
       Feature: 02 (New control value)
       Feature: 04 (Restore factory defaults)
       Feature: 05 (Restore factory brightness/contrast defaults)
       Feature: 08 (Restore color defaults)
       Feature: 10 (Brightness)
       Feature: 12 (Contrast)
       Feature: 14 (Select color preset)
             05: 6500 K
             08: 9300 K
             0b: User 1
       Feature: 16 (Video gain: Red)
       Feature: 62 (Audio speaker volume)
       Feature: 8D (Audio Mute)
       Feature: F4 (manufacturer specific feature)
       Feature: F5 (manufacturer specific feature)
          Values: 00 01 02 (interpretation unavailable)
    Some monitors are not 100% accurate or complete in their capability claims. I have one monitor that claims to have two DisplayPort input sources, but it physically only has one (changing the input to the imaginary one does nothing).

    For safety ddcutil will only allow write access to known/supported codes, mystery manufacturer specific codes are solely read-only. Continuing the example from above, display 2 supports VCP code 10, the code for the brightness control, the monitors real brightness can be retrieved or set as follows:

    % ddcutil --display 2 getvcp 10
    VCP code 0x10 (Brightness                    ): current value =    50, max value =   100
    % ddcutil --display 2 setvcp 10 90
    Scripts can be written to streamline their use of particular codes. For example, I use the following brightness altering script to set the brightness on one or more monitors at a time:

    # Content of $HOME/bin/brightness
    # Reads parameters: displayId newBrightness [displayId newBrightness...]
    while [ $# -ge 2 ]
        old_brightness=$(ddcutil --brief --display $ddc_display_id getvcp 10 | awk '{print $4}')
        if [ $new_brightness -ne $old_brightness ]
            echo "INFO: $ddc_display_id setvcp 10 $new_brightness"
            ddcutil --display $ddc_display_id setvcp 10 $new_brightness
            echo "INFO: $ddc_display_id already set to $new_brightness"
    In my case I normally have two monitors connected, monitors 1 and 2, I can use the above script to set them to 80% and 90% brightness as follows:
            /home/michael/bin/brightness 1 80 2 90
    Commands and scripts such as the above can be hooked into the desktop start menu by creating KDE/Gnome desktop files in an application menu or the favourites menu. For example, my kickoff menu includes a couple of favourites for brightening and dimming my monitors for daytime and nighttime, and further favourite for monitor DPMS suspend:

    The desktop entry files for the above can be constructed by right mousing on the kicker icon, selecting to edit applications, selecting an appropriate menu, and adding a new item. Alternatively .desktop files can be manually created in $HOME/.local/share/applications. My $HOME/.local/share/applications/bright\ display.desktop file contains:

    [Desktop Entry]
    Exec=/home/michael/bin/brightness 1 80 2 90
    GenericName=Brighten all monitors
    Name=Brighten Display
    The file for dimming is also the same, only the brightness values and Name are different. The script associated with the favorite for Suspend Displays, doesn't require ddcutils, loginctl lock-session and xset dpms force suspend is all that is needed.

    A System-tray GUI for ddcutil - vdu_controls

    As an exercise in Qt Python scripting I created vdu_controls, a system tray app with access to a subset of MCCS controls. Here are a few screenshots of vdu_controls in various configurations:

    1. *vdu_controls --show brightness --show audio-volume

      *vdu_controls with all useful controls activated (additional "less useful" controls can be activated via the command line)
    2. vdu_controls --no-splash --system-tray

    The code for vdu_controls is available on github as well as a detailed and man page. The script was developed on Tumbleweed using the default python3.8 with the additional zypper installs of python38-qt5 and ddcutil.

    I tried to write vdu_controls to be as self contained as possible. If ddcutil and python38-qt5 are installed, a copy of the script is all that is needed. Supporting icons and a default splash-screen image are embedded inside the script. The script can optionally self install itself into $HOME/bin along with creating an appropriate .desktop menu file. For development all that is needed is an editor and the python3 command. I have added all the normal python source-hierarchy boilerplate for documentation and builds, but only as a learning exercise, none of that is necessary (it's overkill for one self-contained script).

    Possibilities for automatically adjusting brightness according to the ambient light level

    One other idea I toyed with was to use an old webcam to measure the ambient brightness and then adjust my monitors automatically as conditions changed. I used ffmpeg and ImageMagick to try and measure the ambient light level from a webcam image, for example:
    # Capture one frame
    ffmpeg -y -s 1024x768 -i /dev/video0 -frames 1 $HOME/tmp/out.jpg 2>>$logfile
    # Resize to one pixel and extract the pixel (max) value, then use variuous substitutions to extract just the numberic value.
    ambient=$(convert $HOME/tmp/out.jpg -colorspace gray -resize 1x1 -evaluate-sequence Max -format "%[fx:100*mean]" info: 2>/dev/nulll)
    ambient=$(echo $ambient | sed 's/[.].*//')
    echo INFO: camera ambient maximum $ambient
    That didn't work too well because the camera had built-in automatic exposure, if the ambient light level dropped, the camera increased the exposure. I could roughly determine between night and day, but if clouds moved across the sun my heuristics could get fooled. Perhaps better camera positioning, a fixed image target, some blinkers, or a frosted lens cover might have helped. If I had some better light-metering hardware, some form of automatic brightness adjustment would be quite achievable.

    Final thoughts

    I hope this post up might help if you need to control a monitor without reaching for it's physical controls, or if you need to climb the learning curve on Python or PyQt, or if you'd just like to add some desktop favourites of your own.

    While we're on monitor related issues I have another howto post on KDE multiple monitors with different resolutions. If you want to combine a new 4K monitor with older non-4k monitors for an X11 desktop, that post might be worth a look.

    BTW, Sanford Rockowitz, the author of ddcutil is also working on a comprehensive GUI, but I think smaller/lighter GUI scripts might still be useful for places such as the system tray.

  2. #2
    Join Date
    Feb 2009
    New Zealand

    Default Re: Desktop control of external monitor settings with ddcutil and vdu_controls

    There is now an openSUSE Build Service community package for vdu_controls for Tumbleweed, Leap and Fedora at

    The built packages include the following major features added since my original post:

    • A named presets facility for switching between a set of VDU configurations such as night, day, photography, and video.
    • A default settings file and VDU specific settings files.
    • GUI Settings editor with a tab for default settings and a tab for each VDU.
    • All settings and presets files are now INI files for ease of editing.
    • Grayscale calibration chart to assist with adjustments.

    The build service packaging mainly adds additional files such as a man-page, separate licence file as well as installing those for all users (under /usr/...). It's still possible to download the very latest python script from github and use that directly.

  3. #3
    Join Date
    Feb 2009
    New Zealand

    Default Re: v1.5.3 light/dark theme change support

    The vdu_controls github and OpenSUSE builds have been updated the v1.5.3.

    This version adds support for light/dark desktop theme changes, the icon colors will change according to the set theme, both at startup and while running.

  4. #4
    Join Date
    Feb 2009
    New Zealand

    Default Re: v1.5.4 fix issue #6 related to first time use

    Quote Originally Posted by mchnz View Post
    The vdu_controls github and OpenSUSE builds have been updated the v1.5.3.

    This version adds support for light/dark desktop theme changes, the icon colors will change according to the set theme, both at startup and while running.
    I've released 1.5.4 to address a couple of issues with first time use: The symptoms were errors on first startup when no config files exist. Existing users with established config files would not have noticed.

    Github and OpenSUSE builds have been updated.

  5. #5
    Join Date
    Feb 2009
    New Zealand

    Default Re: vdu_controls 1.5.5

    Thanks to some suggestions and contributions from Matthew Coleman ( I'm releasing version 1.5.5.

    Changes include:

    • Prevent app-exit when running from the tray - by Matthew Coleman. This happens for some desktops, but not my own Tumbleweed KDE desktop where Qt appears to be tray aware, so KDE users may not notice any difference.
    • Fixed the handling of CNC (Complex Non-Continuous) VCP value-types shown as combo-boxes, such as VCP-code 14 Color-Presets.
    • Recover gracefully from unexpected NC/CNC values (due to incorrect metadata being supplied by the VDU hardware).
    • Default to only enabling brightness and contrast, the ones users are most likely to actually want (most VDU's reliably support these two, plus startup is faster).
    • Enable automatic restart at login via KDE/X11 desktop session management (borrowed code from
    • Save/restore Qt window state/geometry across restarts - the main window should now stay where you put it.

    If you have put in place anything to start vdu_controls at login, this may no longer be necessary. It depends on which desktop you use, but on my KDE desktop vdu_controls will now be restored automatically if it was running at logout/shutdown. On the odd occasion KDE may fail to restart all the apps that were running, but this doesn't happen very often.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts