76

is there a tool which enables one to:

  • remember current RandR configuration (position, orientation, resolution etc) on per-monitor basis,
  • automatically apply last known good configuration as soon as the display is plugged in, with no need to muck around with applets or xrandr(1)?

The configurations would have to be applied on a per-user, per-display basis.

If there is no such tool in the wild, I'd like to throw together one myself, but as far as I can see, there's no way to tell that a monitor has been plugged in. Do I have to poll with xrandr -q once in a while to figure out that an output was connected or disconnected, or is there a more efficient way to do it? Can udev be tuned to do just that?

2
  • Are you sure there is no way to tell that a monitor has been plugged in with your card. Try running udevadm monitor --property from the terminal and plugging the monitor in. With my card, I see an event when it is plugged in. You may be able to use a +RUN rule in udev and some bash scripting to get what you want. However, I'm not sure how you would be able to implement this on a per user basis using udev.
    – Steven D
    Dec 3, 2010 at 1:58
  • @Steven: Your comment should be an answer. It's not a complete answer, but it does make significant progress. If udev does see a hardware event, it should notify hal which sends a dbus event which can be seen by user code. Dec 3, 2010 at 19:39

11 Answers 11

31

Regarding a tool which can store monitor configuration profiles on a per-user and per-display basis, autorandr will do exactly that.

My laptop has an NVIDIA card, so I use the disper backend instead of xrandr. Autorandr will use disper as the backend to manage your monitors if you call it as autodisper. For the rest of this post though, I'll refer to it as autorandr for consistency.

You can save profiles with autorandr --save profile_name. Running autorandr by itself will then give you a list of profiles, and identify which one is detected as the current configuration.

For instance:

$ autorandr
laptop
syncmaster19 (detected)

You can tell it to automatically load the appropriate profile for the current configuration with autorandr --change. This command, paired with a udev rule to run it when it is hotplugged, would do what you requested.

As an added precaution I've appended --default laptop to that command, which will make it default to the laptop's display if there is no saved profile that matches the current configuration. So the full command I use to switch displays is:

autorandr --change --default laptop

Unfortunately my machine doesn't give any udev output when I hotplug my monitor. I'm using the NVIDIA proprietary drivers, so that isn't surprising. So I have bound it to the XF68Display key (Fn-F8) for now, which is almost as good.

4
  • 1
    autorandr was a waste of time, bad documented, random monitor blinks, no install/uninstall instructions. Could solve it using udev Jan 26, 2014 at 15:38
  • 15
    Just in case, there's a nice maintained rewritten-in-Python fork of autorandr: github.com/phillipberndt/autorandr
    – Alec Mev
    Nov 17, 2017 at 13:14
  • 2
    autorandr is also nicely packaged in Ubuntu and takes care of all udev and xdg configuration. All you need to do is create your profiles as per README and it will auto-switch. Note that version 1.4 (Ubuntu 18) was not full featured. I'm using 1.8 from here and it works great.
    – Akom
    Apr 20, 2020 at 17:40
  • After getting my profiles setup and correctly detected, I needed to enable and start the autorandr systemd service to automatically run when changes were detected.
    – Tom Hale
    Jun 28, 2020 at 5:55
28
+50

I'm using this simple (homemade) script that keeps polling RandR and switches between LVDS1 and VGA1 when VGA gets connected/disconnected. (For HDMI outputs, in the following script file, change all the VGA1 to HDMI1)

It's a dirty solution, yet it's working just fine.

It's customized for my setup: you'll most likely need to change RandR output names (LVDS1 and VGA1) and unlike me you'll probably be fine with your RandR default mode for VGA.

#!/bin/bash

# setting up new mode for my VGA
xrandr --newmode "1920x1080" 148.5 1920 2008 2052 2200 1080 1089 1095 1125 +hsync +vsync
xrandr --addmode VGA1 1920x1080

# default monitor is LVDS1
MONITOR=LVDS1

