58

Is there any event that is triggered when I plug in or out an external monitor into the DisplayPort of my laptop? ACPID and UDEV don't react at all.

I'm using onboard graphics on an intel chip. Here is a similar discussion which is already a couple of years old.

I don't want to use polling but I need to have some configuration that set's the display settings automatically depending on if the display is connected.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
janoliver
  • 1,776
  • 4
    It can be done with udev. What is your kernel version? Are you using KMS (kernel mode setting)? – Andy May 25 '11 at 16:00
  • Thanks for the answer. I am not sure about KMS, but as I said in the question, udev doesn't send any events. (udevadm monitor --property does not react at all) – janoliver May 25 '11 at 16:27
  • @Andy: the last time this came up, it seemed that most systems required polling. If you've found a way to trigger a udev event, could you answer that question? – Gilles 'SO- stop being evil' May 25 '11 at 23:03
  • @Gilles I added an answer to that question. The support varies, apologies if "can be done with udev" in my earlier comment gives somebody false hope. janoliver, if you are comfortable upgrading your kernel, it might be worth an experiment (or look for a live CD). You may want to update the question with exact hardware and kernel versions for others' reference. – Andy May 27 '11 at 16:29
  • @Andy: I have the newest kernel version (2.6.38) and intel graphics. (Arrandale i3000 chip). I am still not sure where to look for the kms thingy, but I guess it should be loaded. However, udev still doesn't give me anything, so I might have to leave it like this... – janoliver May 27 '11 at 17:34
  • Some additional info: Yesterday I booted the gparted live cd and while gparted was working on something, I, out of curiosity, tried a "udevadm monitor" there - and saw some events when the monitor was plugged in and out. It was the same kernel version as I have. So, it's not a hardware thing and HAS to be possible to reach in my Arch as well... – janoliver May 29 '11 at 10:54
  • 1
    I finally got it running loading i915 as kernel module. – janoliver May 29 '11 at 12:13
  • 3
    You can use xrandr or disper to detect if external monitor has been plugged in. https://github.com/wertarbyte/autorandr can show you how to use them. But xrandr/disper might not supported your video card. – number5 Dec 05 '11 at 10:33
  • Depending on what you are trying to do, there may be a signal/method in dbus which will allow you to detect or set the properties of the external monitor. – James Tocknell May 30 '12 at 13:13

8 Answers8

19

NOTE: This was tested on a laptop with a i915 driven graphics card.


Background

NOTE: When a new screen is plugged in, no event is sent to the host, this stayed true even after my last edit. So the only way is to use polling. Trying to make them as effiicient as possible...

EDIT #3

Finally there's one better solution (through ACPI):

There's still no event, but ACPI seems more efficient than xrandr to inquire. (Nota: This requires ACPI kernel modules loaded, but doesn't require root privileges).

My final solution (using bash):

isVgaConnected() {
    local crtState
    read -a < /proc/acpi/video/VID/CRT0/state crtState
    test $(( ( ${crtState[1]} >>4 ) ${1:+*-1+1} )) -ne 0
}

Now a test:

$ if isVgaConnected; then echo yes; else echo no; fi 
yes

It's plugged in, so now I unplug it:

$ if isVgaConnected; then echo yes; else echo no; fi 
no

NOTE: ${1:+*-1+1} permit a boolean argument: If something is present, answer would be inverted: ( crtState >> 4 ) * -1 + 1.

and the final script:

#!/bin/bash

export crtProcEntry=/proc/acpi/video/VID/CRT0/state

isVgaConnected() {
    local crtState
    read -a < $crtProcEntry crtState
    test $(( ( ${crtState[1]} >>4 ) ${1:+*-1+1} )) -ne 0
}

delay=.1
unset switch
isVgaConnected || switch=not
while :;do
    while isVgaConnected $switch;do
        sleep $delay
      done
    if [ "$switch" ];then
        unset switch
        echo VGA IS connected
        # doing something while VGA is connected
      else
        switch=not
        echo VGA is NOT connected.
        # doing something else, maybe.
      fi
  done

WARNINGS: lighter than xrandr, but not unimportant with a delay smaller than 0.02 seconds, the Bash script will go to the top of resource eaters process (top)!

While this costs ~0.001 sec:

$ time read -a </proc/stat crtStat

This requires ~0.030 sec:

$ read -a < /proc/acpi/video/VID/CRT0/state crtState

This is big! So depending on what you need, delay could be reasonably set between 0.5 and 2.

EDIT #2

I've finally found something, using this:

Important disclaimer: Playing with /proc and /sys entries could break your system!!! So don't try the following on production systems.

mapfile watchFileList < <(
    find /sys /proc -type f 2>/dev/null |
    grep -i acpi\\\|i91 
)

