Adjust laptop backlight

(This is part of a larger series on finding your footing on Arch Linux.)

Last modified: 29 May 2022

Goal: understand how to programmatically adjust your laptop’s backlight brightness from the command line, then create convenient key bindings to do this for you.

Read this if: your laptop has two keyboard functions keys for increasing and decreasing backlight brightness, but these keys have no effect after a standard install of Arch (so you find yourself unable to adjust your backlight brightness).

If your backlight keys are working properly (perhaps your desktop environment set them up for you), congratulations. You’re welcome to read this anyway, and perhaps you’ll learn something useful about udev rules or acpid events, but this guide might not be directly relevant to you.

References:

Contents

Adjust backlight brightness from a shell

Plan: first show how to adjust laptop brightness from a command-line shell, then set up key bindings to do this automatically.

You can interact with your backlight through the Linux sysfs file system, using the contents of the directory /sys/class/backlight.

(sysfs is pseudo file system located in the /sys directory, and provides an interface for interacting with harware devices, drivers, kernel modules, and all sorts of other goodies using virtual files. I suggest taking 10 minutes and browsing through Wikipedia: Sysfs and man 5 sysfs if you haven’t heard of sysfs yet—it’s a really neat feature of the Linux kernel.)

Identify your backlight interface directory

Every computer should have a backlight directory inside of /sys/class/backlight, but the name depends on the graphics card’s manufacturer and model. Standard names I’ve seen in the wild are:

I’ll use /sys/class/backlight/intel_backlight in this guide, but adjust this to /sys/class/backlight/acpi_video0 (or perhaps something else) if that’s what you have inside /sys/class/backlight.

The backlight directory contains the files used to control your backlight. Here’s a look inside mine:

$ ls /sys/class/backlight/intel_backlight
device/
power/
subsystem/
actual_brightness
bl_power
brightness      # useful!
max_brightness  # useful!
scale
type
uevent

We’ll be using the files brightness and max_brightness in this guide.

Using backlight brightness files

The max_brightness and brightness files each contain a single integer number. The number in brightness represents you current backlight brightness, on a scale from 0 (backlight turned off) to the value in max_brightness (maximum brightness). If you change the value in the brightness file, your physical backlight brightness will change accordingly.

The value of max_brightness varies from manufacturer to manufacturer. I think (but cannot confirm) that the values are arbitrary, i.e. they do not correspond directly to any physical quantity.

Do two things:

For example, from my laptop:

$ cd /sys/class/brightness/intel_backlight
$ cat max_brightness
852  # (on my laptop; might be different on yours)

# You must elevate to root privileges to change brightness
su

# Playing around with brightness as a root user
# echo 400 > brightness   # half of maximum brightness
# echo 50 > brightness    # low brightness
# echo 852 > brightness   # max brightness

Note: you must fully elevate to a root shell using e.g. su or sudo -s. A command like sudo echo 42 > brightness will fail, because the sudo privilege applies to the echo command only, and the subsequent output redirection using > runs in a regular (unprivileged) shell. See this Stack Overflow answer for more information.

Instead of using su to elevate to a root shell, you could also combine tee with plain sudo as a temporary solution, e.g.

$ echo 42 | sudo tee /sys/class/backlight/intel_backlight/brightness 

Check-in point: At this point you should be able to adjust your backlight brightness, albeit tediously, by writing to /sys/class/backlight/*/brightness from a root shell.

Allow regular users to modify backlight brightness

Problem: by default, only root users can write to the backlight’s brightness file, and it’s supremely inconvenient to elevate to root privileges for a task as simple and frequent as adjusting your backlight brightness.

Solution: as suggested in ArchWiki: Backlight/ACPI, you can first add backlight-privileged users to the video user group, then create a udev rule that allows unprivileged users in the video group to adjust backlight brightness.

To give regular users backlight permissions…

Add users to the video group

First add any users who should get backlight-adjusting privileges to the video group:

# Replace <username> with the target username(s)
sudo usermod -aG video <username>
# Example: add user foobar to the video group
sudo usermod -aG video foobar

The video group should exist on your system by default, and is described briefly in ArchWiki: Users and groups/Pre-systemd groups. (If words like “permissions”, “file ownership”, “user group”, and chgrp sound unfamiliar, take 15 minutes and read through ArchWiki: Users and groups.)

Create a udev rule

Now create a udev rule to give the video group permissions to write to the backlight sysfs interface. This step depends on your backlight manufacturer. Both options below come from suggestions in ArchWiki: Backlight/ACPI; I have tested both and can confirm they both worked, at least on the hardware I tested (ThinkPad T460 with integrated Intel graphics; MacBookPro 11.5 with discrete AMD graphics). Here’s what to do.

First create the file /etc/udev/rules.d/backlight.rules. Then, depending on your backlight directory, paste in the following line(s):

The two udev rules look so different because the intel_backlight version applies permission changes directly to the brightness file, while the acpi_video0 version applies permissions to the backlight device. But I do not have a good explanation of why this difference is necessary—perhaps because of differences in how the OS kernel and backlight interact on Intel integrated graphics compared to discrete AMD/Nvidia GPUs?

Reboot to ensure changes take effect. You should then be able to write to sys/class/backlight/*/brightness as an otherwise unprivileged member of the video user group.

