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

jan*_*ver 56 udev acpi multi-monitor monitors

是否有任何当我插上或缩小外部监视器到我的笔记本电脑的DisplayPort的所触发的事件?ACPID 和 UDEV 根本没有反应。

我在英特尔芯片上使用板载图形。是一个类似的讨论,已经有几年了。

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

F. *_*uri 16

注意: 这是在带有 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
}
Run Code Online (Sandbox Code Playgroud)

现在进行测试:

$ if isVgaConnected; then echo yes; else echo no; fi 
yes
Run Code Online (Sandbox Code Playgroud)

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

$ if isVgaConnected; then echo yes; else echo no; fi 
no
Run Code Online (Sandbox Code Playgroud)

注意: ${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
Run Code Online (Sandbox Code Playgroud)

警告: 比 轻xrandr,但延迟小于0.02并非不重要,Bash 脚本将进入资源吞噬进程 ( top)的顶部!

虽然这需要大约 0.001 秒:

$ time read -a </proc/stat crtStat
Run Code Online (Sandbox Code Playgroud)

这需要 ~0.030 秒:

$ read -a < /proc/acpi/video/VID/CRT0/state crtState
Run Code Online (Sandbox Code Playgroud)

这很大!因此,根据您的需要,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
Run Code Online (Sandbox Code Playgroud)

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

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

我已经能够阅读这个:

/proc/acpi/video/VID/CRT0/state:state: 0x1d
/proc/acpi/video/VID/CRT0/state:state: 0x0d
/proc/acpi/video/VID/CRT0/state:state: 0x1d
Run Code Online (Sandbox Code Playgroud)

当我插入、拔下和重新插入显示器电缆时。

原答案

当查询配置(正在运行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 /"'
Run Code Online (Sandbox Code Playgroud)

毕竟,如果您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
Run Code Online (Sandbox Code Playgroud)

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

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

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

这将是可用的,如:

$ if isVgaConnected; then echo yes; fi
Run Code Online (Sandbox Code Playgroud)

但要小心,xrandr大约需要0.140 秒到 0.200 秒,而插头没有任何变化,并且在之前插入或拔出某些东西时最多需要0.700 秒注意:它似乎不是资源消耗者)。

编辑#1

为了确保我没有教错东西,我搜索了 Web 和文档,但没有找到关于DBus 和 Screens 的任何信息。

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

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

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

  • 在此配置中,使用 i915 驱动程序,除了运行xrandr -q以了解监视器是否已插入之外别无他法。

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

一些文档


seb*_*ner 7

以下几行出现在 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)
Run Code Online (Sandbox Code Playgroud)

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


小智 6

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

#!/bin/bash

SCREEN_LEFT=DP2
SCREEN_RIGHT=eDP1
START_DELAY=5

renice +19 $$ >/dev/null

睡眠 $START_DELAY

OLD_DUAL="虚拟"

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

    if [ "$OLD_DUAL" != "$DUAL" ]; 然后
        if [ "$DUAL" == "connected" ]; 然后
            echo '双显示器设置'
            xrandr --output $SCREEN_LEFT --auto --rotate normal --pos 0x0 --output $SCREEN_RIGHT --auto --rotate normal --below $SCREEN_LEFT
        别的
            echo '单显示器设置'
            xrandr --auto
        菲

        OLD_DUAL="$DUAL"
    菲

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

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


小智 5

我坚持使用srandrd。它监视 X 事件并在显示器连接或断开连接时触发您的脚本。