ThinkPad x61s UltraBay docking script

UPDATE: I’ve updated this script with a much better design because SELinux wouldn’t let me muck around with X’s tmp files. dock.sh.

Over a year ago I decided to pick up a new ThinkPad, this time the ultra portable x61s. My desktop was aging and I needed mobility more than anything else. I had always intended to get the X6 UltraBay so I could set it up on my desk but I never seemed to get around to it (probably on account of the phobia I developed while trying to get RedHat 9 to suspend and resume on my old T40).

Tonight, after getting my new (well new to me) UltraBay from ebay in the mail I took on setting up a script to run when docking and undocking the laptop. First off, I’m impressed with how well Linux works with the docking station “out of the box”. Even without udev rules to handle the dock/undock event the CD-RW/DVD combo drive hot plugs perfectly (I’m running a vanilla 2.6.31 kernel on Debian Lenny).

The udev rules and docking scripts for this set-up are well documented out there on the web but there were some details I had to assemble from a few different places. This post is mostly to collect the details in one place. If it’s useful to someone else out there even better.

First lets define what we’re trying to do: I’ve hooked up an external monitor to the UltraBase and I want to distribute my desktop across the external monitor and the LVDS display on the laptop when it’s docked. When it’s undocked the laptop should return to using only the LVDS display. We can script all that using a little bit of control logic and some very basic xrandr commands.

Let’s run through getting xrandr working first, then worry about when and how the script should be run. ThinkWiki has some great info for using xrandr to configure an external monitor. But when I ran some of these commands manually, nothing happened?

Turns out that when I first installed Debian on this laptop there wasn’t an external monitor attached (big surprise) so when X was configured it generated a configuration file that couldn’t handle the external monitor. This means xrandr could query properties from the connected monitor just fine, but any attempt to change the configuration did nothing. The command gave a successful status code but nothing happened.

The suggested fix is to attach the second monitor and reconfigure X. On Debian we’d expect this to be done through dpkg:

dpkg-reconfigure xserver-xorg

Which does nothing. Turns out you can tell X to generate a config file directly which does the trick this time:

sudo X -configure

I know nothing about X configuration files so I can’t say why this works but it does because now when we send X commands through xrandr it responds.

Start small by just turning on the external display:

xrandr --output LVDS --auto --output VGA --auto

Your displays may be named a bit differently which you can check using the query option (-q). The external monitor turned on but the screens overlapped in a funny way … progress.

The effect I want is to have my desktop extend across both screens so they’re side by side. xrandr does this without any hassle:

xrandr --output LVDS --mode 1024x768 --output VGA --mode 1024x768 --left-of LVDS

The command above makes the layout pretty obvious: both screens are 1024 by 768 and the VGA screen is positioned to the left of the built in LVDS. Some people want their external screen to have a higher resolution but it’s easy to change the configuration so I’m going for symmetry to start off. That and the larger the screens get the more RAM the video card borrows from the rest of the system.

This is the command we want to run when the laptop is docked, now the command when it’s undocked:

xrandr --output VGA --off

The two commands can be combined into a script which we can pass a parameter, maybe a 1 when the system is being docked, a 0 when it’s undocked. Throw in a case statement and then we can test it:

#!/bin/sh
case $1 in
    0)
        echo "undock" | logger -t $0
        xrandr --output VGA --off
        ;;
    1)
        echo "dock" | logger -t $0
        xrandr --output LVDS --mode 1024x768 --output VGA --mode 1024x768 --left-of LVDS
        ;;
    *)
        echo "unexpected input" | logger -t $0
        exit 1
        ;;
esac
exit 0

Make this file executable and put it in /etc/thinkpad, call it dock.sh

Now to get the system to run the script for us when the laptop is docked and undocked. Debian Lenny uses udev so this is as simple as adding the following in a file in /etc/udev/rules.d/

KERNEL=="dock.0", ATTR{docked}=="1", RUN+="/etc/thinkpad/dock.sh 1"
KERNEL=="dock.0", ATTR{docked}=="0", RUN+="/etc/thinkpad/dock.sh 0"

I named this file 55-thinkpad-local.rules based on someone’s reported success on the linux thinkpad mailing list. The order in which udev rules are run, why the order is important and how to write them is still a mystery to me and will remain that way for now.

Now we put the two together. The log messages that are sent to the syslog should be verified to be sure the script is running right because when the laptop is docked/undocked … the screen layout won’t change! Great right? We can get the error message by redirecting the output of the xrandr commands to syslog too like so:

xrandr --output LVDS --mode 1024x768 --output VGA --mode 1024x768 --left-of LVDS 2>&1 | logger -t $0

The error message tells us that the script “Can’t open display”. Wait, root (the script is run as roor) doesn’t have permission to open the display? This turns out to be some X magic that’s explained on the ThinkWiki Fn-F7 page. The important part is way down at the end of the script where root enumerates the X sessions and then one by one exports the server identifiers and the Xauthority cookie. After this root can change the display all it wants. We’ve gotta include this in the original script and while we’re at it throw in some extra stuff to make it pretty. The final script is here. Works pretty good for me, but YMMV.

4 thoughts on “ThinkPad x61s UltraBay docking script

  1. Interesting, trying to do similar on my dell laptop but it doesn’t seem to call that udev rule when docked/undocked. I have to look into this further.

    Just wanted to say, instead of having a “case” in your script you can have a single script that checks if the external monitor is connected or not and then runs the correct xrandr command.

    The following script was not created by me, I found it on the net, unfortunately don’t remember where… sry

    # If an external monitor is connected, place it with xrandr

    # External output may be "VGA" or "VGA-0" or "DVI-0" or "TMDS-1"
    EXTERNAL_OUTPUT="HDMI1"
    INTERNAL_OUTPUT="LVDS1"
    # EXTERNAL_LOCATION may be one of: left, right, above, or below
    EXTERNAL_LOCATION="right"

    case "$EXTERNAL_LOCATION" in
    left|LEFT)
    EXTERNAL_LOCATION="--left-of $INTERNAL_OUTPUT"
    ;;
    right|RIGHT)
    EXTERNAL_LOCATION="--right-of $INTERNAL_OUTPUT"
    ;;
    top|TOP|above|ABOVE)
    EXTERNAL_LOCATION="--above $INTERNAL_OUTPUT"
    ;;
    bottom|BOTTOM|below|BELOW)
    EXTERNAL_LOCATION="--below $INTERNAL_OUTPUT"
    ;;
    *)
    EXTERNAL_LOCATION="--left-of $INTERNAL_OUTPUT"
    ;;
    esac

    xrandr |grep $EXTERNAL_OUTPUT | grep " connected "
    if [ $? -eq 0 ]; then
    xrandr --output $INTERNAL_OUTPUT --auto --output $EXTERNAL_OUTPUT --auto $EXTERNAL_LOCATION 2>&1 | logger -t $0
    # Alternative command in case of trouble:
    # (sleep 2; xrandr --output $INTERNAL_OUTPUT --auto --output $EXTERNAL_OUTPUT --auto $EXTERNAL_LOCATION) &
    else
    xrandr --output $INTERNAL_OUTPUT --auto --output $EXTERNAL_OUTPUT --off 2>&1 | logger -t $0
    fi

    Like

  2. The file placed in /etc/udev/rules.d/ must be named “55-thinkpad-local.rules”, not “55-thinkpad-local.rule”

    That would be why the previous commenter’s udev rule was never called.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s