Monitor Brightness on the Desktop

Since I found the buttons on my monitor to be inconvenient, I decided to try to go for a commandline utility that could set the brightness. There are several ways this can be implemented:

  • the driver offers screen brightness setting through ACPI (via /sys/class/backlight, should work on most laptops)
  • the driver offers a backlight control via X11
  • the driver offers an i2c device (/dev/i2c-*, can be used with ddccontrol)
  • using VESA BIOS Extensions functions (which works by mapping the video card memory and often using real mode calls, which might not support setting brightness, not 100% sure about that though)
  • using setpci (this is not recommended)

All of those methods end up using DCC/CI in the end I guess. Sadly, with the proprietary AMD driver I was using, none of those options were working. The solution was to get rid of it and go back to the open source radeon driver. Minor downside of that: I had to disable the desktop compositor because it was tearing like crazy (Xfce, uses Xrender which doesn't do VSync). The radeon driver offers an i2c interface (after loading the i2c-dev kernel module), so I'm quite happy there's at least one method that works now. To set the brightness I use ddccontrol:
ddccontrol -r 0x10 -w 30 dev:/dev/i2c-2
The argument to -r being the Command Interface function and the value after -w being the new value/brightness. You can use gddccontrol to easily find out what functions your monitor offers.
For my convenience, I of course wrote a small Python script with some profiles to quickly change the brightness:

#!/usr/bin/env python3
"""
Change screen brightness through DDC/CI using ddccontrol
sudo ddccontrol -r 0x10 -w 20 dev:/dev/i2c-4
-r: control to query
-w: value to write

Controls:
0x10: Brightness (0-100)
0x12: Contrast (0-100)
0xd6: DPMS (1=on, 4=standby)

Devices:
i2c-2: U2713HM
i2c-4: SyncMaster 2253LW
"""

devs = {0: "dev:/dev/i2c-2", 1: "dev:/dev/i2c-4"}
presets = {"default": [4, None],
    "normal": [4, 20],
    "sun": [100, 100],
    "movie": [30, None],
    "movie2": [60, None]}

import argparse
p = argparse.ArgumentParser("br – monitor brightness changer")
p.add_argument("-m", "--monitor", type=int, choices=devs.keys(), default=0)
p.add_argument("-b", "--brightness", type=int, choices=range(0, 100+1))
p.add_argument("preset", nargs="?", choices=presets.keys(), default="default")

args = p.parse_args()

def br(dev, brightness):
    from subprocess import call
    cmd = ["sudo", "ddccontrol", "-r", "0x10", "-w", str(brightness), dev]
    print(" ".join(cmd))
    return call(cmd)

if args.brightness:
    if args.monitor is not None:
        br(devs[args.monitor], args.brightness)
    else:
        for dev in devs.values():
            br(dev, args.brightness)

else:
    for idx, dev in devs.items():
        preset = presets[args.preset][idx]
        if preset is None:
            continue
        br(dev, preset)