# functions to switch from LVDS1 to VGA and vice versa
function ActivateVGA {
    echo "Switching to VGA1"
    xrandr --output VGA1 --mode 1920x1080 --dpi 160 --output LVDS1 --off
    MONITOR=VGA1
}
function DeactivateVGA {
    echo "Switching to LVDS1"
    xrandr --output VGA1 --off --output LVDS1 --auto
    MONITOR=LVDS1
}

# functions to check if VGA is connected and in use
function VGAActive {
    [ $MONITOR = "VGA1" ]
}
function VGAConnected {
    ! xrandr | grep "^VGA1" | grep disconnected
}

# actual script
while true
do
    if ! VGAActive && VGAConnected
    then
        ActivateVGA
    fi

    if VGAActive && ! VGAConnected
    then
        DeactivateVGA
    fi

    sleep 1s
done

Full Steps:

  1. Put above script (homemadeMonitor.sh) into you preferred directory

  2. Make the .sh file executable by typing the following command in the terminal

    chmod +x homemadeMonitor.sh

  3. Run the .sh file

    ./homemadeMonitor.sh

3
  • Good script! Thanks, I needed something like this. I just copied it and set it up to run automatically. Thanks!
    – Linuxios
    Jan 31, 2012 at 23:20
  • > It's a dirty solution, yet it's working just fine. Those are the most persistent. :)
    – Suuuehgi
    May 12, 2020 at 8:39
  • I was using a solution like this for a while but found it conflicts with picom performance in an inexplicable way. In awesomewm and i3 it caused window movement to be not-so-smooth.
    – SoyBison
    Oct 4, 2022 at 22:11
24

Responding to the "[a] way to tell that a monitor has been plugged in" part of the question:

The support still varies quite a bit, but with recent kernels there is some support for generating udev events when a display hotplug occurs. With kernel 2.6.38 and ATI X1400 hardware, I get an event the first time I connect a VGA display but no events on subsequent disconnects or reconnects of the display. Intel hardware may have better support. The NVIDIA proprietary driver does not currently support KMS; I haven't tried looking for hotplug events on NVIDIA hardware, but I'm doubtful it would work.

If you want to experiment with udev you could try the following steps:

  • update to the newest kernel
  • make sure that kernel mode setting (KMS) is enabled. If enabled, it should be reported in your kernel output. Mine says [drm] radeon kernel modesetting enabled and [drm] initializing kernel modesetting
  • run udevadm monitor --property and see if events are reported when you (dis-)connect displays

If you are getting udev events on display hotplug, you can trigger a script with a udev rule like:

ACTION=="change", SUBSYSTEM=="drm", HOTPLUG=="1", RUN+="/path/to/hotplug.sh"

Note: This will not work if you use an nVIDIA GPU with the proprietary binary driver, since it does not use KMS. You will not get any udev events.

1
  • Proprietary Nvidia works for me
    – smac89
    Jan 5, 2021 at 5:50
9

If you must have it automatically detect the display when it is plugged in, it seems that the cleanest solution is to add a UDEV rule to run a script containing your xrandr commands. Here is an example from one user's solution1:

Monitor (output) UDEV events with udevadm
This step will be most important for each user. Run udevadm monitor --environment --udev. Then connect your HDMI cable.

UDEV rule
Based on the output from the above command, the user created this UDEV rule at /etc/udev/rules.d/95-monitor-hotplug.rules.

KERNEL=="card0", SUBSYSTEM=="drm", ENV{DISPLAY}=":0", ENV{XAUTHORITY}="/home/dan/.Xauthority", RUN+="/usr/local/bin/hotplug_monitor.sh"

Note the environment variables used so xrandr will be run under the user profile.

xrandr script hotplug_monitor.sh

You may want to adjust the xrandr options to suit your needs.

#! /usr/bin/bash

export DISPLAY=:0
export XAUTHORITY=/home/dan/.Xauthority

function connect(){
    xrandr --output HDMI1 --right-of LVDS1 --preferred --primary --output LVDS1 --preferred 
}