Check-in point: Before moving on, ensure you can change the backlight brightness by writing to the /sys/class/backlight/*/brightness file as a regular user in the video group.

Convenient key mappings for backlight control

We’ll set up backlight key bindings using the acpid daemon. If you’re a new user, I’ve just introduced two potentially unfamiliar bits of jargon. For our purposes, here is what they mean:

Installation

First install the acpid package and enable acpid.service:

# Install the acpid package
sudo pacman -S acpid

# Enable and start the acpid daemon
systemctl enable --now acpid.service

The acpid service will then listen for ACPI events.

The ACPI event workflow

ACPI events (e.g. function key presses, closing your laptop lid, plugging in a computer charger, etc.) are managed using text files in the directory /etc/acpi/events/. Each event file must define an ACPI event and an action to take in response to the event. These event files use a key value syntax of the form:

# Comments are allowed on new lines
event=<ACPI-event-regex>
action=<shell-command>

The event key’s value should be a regular expression matching the name(s) of ACPI event(s), and the action key’s value should be a valid shell command, which will be invoked by /bin/sh whenever an ACPI event matching the event key’s value occurs.

By default, the directory /etc/acpi/events/ contains a single file, called anything, with the following contents:

# Pass all events to our one handler script
event=.*
action=/etc/acpi/handler.sh %e

This generic anything file catches all ACPI events using the event=.* (note the .* catch-all regex). The event’s name, accessed using the %e macro, is then passed as an argument to the default event handler script /etc/acpi/handler.sh.

Key bindings and event handler for backlight control

Here’s the recipe we’ll use:

Reference: ArchWiki: Acpid > Enabling backlight control.

Identify event names

First run the acpi_listen event listener from a command line and identify its output in response to brightness key presses. In my case:

$ acpi_listen
# *presses brightness up and brightness down keys* (F5 and F6 on my laptop)
video/brightnessup BRTUP 00000086 00000000
video/brightnessdown BRTDN 00000087 00000000

Record the event names (video/brightnessup and video/brightnessdown) and corresponding labels (BRTUP and BRTDN).

Create an event handler script

Create the following shell script to handle brightnessup and brightnessdown events. I’ve named it backlight.sh and placed it in the conventional location /etc/acpi/handlers, but you could name it anything you like and probably place it in any readable location on your file system.

#!/bin/sh
# Location: /etc/acpi/handlers/backlight.sh
# A script to control backlight brightness with ACPI events
# Argument $1: either '-' for brightness up or '+' for brightness down

# Path to the sysfs file controlling backlight brightness
brightness_file="/sys/class/backlight/intel_backlight/brightness"

# Step size for increasing/decreasing brightness.
# Adjust this to a reasonable value based on the value of the file
# `/sys/class/backlight/intel_backlight/max_brightness` on your computer.
step=20

# Some scary-looking but straightforward Bash arithmetic and input/output redirection
case $1 in
  # Increase current brightness by `step` when `+` is passed to the script
  +) echo $(($(< "${brightness_file}") + ${step})) > "${brightness_file}";;

  # Decrease current brightness by `step` when `-` is passed to the script
  -) echo $(($(< "${brightness_file}") - ${step})) > "${brightness_file}";;
esac

This script (taken from ArchWiki: Acpid > Enabling backlight control) takes one parameter, which should be either + or -, and either increases (if + is passed) or decreases (if - is passed) the current backlight brightness by the value of the step variable.

Make the handler script executable:

sudo chmod +x /etc/acpi/handlers/backlight.sh

Create event-matching files

Create the event files /etc/acpi/events/BRTUP and /etc/acpi/events/BRTDN (using the event labels BRTUP and BRTDN is not necessary; you can use whatever alphanumeric characters you want that obey the naming conventions in the second paragraph of man acpid). Inside the files place:

# (Adjust path to the `backlight.sh` script as needed)

# Inside /etc/acpi/events/BRTUP
event=video/brightnessup
action=/etc/acpi/handlers/backlight.sh +

# Inside /etc/acpi/events/BRTDN
event=video/brightnessdown
action=/etc/acpi/handlers/backlight.sh -

Reboot. The backlight keys should then change backlight brightness.

Speaking from personal experience: if the backlight keys aren’t working after a reboot, double-check the handler script and event files for typos and ensure the handler script is executable (and make sure you’ve passed both “Check-in points” in the previous section). There’s a lot of moving parts here and even a small typo can prevent things from working.

Troubleshooting: fix failed loading of acpi_video0 with dual graphics

(Probably irrelevant to most users, but I thought I’d include it after going through the problem myself.)

Context: after installing Arch on a MacBookPro with dual graphics (integrated Intel graphics and discrete AMD GPU), the acpi_video0/ directory initially failed to appear inside /sys/class/backlight. Also relevant: my start-up log when booting into Arch included the message:

[FAILED] Failed to start `Load/Save Screen Backlight Brightness of backlight:acpi_video0.`
See `systemctl status systemd-backlight@backlight:acpi_video0.service` for details.

Solution: I got the acpi_video0 directory to appear in /sys/class/backlight (and also eliminated the boot-up error message), by adding the kernel parameter acpi_backlight=video (as suggested in ArchWiki: Backlight/Kernel command-line options) to my boot configuration.

To add pass the acpi_backlight=video parameter to the Linux kernel, assuming you are using systemd-boot as your boot loader, edit the file /boot/loader/arch.conf, and make the following change:

# To your current kernel parameters, for example...
options root=/dev/sdaXYZ rw                          # before

# ...append `acpi_backlight=video`
options root=/dev/sdaXYZ rw acpi_backlight=video     # after

(Or, for a one-time test, type e in the systemd-boot boot screen when logging in, and add acpi_backlight=video to the kernel parameters.)

For adding kernel parameters with other boot loaders, consult ArchWiki: Kernel parameters.