如何检测显示器何时插入或拔出?

如何检测显示器何时插入或拔出?

有没有任何当我将外部显示器插入或拔出笔记本电脑的 DisplayPort 时会触发事件吗? ACPID 和 UDEV 根本没有反应。

我正在使用英特尔芯片上的板载显卡。这里类似的讨论已经有几年了。

我不想使用轮询,但我需要进行一些配置,根据显示器是否连接自动设置显示设置。

答案1

笔记: 这是在配备 i915 驱动显卡的笔记本电脑上进行测试的。


背景

笔记:当插入新屏幕时,不会向主机发送任何事件,即使在我上次编辑之后也是如此。所以唯一的办法就是使用轮询。试图让他们尽可能高效......

编辑#3

最后有一个更好的解决方案(通过 ACPI):

仍然没有事件,但 ACPI 似乎比xrandr查询更有效率。 (注:这需要已加载 ACPI 内核模块,但不需要 root 权限)。

我的最终解决方案(使用bash):

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

现在测试一下:

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

它已插入,所以现在我拔掉它:

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

笔记: ${1:+*-1+1}允许一个布尔值论证:如果有东西存在,答案将颠倒:( crtState >> 4 ) * -1 + 1

和最终脚本:

#!/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

警告: 比 轻xrandr,但并非不重要延迟小于0.02几秒钟后,Bash 脚本将转到资源消耗者进程的顶部 ( top)!

虽然这需要约 0.001 秒:

$ time read -a </proc/stat crtStat

这需要 ~0.030 秒:

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

这可大了!因此,根据您的需要,delay可以在0.5和之间合理设置2

编辑#2

我终于找到了一些东西,使用这个:

重要免责声明:玩弄/proc/sys条目可能会破坏你的系统!因此,请勿在生产系统上尝试以下操作。

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

...清理不需要的条目后:

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

我已经能够读到这个:

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

当我插入、拔出并重新插入显示器电缆时。

原答案

当查询配置时(运行system/preferences/monitorxrandr),显卡会执行以下操作扫描,因此运行xrandr -q会给您提供信息,但您必须轮询状态。

我扫描了所有日志(内核、守护进程、X 等)并通过/proc&进行搜索/sys,显然似乎不存在满足您的请求的内容。

我也尝试过这个:

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

毕竟,如果您System/Preferences/Monitor在没有插入或拔出新屏幕的情况下运行,该工具将简单地显示(通常)。但是,如果您以前插过或拔过屏幕,有时您会运行此工具,并且您会看到桌面上出现一种重置或者刷新(如果你跑的话也是一样xrandr)。

这似乎证实了该工具要求xrandr(或以相同的方式工作)通过从运行时开始定​​期轮询状态。

你可以自己尝试一下:

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

这将显示连接了多少个屏幕(显示器),持续 10 秒。

当它运行时,插入和/或拔掉你的屏幕/显示器,看看会发生什么。所以你可以创建一个小的 Bash 测试函数:

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

其用途如下:

$ if isVgaConnected; then echo yes; fi

但要小心,xrandr需要大约0.140秒至0.200秒虽然插头上没有发生任何变化,但最多0.700秒每当之前插入或拔出某些东西时(笔记:看来不是资源吞噬者)。

编辑#1

为了确保我没有教错东西,我在网络和文档中进行了搜索,但没有找到任何关于DBus 和屏幕

最后,我在两个不同的窗口中运行dbus-monitor --system(我也一直在使用选项)和我编写的小脚本:

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

...再次插入,而不是拔掉显示器的插头,很多次。所以现在我可以说:

  • 在此配置中,使用 i915 驱动程序,除了运行之外没有其他方法xrandr -q可以知道显示器是否已插入。

但要小心,因为似乎没有其他方法。例如,xrandr似乎共享此信息,因此我的 GNOME 桌面会xinerama自动切换到...当我跑的时候xrandr

一些文档

答案2

以下几行出现在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)

将显示器连接到 VGA 连接器时。所以可能有一种方法可以解决这个问题。

答案3

对于那些出于某种原因不想采用热插拔路线的人,仍然可以使用 inotifywait 在脚本中不进行轮询:

#!/bin/bash

SCREEN_LEFT=DP2
SCREEN_RIGHT=eDP1
START_DELAY=5

雷尼斯 +19 $$ >/dev/null

睡眠 $START_DELAY

OLD_DUAL="虚拟"

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

    if [ "$OLD_DUAL" != "$DUAL" ];然后
        if [ "$DUAL" == "已连接" ];然后
            echo '双显示器设置'
            xrandr --输出 $SCREEN_LEFT --auto --旋转正常 --pos 0x0 --输出 $SCREEN_RIGHT --auto --旋转正常 --低于 $SCREEN_LEFT
        别的
            echo '单显示器设置'
            xrandr--自动

        OLD_DUAL=“$DUAL”

    inotifywait -q -e 关闭 /sys/class/drm/card0-DP-2/status >/dev/null
完毕

最好从 .xsessionrc 调用它,不要忘记结尾 &。使用 xrandr 进行轮询在我的全新笔记本电脑上出现了严重的可用性问题(鼠标会定期停止)。

答案4

我使用 bash、gawk、xrandr 和 xev 编写了这个非常好的脚本。它非常干净,因为它不会与任何系统设备混在一起,也不需要对此类设备进行广泛的了解,而是仅依赖于使用xevrandr来检测连接的监视器。 awk 脚本只是用来解析并选择正确的事件。

笔记:需要 xev v1.2.4 或更高版本

建议始终与其他外部显示器连接的笔记本电脑用户使用。

#!/bin/bash

function monitor_xevents {
    local connected_monitors=()
    local monitor

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

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

您可以将其作为 systemd 运行用户服务:

[Unit]
Description="Monitor hotplug notifier"

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

[Install]
WantedBy=default.target

启动时,它首先告诉您所有已连接的显示器,然后当您继续使用它时,它会告诉您显示器何时连接或断开连接。

输出示例:

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
...

由于我使用笔记本电脑,eDP-1(嵌入式显示端口 1)基本上是我的笔记本电脑,因此它始终不会断开连接。

您可以扩展它以调用其他脚本等。这只是一个 POC,适合那些不想开始编写与上述内容等效的 C/C++ 的人。

相关内容