Multi-Screen Configuration Using Xrandr

2016-07-02

The command line interface to the RandR extension xrandr(1) allows for configuring screens under Linux and Unix. GUI front ends such as ARandR determine the xrandr command for a screen configuration that the user specifies visually by positioning rectangles representing the available screens. However, ARandR is limited to simple configurations. The Arch Linux Wiki contains some example scripts for more complex xrandr configurations. As none of the scripts allow for cloning your current screen or properly setting another connected screen as the secondary left or right screen, I wrote two bash(1) functions which accomplish those two tasks. The functions can be kept in a bash alias file (see bash-builtins(7) manual) to be able to call them from any shell. Each function builds and executes an xrandr command. The functions also print the used command on the console to enable further manual tweaking. The identifier (name) for the primary (main) screen defaults to eDP1, but the function arguments allow you to specify a different identifier. Calling xrandr without arguments displays basic information including the screen identifiers as well as available and active resolutions. The command xrandr --verbose additionally shows details such as supported ratio, color, and audio parameters.

function screenclone() {
  local src="eDP1"
  if [[ ! -z "$1" ]]; then
    src="$1"
  fi
  # determine the optimal source resolution
  local srcres=$(xrandr | grep -A 1 "$src" | tail -1 | awk '{ print $1; }')

  # determine outputs and their optimal resolution
  for out in $(xrandr | grep '\Wconnected' | grep -v "$src" | awk '{ print $1; }'); do
    outres=$(xrandr | grep -A 1 "$out" | tail -1 | awk '{ print $1; }')
    xrandr --output "$out" --off
    echo "xrandr --output $src --auto --output $out --mode $outres --scale-from $srcres --panning $srcres --set \"Broadcast RGB\" \"Automatic\" --set audio force-dvi --same-as $src"
    xrandr --output "$src" --auto --output "$out" --mode "$outres" --scale-from "$srcres" --panning "$srcres" --set "Broadcast RGB" "Automatic" --set audio force-dvi --same-as "$src"
  done
}

The screenclone function takes an optional command line argument specifying the identifier of the screen to clone. If the function is called without an argument, eDP1 is taken as the default identifier. The function determines the optimal solution of the screen to clone and the external screen and runs both screens at their optimal resolution. The image of the main screen is potentially scaled in order to display it on the external screen.

function screenconnect() {
  local src="eDP1"
  local left=1
  if [[ "r" = "$1" ]]; then
    left=0
  fi
  if [[ ! -z "$2" ]]; then
    src="$2"
  fi

  # determine outputs
  for out in $(xrandr | grep '\Wconnected' | grep -v "$src" | awk '{ print $1; }'); do
    xrandr --output "$out" --off
    if [[ $left -eq 1 ]]; then
      echo "xrandr --output $src --auto --primary --output $out --auto --left-of $src --panning 0x0 --set \"Broadcast RGB\" \"Automatic\" --set audio force-dvi"
      xrandr --output "$src" --auto --primary --output "$out" --auto --left-of "$src" --panning 0x0 --set "Broadcast RGB" "Automatic" --set audio force-dvi
    else
      echo "xrandr --output $src --auto --primary --output $out --auto --right-of $src --panning 0x0 --set \"Broadcast RGB\" \"Automatic\" --set audio force-dvi"
      xrandr --output "$src" --auto --primary --output "$out" --auto --right-of "$src" --panning 0x0 --set "Broadcast RGB" "Automatic" --set audio force-dvi
    fi
    left=$((1 - $left))
  done
}

Function screenconnect sets another detected screen as the secondary left or right screen. The first optional command line argument must be either l (default) or r and sets the detected external screen as the secondary left or right monitor, respectively. The second optional argument allows for specifying the identifier of the primary monitor and defaults to eDP1.

Both functions specify the command line argument --set audio force-dvi which was necessary during my tests to fix overscan issues with TVs. The drawback of --set audio force-dvi is that only the video signal but not an audio signal is transmitted to the external device and therefore you cannot use its speakers.

If you want to play a video on an external screen, you could use screenconnect to run the external screen at its optimal resolution and play the video in full screen mode. However, this approach wastes CPU power by scaling the video to full screen. A more efficient way is to play the video at its native resolution and set the resolution of the external screen to match the resolution of the video. A blog post by SF-Xpt explains the details. For example, to play a video with a resolution of 640×360 on an external monitor named HDMI2 at a resolution of 640x480 (executing xrandr without arguments shows all supported resolutions of the screens and the screen names), while preserving the resolution of 1920x1080 of the main screen, run the following command:

xrandr --output HDMI2 --mode 640x480 --panning 1920x1080 --set audio dvi-on