有没有任何当我将外部显示器插入或拔出笔记本电脑的 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/monitor
或xrandr
),显卡会执行以下操作扫描,因此运行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 编写了这个非常好的脚本。它非常干净,因为它不会与任何系统设备混在一起,也不需要对此类设备进行广泛的了解,而是仅依赖于使用xev
和randr
来检测连接的监视器。 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++ 的人。