Tomáš Janoušek, Blog

A better xrandr command-line experience

published (revision history)

Can we make a small improvement to xrandr command-line user experience so that extra tools like arandr (GUI for xrandr) or autorandr become unnecessary for some people (like me)? Yes, I think that making the --output option a bit more powerful goes a long way, and lets me cover most use-cases with just four shell functions/aliases.

function layout-vertical {
  xrandr-smart --output 'eDP-*' --auto \
               --output '!(eDP-*)' --auto --above 'eDP-*'
}
function layout-horizontal {
  xrandr-smart --output 'eDP-*' --auto \
               --output '!(eDP-*)' --auto --right-of 'eDP-*'
}
function layout-clone {
  xrandr-smart --output 'eDP-*' --auto \
               --output '!(eDP-*)' --auto --same-as 'eDP-*'
}
function layout-extonly {
  xrandr-smart --output '!(eDP-*)' --auto
}
functions/aliases I wish I could have
Table of Contents

TL;DR

Grab it here: release branch, tarball.

What? Why?

The way monitor layouts work for most people with mainstream operating systems or desktop environments (like GNOME) is this: you connect an external monitor for the first time, your desktop expands to this monitor, then you can change its position or turn it off in the settings, and then this setup is remembered so that you don’t need to do it again the next time you connect it. People with non-mainstream X11 window managers (like xmonad, i3, awesomewm, fluxbox) can get a similar experience: arandr being the “settings UI” and autorandr handling the initial expansion, saving and restoring.

Still, many people don’t know about these tools, and just use plain xrandr, and then look a bit too nerdy when trying to connect to a projector at some meetup or conference. I’ve got to admit I used to be one of them: I had a script to handle external monitors at home/work, but connecting anything else was so unusual that I didn’t bother writing a script, and had to do it manually, and feel bad about myself afterwards.

This year I finally decided to do something about it. Not a big deal, right? Just adopt autorandr and be done with it. But the script is massive (1500 lines of code including various workarounds for X11 and driver bugs that are already fixed) and the format of its state/configuration files is undocumented, and that’s a bit of a red flag as I like to keep this stuff cleaned up and in version control. So I tried to think of something simpler.

The simple solution I came up with is to extend xrandr to allow shell globs as output names and disable unspecified outputs.

So instead of having several scripts like

layout-home-cz-hdmi
xrandr \
    --output HDMI-2 --mode 1920x1200 --pos 0x0 --dpi 96 \
    --output eDP-1 --mode 1920x1080 --pos 0x1200 --dpi 96 --primary
layout-home-uk-dock
xrandr \
    --output DP-2-1 --mode 1920x1080 --pos 0x0 --dpi 96 \
    --output eDP-1 --mode 1920x1080 --pos 0x1080 --dpi 96 --primary

now I just have

layout-vertical
xrandr-smart \
    --output 'eDP-*' --auto --dpi 96 --primary \
    --output '!(eDP-*)' --auto --above 'eDP-*' --dpi 96

and I think that’s beautiful.

The source for the xrandr-smart script as described is in my dotfiles monorepo, but the best way to obtain the most recent version of it is to use the standalone/xrandr-smart branch, which can also be downloaded as a tarball.

Behind the scenes

xrandr-smart invokes the xrandr-auto-find function which resolves output globs (if the globs match nothing or more than one output, it fails) and invokes the xrandr-auto-off function to disable all other unspecific outputs.

As an example, layout-vertical might translate to:

xrandr-auto-off \
    --output eDP-1 --auto --dpi 96 --primary \
    --output HDMI-1 --auto --above eDP-1 --dpi 96
↓
xrandr \
    --output eDP-1 --auto --dpi 96 --primary \
    --output HDMI-1 --auto --above eDP-1 --dpi 96 \
    --output DP-1 --off \
    --output DP-2 --off \
    --output HDMI-2 --off

And that’s all there is to it, really.

Limitations

There are two significant limitations compared to GNOME and autorandr:

  1. The generic layout-horizontal, layout-vertical scripts can only support the laptop panel and one external monitor. In reality, this isn’t a problem as triple head setups usual need fine-tuned positioning anyway.

  2. We need extra code to support layout saving and (possibly automatic) restoring. Turns out that’s just a few lines: layout-auto, keybindings1.

Future work


  1. Yeah, I invoke this manually using a Fn-key combo. There’s an endless stream of bugs in the kernel, X server and GPU drivers, plus the occassional security issue in a screensaver, so I feel safer to just invoke it manually when I think everything is settled down.