function disconnect(){
      xrandr --output HDMI1 --off
}

xrandr | grep "HDMI1 connected" &> /dev/null && connect || disconnect
3
  • 1
    the XAUTHORITY is necessary! Triggering with ACTION="change" was not working for me.
    – poleguy
    Aug 25, 2021 at 22:26
  • 1
    Note regarding the udev rule : make sure the script being run is editable only by root or this is a security issue that would allow the user to perform privilege escalation
    – ShellCode
    Jan 10, 2022 at 13:04
  • In my case, the XAuthority variable did not work when set to ~/.XAuthority. Had to set it to /run/user/1000/gdm/Xauthority Feb 2, 2022 at 18:04
8

I used the udev rule suggested above along with a shell script that looks like

#!/bin/sh

dmode="$(cat /sys/class/drm/card0-VGA-1/status)"
export DISPLAY=:0
export XAUTHORITY=/home/yourusername/.Xauthority

if [ "${dmode}" = disconnected ]; then
     /usr/bin/xrandr --auto
elif [ "${dmode}" = connected ];then
     /usr/bin/xrandr --output VGA1 --auto --right-of LVDS1
else /usr/bin/xrandr --auto
fi

The Xauthority part is essential as well as the export DISPLAY, you can use echo $DISPLAY to see which number it's equal to. Use xrandr -q to see the external monitors you have. The last else statement is just to make sure that you're never stuck without a display.

6

A small program that waits for the X server to notify it about changed monitor configuration, and then executes a given command (e.g. the autorandr mentioned in another answer) is available at: https://bitbucket.org/portix/srandrd/overview

This seems to be a cleaner solution that using udev (where you have to worry about finding the right X server etc.)

3

For those who, for whatever reason, don't want to take the hotplug route, it is still possible to not poll within a script using inotifywait:

#!/bin/bash

SCREEN_LEFT=DP2
SCREEN_RIGHT=eDP1
START_DELAY=5

renice +19 $$ >/dev/null

sleep $START_DELAY

OLD_DUAL="dummy"

while [ 1 ]; do
    DUAL=$(cat /sys/class/drm/card0-DP-2/status)

    if [ "$OLD_DUAL" != "$DUAL" ]; then
        if [ "$DUAL" == "connected" ]; then
            echo 'Dual monitor setup'
            xrandr --output $SCREEN_LEFT --auto --rotate normal --pos 0x0 --output $SCREEN_RIGHT --auto --rotate normal --below $SCREEN_LEFT
        else
            echo 'Single monitor setup'
            xrandr --auto
        fi

        OLD_DUAL="$DUAL"
    fi

    inotifywait -q -e close /sys/class/drm/card0-DP-2/status >/dev/null
done

Polling with xrandr gave serious usability issues on my brand new laptop (mouse would stall periodically).

1

When using peoro's solution, I got some extra output from xrandr, so I used the -c option for grep which counts the number of matches. I adapted it for HDMI and added audio switch as well:

#!/bin/bash
# adapted from http://unix.stackexchange.com/questions/4489/

# default monitor is LVDS1
MONITOR=LVDS1

# functions to switch from LVDS1 to HDMI1
function ActivateHDMI {
    xrandr --output HDMI1 --mode 1920x1080 --dpi 160 --output LVDS1 --off
    pactl set-card-profile 0 output:hdmi-stereo-extra1
    MONITOR=HDMI1
}
function DeactivateHDMI {
    xrandr --output HDMI1 --off --output LVDS1 --auto
    pactl set-card-profile 0 output:analog-stereo
    MONITOR=LVDS1
}

# functions to check if HDMI is connected and in use
function HDMIActive {
    [ $MONITOR = "HDMI1" ]
}
function HDMIConnected {
    [[ `xrandr | grep "^HDMI1" | grep -c disconnected` -eq 0 ]]
}