prompt=("/" "|" '\' '-');

l=0
while :; do
  mapfile watchStat < <(
    grep -H . ${watchFileList[@]} 2>/dev/null
  )

  for ((i=0;i<=${#watchStat[@]};i++)); do
    [ "${watchStat[i]}" == "${oldStat[i]}" ] || echo ${watchStat[i]}
  done

  oldStat=("${watchStat[@]}")
  sleep .5
  printf "\r%s\r" ${prompt[l++]}
  [ $l -eq 4 ]&&l=0
done

... after some cleaning of unwanted entrys:

for ((i=0;i<=${#watchFileList[@]};i++)); do
  [[ "${watchFileList[$i]}" =~ /sys/firmware/acpi/interrupts/sci ]] &&
      unset watchFileList[$i] && echo $i
done

I've been able to read this:

/proc/acpi/video/VID/CRT0/state:state: 0x1d
/proc/acpi/video/VID/CRT0/state:state: 0x0d
/proc/acpi/video/VID/CRT0/state:state: 0x1d

When I plug, unplug, and replug in monitor cable.

Original Answer

When the config is inquired (running system/preferences/monitor or xrandr), graphics cards do a type of scan, so running xrandr -q give you the info, but you have to poll the status.

I've scanned all logs, (kernel, daemon, X and so forth) searching through /proc & /sys, and clearly nothing seems to exist that satisfies your request.

I've tried this too:

export spc50="$(printf "%50s" "")"
watch -n1  '
    find /proc/acpi/video -type f |
        xargs grep -H . |
        sed "s/^\([^:]*):/\1'$spc50'}:/;
             s/^\(.\{50\}\) *:/\1 /"'

After all that, if you run System/Preferences/Monitor while no new screen has just been plugged in, nor unplugged, the tool will appear simply (normally). But if you've plugged or unplugged a screen before, at times you'll run this tool and you'll see your desktop make a type of reset or refresh (same if you run xrandr).

This seems to confirm that this tool asks for xrandr (or works in the same manner) by polling status periodically, starting at the time it's run.

You could try yourself:

$ for ((i=10;i--;)); do xrandr -q | grep ' connected' | wc -l; sleep 1; done
1
1
1
2
2
2
1
1
1
1

This will display how many screens (displays) are connected, for 10 seconds.

While this runs, plug and/or unplug your screen/monitor and look what's happens. So you could create a little Bash test function:

isVgaConnected() {
    local xRandr=$(xrandr -q)
    [ "$xRandr" == "${xRandr#*VGA1 con}" ] || return 0
    return 1
}

which would be useable as in:

$ if isVgaConnected; then echo yes; fi

But be careful, xrandr takes about 0.140 sec to 0.200 sec while no change happens on plugs and up to 0.700 seconds whenever something was plugged or unplugged just before (NOTE: It seems not to be a resource eater).

EDIT #1

To ensure I'm not teaching something incorrect, I've searched around the Web and docs, but didn't find anything about DBus and Screens.

Finally, I've run in two different windows dbus-monitor --system (I've been playing with options too) and the little script I wrote:

$ for ((i=1000;i--;)); do isVgaConnected && echo yes || echo no; sleep .5; done

... and again plugged, than unplugged the monitor, many times. So now I could say:

  • In this configuration, using i915 driver, there is no other way than running xrandr -q to know if a monitor is plugged in or not.

But use caution, because there doesn't appear to be other ways. For instance, xrandr seems to share this info, so my GNOME desktop would switch to xinerama automatically... when I ran xrandr.

Some docs

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
8

The following lines appeared in udevadm monitor

KERNEL[46578.184280] change   /devices/pci0000:00/0000:00:02.0/drm/card0 (drm)
UDEV  [46578.195887] change   /devices/pci0000:00/0000:00:02.0/drm/card0 (drm)

when attaching a monitor to the VGA-Connector. So there might be a way to figure this out.

  • With an nVidia 9800 GT and proprietary drivers, udevadm monitor shows nothing when I connect an HDMI monitor. What hardware/drivers are you using? – frankster Sep 08 '14 at 08:30
  • Pityingly, that does not work reliable for me. Sometimes I get these event messages when I plug-in my monitor and sometimes not. – Tobias Jul 07 '17 at 04:24
7

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

It is best invoked from your .xsessionrc, not forgetting the ending &. Polling with xrandr gave serious usability issues on my brand new laptop (mouse would stall periodically).

Balzola
  • 201
  • I wouldn't have thought that you can use inotify on /proc and just doing inotifywait -q -e close /sys/class/drm/card0-DP-2/status indeed did not end upon disconnecting DP-2 on my system – nhed Jan 16 '18 at 23:42
5

I stuck to using srandrd. It monitors X events and triggers your script when a display gets connected or disconnected.

scorpp
  • 181
  • 1
  • 2
2

I wrote this really nice script using bash, gawk, xrandr, and xev. It's quite clean in that it doesn't muck around with any system devices nor does it require extensive knowledge of such devices, instead relies solely on using xev, and randr to detect connected monitors. The awk script is just there to parse and pick the correct events.

Note: Requires xev v1.2.4 or higher

Recommended for laptops users who are always interfacing with other external monitors.

#!/bin/bash

function monitor_xevents { local connected_monitors=() local monitor

for monitor in $(xrandr --listactivemonitors | awk '/^\s+[0-9]+:/ {print $4}'); do
    connected_monitors+=(&quot;$monitor&quot;)
done

xev -root -event randr -1 | stdbuf --output=L gawk --sandbox \
    --source &quot;BEGIN{$(printf -- 'monitors[&quot;%s&quot;]=1\n' ${connected_monitors[@]})}&quot; \
    --source 'BEGIN {
        pat=@/output (.[^,]*),.*connection RR_(\w+),/
        for (monitor in monitors)
            print monitor, &quot;connected&quot;
    }
    !/crtc None/ &amp;&amp; match ($0, pat, s) {
        # Newly discovered monitor at runtime
        if (!(s[1] in monitors)) {
            monitors[s[1]] = 1
            print s[1], &quot;connected&quot;
            next
        }
        switch (s[2]) {
            case &quot;Connected&quot;:
                if (!monitors[s[1]])
                    monitors[s[1]] = 1
                else next
                break
            case &quot;Disconnected&quot;:
                if (monitors[s[1]])
                    monitors[s[1]] = 0
                else next
                break
        }
        print s[1], tolower(s[2])
    }'

}

while read output status; do printf "$output was $status\n" done < <(monitor_xevents)

You can run it as a systemd user service:

[Unit]
Description="Monitor hotplug notifier"

[Service] ExecStart='/home/chigozirim/Dev/mon.sh' Restart=on-failure RestartSec=5s RemainAfterExit=yes

[Install] WantedBy=default.target

On startup, it first tells you all the connected monitors, then as you continue using it, it will tell you when a monitor is connected, or disconnected.

Example output:

Jan 06 01:48:18 ArcoB mon.sh[495478]: eDP-1 was connected
Jan 06 01:48:18 ArcoB mon.sh[495478]: HDMI-1-0 was connected
Jan 06 18:51:13 ArcoB mon.sh[495478]: HDMI-1-0 was disconnected
Jan 06 20:29:19 ArcoB mon.sh[495478]: HDMI-1-0 was connected
Jan 07 12:47:23 ArcoB mon.sh[495478]: HDMI-1-0 was disconnected
...

Since I use a laptop, eDP-1 (Embedded Display Port 1) is basically my laptop, so it is never disconnected throughout.

You can extend it to call other scripts, etc. This was just a POC for anyone not looking to start writing the C/C++ equivalent of the above.

smac89
  • 1,443
  • the -1 argument for xev doesnt seem to be something standard, how do you make the output of xev single-lined so that the rest of the script works? – para Oct 18 '21 at 09:44
  • 1
    @para what version of xev do you have? I was using version 1.2.4 in this example, and that version contains the -1 option, which changes the output to a single line – smac89 Oct 18 '21 at 16:25
  • i have version 1.2.3, time to update! thanks ^_^ – para Oct 19 '21 at 10:13
0

Obviously there should be something! :) /sys filesystem tells userspace what hardware is available, so userspace tools (such as udev or mdev) can dynamically populate a "/dev" directory with device nodes representing the currently available hardware. Linux provides two hotplug interfaces: /sbin/hotplug and netlink.

There is a small C demo in the following file. http://www.kernel.org/doc/pending/hotplug.txt

roncsak
  • 299
0

Mostly system/application software on Linux today used some ipc techniques for communicating with each other. D-Bus is now mostly used with GNOME applications, and might help.

Linux Journal:

D-BUS can facilitate sending events, or signals, through the system, allowing different components in the system to communicate and ultimately to integrate better. For example, a Bluetooth dæmon can send an incoming call signal that your music player can intercept, muting the volume until the call ends.

wiki:

D-Bus supplies both a system daemon (for events such as "new hardware device added" or "printer queue changed") and a per-user-login-session daemon (for general inter-process communication needs among user applications)

There is even a Python library for this, and ubuntu recently used this ability which called "zeitgeist".

tshepang
  • 65,642
-6

Graphically you can see if the monitor is recognized with Monitor, I know that you can find this on Ubuntu, Fedora and others in this(or a similar) location.

System/Preferences/Monitor

And you can turn-on/off any monitor you want or use both at same time with duplicate image in both monitor or independent monitors

Malachi
  • 105