Automatic Display Configuration
I've been using a Thinkpad X201 for a few years now, and had previously written
a script, displayset, which I called from application shortcuts. <super>l
set laptop mode and super<d>
set dual external display mode.
Over time, this proved to be too restrictive. For example, when working on-site at a customer location, I may receive access to one or two monitors of differing resolutions than the monitors in my office used when the notebook was docked. I ended up using a display configuration app bundled with XFCE, which was fine but was far from automatic.
I decided to smarten up displayset so that it would query attached displays via XRANDR and then configure according to some simple rules. dispcfg was born.
Goals of dispcfg
My goals for a smarter display configuration tool were fairly straight forward:
- Find the output devices
xrandr
shows as connected, - Configure the 'best' two outputs as the canvas for X, and
- Require no human intervention.
For dispcfg to configure the displays automatically, I had to set some rules. Namely, the priority of display devices, and the positioning of display devices relative to each other.
Display priority
The Thinkpad X201 has four display outputs, but the Intel graphics hardware can
only use any two at one time. I elected to prefer external displays to the
internal LCD panel, and to prefer digital external outputs over analog external
outputs. xrandr
shows the possible X201 outputs in the order of ascending
preference, least preferable first, which was perfect for my case but may not be
true for other computer systems. The four X201 display outputs are:
- LVDS1 -- the internal LCD panel
- VGA1 -- output on notebook and on dock
- HDMI1 -- output on dock
- DP1 -- output on dock
Therefore a simple, repeatable algorithm can be used to select the display devices. In the given order above, pick the last two outputs showing connected state if two or more outputs are showing connected. If one output is showing connected it will be LVDS1, as the LCD panel is (should be) always connected.
Display positioning
To automatically configure the displays for use by X, positioning information is required. To meet the goal of no human intervention, some automated method for determining, or remembering, positioning information is requried. The most powerful method would be to implement a database of display combinations, using information provided by each display so each set of displays can have a persistent positioning configuration. I didn't investigate this option, because it was more work than I wanted to put into this project. I also expect DPMS information from the monitors would be less than ideal for this strategy, although it's possible something could be crafted that would be good enough to be useful.
I elected for a much simpler methodology. Instead, I always asssume that connected displays are always arranged from left to right, with the most preferred display on the left when two displays will be utilized. So if any one external monitor is connected, it is assumed to be on the immediate left of the LCD panel. And if any two external monitors are connected, it is assumed they are side by side. If HDMI1 and VGA1 are connected, HDMI1 is on the left. If DP1 is connected, it is on the left.
Not accounting for presentation situations, this simple positioning rule works perfectly for all the places I've used my X201 with external monitors. It doesn't work for vertically stacked orientations, which I've not really used much. Nor does it serve to perfectly fine-tune vertical positioning between panels of different vertical display heights. But for my use these limitations are not significant.
How it works
The dispcfg script is written in python. The documentation for the Xlib
module for Python assumes familiarity with Xlib and how the XRANDR extension
works. To reduce the time investment for this project I instead opted to parse
the output of the xrandr
command line utility instead. I'm running
dispcfg on Xubuntu 14.04, but it probably would work fine on any recent-ish
version of Linux and display devices that support XRANDR.
The operation of the script is quite simple: (1) find the connected devices, (2)
select the one or two best, then (3) use xrandr
to configure these outputs. X
receives notification of the change in display, which is then passed on to the
window manager, which then presumably will do something useful given the change.
Thankfully the result of running xrandr
to configure outputs in the same
manner in which they are already configured results in no apparently disruption
to X, as far as I can tell, so running dispcfg when one doesn't need to is
safe.
Then, one need only invoke dispcfg whenever the display connections are changed, or when they are likely to have changed. I'm calling dispcfg when:
- The window manager starts
- The computer resumes from suspend
- Upon the user depressing the Alt+ScrollLock key combination
This strategy has proven to work quite well. I generally end up changing display configurations when the computer is suspended, for example moving from my office to a customer location. So running dispcfg automatically when resuming catches most display configuration changes I see. I rarely power off my computer or reboot it, but when I do, calling dispcfg when the window manager starts catches these events. Finally, for the case when I'm changing displays while the computer is active, a simple tap on Alt+ScrollLock does the job.
Possible improvements
Automatically detect monitor changes
The XRANDR extension and infrastructure allows one to create a daemon that is listening for display changes. dispcfg could be daemonized so its operation would even be more automatic. However, this might actually be undesirable. For example, unplugging a monitor and then plugging it back in has a no net effect to the output devices available, but the transition of one monitor leaving and then returning might have a less than desirable affect for how the window manager ends up positioning windows. While it is certainly possible for dispcfg to become this automated, the increased utility might be eclipsed by annoying side effects.
Presentations
I occasionally give presentations. Often it is very convenient to mirror the notebook LCD panel with an external display such as a projector. dispcfg offers no support for this mode of operation. Being able to run dispcfg with an option to request presentation mode could alter its behavior to use the 'best' connected external monitor and the internal LCD panel at the highest resolution common to both devices, possibly with fall-back to 1024x768 for older projectors that don't support DPMS correctly. It wouldn't be terribly difficult to add this feature.
Where's the code?
The dispcfg utility is available in the GIT repository located here.
What about displayset's maximized window mode?
The original displayset offered one interesting feature that dispcfg
does not. When displayset was executed with the lcd
option, it would
configure only the LCD output, set all current windows to maximized, and
instruct the window manager to create all future windows maximized. This was
useful because the X201 LCD panel is only 1366x768. Especially in vertical
resolution the LCD panel is wanting, and maximizing application windows helps.
This feature is not present in dispcfg for two reasons. First, it seems to me that the role of dispcfg is to configure displays for use by X. How X uses the display area is generally the role of the window manager, and any behavior embedded in dispcfg to give hints to a window manager could make it less generic. Second, I've changed to a tiling window manager, where a 'hint' to maximize windows isn't really useful. I'm having a great experience with i3 and hope to write about it in the future.