External monitors with xrandr and a Lenovo Thinkpad Pro Dock

Recently, I purchased a nice new Lenovo ThinkPad T460s. Naturally, I'm running Ubuntu on it and maintaining my new venture into using the i3 window manager. One problem that I ran into with this was when docking the ThinkPad at work. I wanted to use an external monitor that was plugged into the dock. Since i3 doesn't manage things like this for you, I had to hook into some events that know when the laptop has been docked and undocked and then use xrandr to hook up the external monitor. This is how I managed to make it work. Albeit, with a few bugs...

First, you need to create a script that will handle your xrandr commands. I called this script manage-monitor.sh and I put it inside of /etc/acpi/.

#!/bin/sh
export DISPLAY=:1
export XAUTHORITY=/run/user/1000/gdm/Xauthority

if [ "$1" = "dock" ]; then
    logger "ACPI event: Turning on DP2-2. This will take 10 seconds or so..."
    while (xrandr | grep "DP2-2 disconnected"); do
        sleep 1
    done
    sleep 1
    xrandr --verbose --output eDP1 --off --output DP2-2 --mode 2560x1440 --primary 2>&1 | logger 
    /home/dean/Scripts/background.sh
else
    logger "ACPI event: Turning off DP2-2"
    xrandr  --verbose --output DP2-2 --off --output eDP1 --mode 1920x1080 --primary 2>&1 | logger
fi

Just a few notes before moving on

  1. You must set the DISPLAY environment variable inside of the script. Since it will need it. You can also opt to tell xrandr directly with the -d option. Usually, I've found that setting this to :0 works, but in my case, it was :1
  2. You must set the XAUTHORITY environment variable inside of this script too. This should point to your Xauthority file. Sometimes this is in ~/.Xauthority, but in my case it was in the place shown above. An easy way to find this out is to boot into a workable desktop environment like Gnome and just open a terminal and run echo $XAUTHORITY and use that.
  3. The sleeping in the script is due to my monitor being slow to wake up and be available to xrandr. I was running into issues where I was getting the error xrandr: cannot find mode 2560x1440 and this was due to the monitor not being immediately available by the time the command was run. You may or may not need this extra sleep. I did.
  4. I'm turning off my laptop screen and turning on my external screen. You can opt to have both on if you'd like, just edit the xrandr command in the script to suit your needs.
  5. Lots of logging is happening to help you out.
  6. Run xrandr with no arguments to see what monitors and modes you have available to you. You may not have a DP2-2 monitor and it may be called something else.
  7. The background.sh script that is being run simply resets my background image using feh. You may not need to do this.

Hooking up the events

Using udev, you can watch the events that are happening and fire off this script. I created two files in /etc/acpi/events and they are:

/etc/acpi/events/thinkpad-dock:

event=ibm/hotkey LEN0068:00 00000080 00004010
action=su dean -c "/etc/acpi/external-monitor.sh dock"

/etc/acpi/events/thinkpad-undock:

event=ibm/hotkey LEN0068:00 00000080 00004011
action=su dean -c "/etc/acpi/external-monitor.sh undock"

Making sure you have the right event

Notice the weird string for the event? I found these online and it worked properly for me. If these events aren't firing off your script for you, open a terminal and run sudo acpi_listen. Once you've done this, undock and redock your laptop. You'll see the event keys print out for these events. Copy and paste the correct one into your events file and you should be good.

Once you've done this, reload udev with sudo udevadm control -R. After this, dock or undock your laptop and things should work. If they don't, take a look at your syslog file. There should be plenty of logging in there to help you out, as I'm redirecting all of the output from xrandr and throwing it into syslog. It may be you're not tied to the right events. If you are, at a minimum you should see something in syslog that says ACPI event: Turning on ... If you don't see that, udev hasn't registered the events you're looking for, so you'll have to do what I recommended above with acpi_listen.