# actual script
while true
do
    if ! HDMIActive && HDMIConnected
    then
        ActivateHDMI
    fi

    if HDMIActive && ! HDMIConnected
    then
        DeactivateHDMI
    fi

    sleep 1s
done
1

Editing my answer to make it simpler to setup.

Install autorandr to automate screen layout in xrandr, grab latest .deb from https://github.com/nalipaz/autorandr/releases and run:

dpkg -i [file]

Setup autorandr with stored screen layouts

autorandr --save [docked|mobile|home|etc]

As an example, on my laptop without any monitors plugged in, I setup arandr the way I want it, then ran:

autorandr --save mobile

Then plugged in my hdmi and reconfigured arandr, then ran:

autorandr --save docked

After setting up each of your layouts you can run (replace 'mobile' with previous favored name, I used mobile):

autorandr --default mobile

Now that autorandr is all setup you can automate the process by installing a package which will poll for connected displays and run autorandr --change upon detection. Yes, I know about udev and you can set that up if it works for you, but in my testing udev didn't work for my hdmi consistently. It only worked 1 out of every 20 plugs/unplugs or so, and sometimes it just stopped altogether.

Download the latest .deb from here: https://github.com/nalipaz/poll-xrandr/releases/ then install with

dpkg -i [file]

It is most likely necessary that you run more tasks upon completion of autorandr --change, autorandr allows for these commands to be inserted into a file called ~/.autorandr/postswitch. Do as follows:

cd ~/.autorandr/ && touch postswitch && chmod +x postswitch

Now edit the postswitch file to have something similar to the following:

#!/bin/bash
if pidof conky > /dev/null 2>&1; then
  killall conky
fi
(sleep 2s && xrandr-adjust-brightness restore -q) &
(sleep 2s && nitrogen --restore) &
(sleep 3s && conky -q) &

Additionally you will likely want to add autorandr and poll-xrandr to your startups, something like the following two commands:

autorandr --change &
poll-xrandr &

Unplug or plug in a monitor and watch the magic!

1

I guess people looking at this question are not those that want to use GNOME, and those that use GNOME wouldn’t have to look at this question, but in the interest of completeness:

GNOME has this functionality built-in. If you change the setup via gnome-control-center, gnome-settings-daemon remembers it (in .config/monitors.xml) and automatically applies it when a monitor is plugged in or out.

Sadly the configuration in monitors.xml file is not well documented. See question here. The gnome display tooling also has no means of setting up panning, scaling per monitor and it doesn't do zoom-out. As many people are using a HiDPI laptop screen together with a regular DPI screen the tools are not enough to get a workable solution.

I have seen this mentioned as a roadmap item for Wayland though so when it finally comes we might see a fix for the issues mentioned. Till then on Gnome I just run a startup script after a delay to setup xrandr manually.

1

To simplify in Xfce for example please install "arandr" which should be available in your distro packages. Configure your display using "arandr" then save it as "displayLayout" for example. Arandr should add for you ".sh" extension so it can be directly used in "function connect()" as written in the script "hotplug_monitor.sh" mentioned by iyrin above, for example:

function connect(){
 /path/to/displayLayout.sh
}

To use disconnect mechanism add at the top of the script just below "#!/bin/bash" like so:

#!/bin/bash
#
PLUGGED_EXTERNAL=`xrandr | awk /"connected [0-9]"/'{print $1}'`

xrandr will detect the display and pass output to awk which will filter it based on regular expression "connected" followed by a digit which in essence is what we want. In my opinion it is not necessary to match exact resolution in regular expression because primary display is reported as "connected primary" followed by number i.e. resolution. Then awk "print $1" will print only first column which is the name of external monitor in this case and this will be stored in PLUGGED_EXTERNAL variable. Next instead of fixed display name "HDMI1" in the script $PLUGGED_EXTERNAL can be used like so:

function disconnect(){
  xrandr --output $PLUGGED_EXTERNAL --off
}

xrandr | grep "$PLUGGED_EXTERNAL connected" &> /dev/null && connect || disconnect

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .