Upgrading to Ubuntu 22.04 brought along a frustrating bug: every time I switch monitors on my KVM, or when the system comes out of power saving, my monitor brightness settings are lost. I have a script I run to reset the values, but it’s too annoying. Something has to be done.
But why?
A normal person might think, why don’t you just turn down the brightness on your monitor? Alas, my monitor is beholden to the vanity of its HDR range, and 0% brightness is more like 50%. I need to turn it down more.
Plus, I don’t like the gamma colour balance.
I want this setting, persisted, forever, only changing if I decide so:
xrandr --output HDMI-0 --brightness 0.5 --gamma “0.9:0.9:0.75”
xrandr
xrandr
is the lifeblood of monitor configuration in Linux. From activating monitors, changing orientation, and, of course, to setting the brightness, it does everything. Well, not quite, especially since Ubuntu 22.04 overwrites the settings — bad Ubuntu.
Asking at “Ask Ubuntu” got me the less than helpful response that I shouldn’t expect the system to honour settings not done via the settings console. I might accept that position if brightness and gamma were adjustable in the settings, but alas, they are not. I’m using Kubuntu desktop, and there’s no brightness setting. There’s gamma, but for who knows what reason, brightness is absent.
I use an nvidia card, so tried their settings tool as well. The “permanent” nvidia settings are also overwritten. This further confirms a regression in the operating system.
A Solution
After many headaches and searching, the below is the best I could come up with it. It watches for udev changes, which should trigger anytime the monitor is disconnected, or powered on/off. As you see however, there’s nonsense.
#!/bin/bash log_file=/tmp/watch_monitor.txt echo "Watching" > $log_file udevadm monitor --subsystem-match=drm --udev | while read line do date >> $log_file echo $line >> $log_file status=$(cat /sys/class/drm/card0-HDMI-A-1/status) if [ "$status" = "connected" ]; then echo "CONNECTED" >> $log_file for i in {1..10}; do xrandr --output HDMI-0 --brightness 0.5 --gamma "0.9:0.9:0.75" &>> $log_file brightness=$(xrandr --verbose | grep Brightness | cut -d ' ' -f 2) if [ "${brightness}" = "0.50" ]; then break fi echo "Try Again..." >> $log_file sleep 0.5s done fi done
udevadm
udevadm
is a high-level interface to the udev system. I’m using it to read events about the hardware. I appear unable to watch for events for a particular connection, so I’m listening to all “drm” events — which is the display subsystem. The events look like this:
UDEV [1483.203365] change /devices/pci0000:64/0000:64:00.0/0000:65:00.0/drm/card0 (drm)
There’s no clean way to map this device path to the monitor of interest, at least not one that I’ve found. I don’t know from the event whether my monitor has changed, or what its status is, only that something display related has changed. Indeed, each time I do a KVM switch, I get several events for the same device. I’m unsure why.
Thus I read /sys/class/drm/card0-HDMI-A-1/status
to check if the video output is actually connected.
Mystery of xrandr and /sys/class/drm names
Hold on though, what is this “card0-HDMI-A-1” name? In xrandr my output is HDMI-0
. I could not find any way that would link the two names together, so I manually tried all the cards in sys/class/drm
until I found the right one.
Does anybody know how to determine link the two names together?
Curiously, if I want information about this device I can type
udevadm info -q all -a /dev/dri/card0
, which is yet another name for it. From that output I could, if I wanted, get the full device path to filter in my script. But so few events show up it felt unnecessary.
An undesired sleep
And what about the sleep 0.5s
? I determined that until the status file reported “connected” I couldn’t do any operations on the monitor. However, even after it connects, the first operation would still sometimes fail, with the message xrandr: Need crtc to set gamma on
.
I’ve not found any explanation for this message, nor have I found any solution other than a sleep statement. Even then, it sometimes fails, thus I created a loop that checks for my desired brightness and retries until it can be set.
Surely there must be someway to figure out the “true” status of the connection?
It works
Despite the nonsense, this script has been working for a few weeks now. When I switch devices via the KVM, power the monitor on/off, or wake it up from sleep, I always get my brightness and gamma settings restored. Despite the sleep and loops, this also always happens prior to me visually seeing anything on the monitor. Thus I’m saved from the blinding flash of full brightness.