In order to automatically update my monitor setup and activate/deactivate my external monitor when plugging my ThinkPad into its dock, I found a way to hook into the ACPI events and run arbitrary scripts.
This was tested on a T420 with a ThinkPad Dock Series 3 as well as a T440p and a T460p with a ThinkPad Ultra Dock.
The only requirement is the ThinkPad kernel module. On most ThinkPads
it's the tp_smapi
module
(which you can find in the tp-smapi-dkms
package in Debian)
but on newer hardware, that interface is
gone and you can simply
use the thinkpad_acpi
module built into the
kernel. That's what generates the ibm/hotkey
events we will listen
for.
Hooking into the events
Create the following ACPI event scripts as suggested in this guide.
Firstly, /etc/acpi/events/thinkpad-dock
:
event=ibm/hotkey LEN0068:00 00000080 00006030
action=su francois -c "/home/francois/bin/external-monitor dock"
Secondly, /etc/acpi/events/thinkpad-undock
:
event=ibm/hotkey LEN0068:00 00000080 00004011
action=su francois -c "/home/francois/bin/external-monitor undock"
then restart acpid:
sudo systemctl restart acpid.service
Note that I'm not using the real "docking" event (ibm/hotkey LEN0068:00 00000080 00004010
)
because it seems to be triggered too early and the new displays aren't ready.
Finding the right events
To make sure the events are the right ones, lift them off of:
sudo acpi_listen
and ensure that your script is actually running by adding:
logger "ACPI event: $*"
at the begininng of it and then looking in /var/log/syslog
for lines
like:
logger: external-monitor undock
logger: external-monitor dock
If that doesn't work for some reason, try using an ACPI event script like this:
event=ibm/hotkey
action=logger %e
to see which event you should hook into.
Using xrandr inside an ACPI event script
Because the script will be running outside of your user session, the
xrandr
calls must explicitly set the display variable (-d
). This is what
I used:
#!/bin/sh
logger "ACPI event: $*"
xrandr -d :0.0 --output DP2 --auto
xrandr -d :0.0 --output eDP1 --auto
xrandr -d :0.0 --output DP2 --left-of eDP1