如何在英特尔图形设备上创建“监视器已插入”的回调函数?

25

我有一台带有英特尔显卡的eeepc,我想将一个脚本挂钩到通过VGA连接的监视器插入事件上。该如何做呢?


1
寻求Windows解决方案的人可以参考以下链接:https://dev59.com/IknSa4cB1Zd3GeqPNmdPhttps://dev59.com/Y0_Sa4cB1Zd3GeqP9iBXhttps://dev59.com/Tm025IYBdhLWcg3weV8s - hippietrail
5个回答

29

作为一种粗略的解决方案,您可以在sysfs上进行轮询。在我的笔记本电脑上,我有:

$ cat /sys/class/drm/card0-LVDS-1/status
connected

$ cat /sys/class/drm/card0-VGA-1/status
disconnected
我猜这需要内核DRM和可能的KMS支持。
要查看是否可以自动触发某些内容,您可以运行udevadm monitor --property命令,并在(断开)连接显示器时观察以查看是否报告了事件。
使用我的Radeon显卡,第一次连接VGA显示器时会收到一个事件,但在随后的断开和重新连接中不会有任何事件。该事件应该类似于以下示例(使用您的作为示例):
KERNEL[1303765357.560848] change /devices/pci0000:00/0000:00:02.0/drm/card0 (drm)
UDEV_LOG=0
ACTION=change
DEVPATH=/devices/pci0000:00/0000:00:02.0/drm/card0
SUBSYSTEM=drm
HOTPLUG=1
DEVNAME=dri/card0
DEVTYPE=drm_minor
SEQNUM=2943
MAJOR=226
MINOR=0

很不幸,没有太多可以匹配的内容,但只要图片中只有一个显卡,这就不太重要了。在您的系统上查找udev获取规则的位置(可能是/etc/udev/rules.d/),并创建一个名为99-monitor-hotplug.rules的文件,并输入以下内容:

ACTION=="change", SUBSYSTEM=="drm", ENV{HOTPLUG}=="1", RUN+="/root/hotplug.sh"

udev会在显示器连接时运行hotplug.sh。 作为测试,我将以下内容放入/root/hotplug.sh中(不要忘记将此脚本设置为可执行):

#!/bin/sh

for output in DVI-I-1 LVDS-1 VGA-1; do
        echo $output >> /root/hotplug.log
        cat /sys/class/drm/card0-$output/status >> /root/hotplug.log
done

我连接了一个外部显示器后,在hotplug.log中得到了一条记录。即使过滤掉ACTION=="change",在启动时仍会收到一些事件,因此您可能需要在脚本中考虑这一点。


1
不错的开始,但我不喜欢轮询。 - Reactormonk
@Tass 如果你正在接收事件,请联系我,我可能可以想出一个udev规则来运行脚本。 - Andy
4
谢谢Andy!我已创建了一个脚本,受这篇文章的启发。您可以在https://github.com/codingtony/udev-monitor-hotplug获取它。当显示器插入/拔出时,它使用xrandr来切换显示器的开关状态。希望这能有所帮助。 - Tony
我第一次连接VGA显示器时会收到一个事件,但在随后的断开和重新连接中没有事件。对我也是如此,但似乎只有第一次执行我的脚本。你的hotplug.sh自定义脚本每次断开和重新连接显示器时都被调用了吗? - user779159
我正在尝试设置此项以考虑系统启动时没有连接监视器的可能性。对我来说,udev会显示连接和断开连接事件,但仅在系统启动时连接了监视器。有什么办法让它在udev启动时注意到没有连接任何设备?采用轮询将需要一个更混乱的脚本... - Perkins
99 是从哪里来的? - Foobar

6
这篇答案的方法是正确的:您需要从udev监听DRM事件。 我已经实现了一个Python脚本,当USB设备或外部显示器插入或拔出时运行一些代码。下面是该脚本的最小版本(未经测试):
#!/usr/bin/env python3
import pyudev

def udev_event_received(device):
    ...  # Your code here!

context = pyudev.Context()
monitor_drm = pyudev.Monitor.from_netlink(context)
monitor_drm.filter_by(subsystem='drm')
observer_drm = pyudev.MonitorObserver(monitor_drm, callback=udev_event_received, daemon=False)

observer_drm.start()

# This will prevent the program from finishing:
observer_drm.join()

另请参阅:


4
你有三个选择:
  1. 在sysfs中针对特定条目进行轮询。
  2. 使用inotify检测sysfs中的更改。
  3. 使用NETLINK_KOBJECT_UEVENT的netlink套接字,并监听所需设备的change uevent。
无论哪种方式,你仍然需要以某种方式进行轮询,因此我建议选择第一种选项。

2
inotifynetlink套接字都可以使用select()等函数。您真的认为这是“轮询”吗? - Brian Cain
4
由于sysfs并非真正的文件系统,因此inotify无法监测其变化。如果不相信,请尝试使用以下命令:sudo inotifywait -e modify -m --format '%:e %f' /sys/class/drm/something。因此你的第二个选项是无效的。 - nicholas.alipaz
“不,inotify在sysfs上不起作用,如果它确实起作用,那只是“偶然”,而且你得到的任何数据可能完全错误。” [Greg KH on 2014-03-29] (http://comments.gmane.org/gmane.linux.file-systems/83641) - Denilson Sá Maia
@DenilsonSáMaia 今天你的链接坏了,或者我无法访问它... - WinEunuuchs2Unix
@WinEunuuchs2Unix:新链接:https://www.spinics.net/lists/linux-fsdevel/msg73924.html - Denilson Sá Maia
显示剩余3条评论

3

感谢sebastianwagner的帮助!

通过这些信息,我现在可以成功地在电视关闭的情况下启动我的Kodi媒体中心了。当电视关闭时,Intel驱动程序不会设置模式,所以当我之后打开电视时屏幕上就是一片空白。

Kodi日志显示了以下内容:

WARNING: CXRandR::Query - output HDMI1 has no current mode, assuming disconnected

所以我在 /etc/udev/rules.d/99-monitor-hotplug.rules 中创建了以下行:

ACTION=="change", SUBSYSTEM=="drm", ENV{HOTPLUG}=="1", RUN+="/usr/sbin/hotplugtv.sh"

/usr/sbin/hotplugtv.sh的内容(我的X服务器以root身份运行):

#!/bin/bash

export DISPLAY=:0
export XAUTHORITY=/root/.Xauthority

/bin/date 2>&1 >> /var/log/hotplugtv.log;
if [[ $(cat /sys/class/drm/card0-HDMI-A-1/status | grep -Ec "^connected") -eq 1 ]]; then
        echo "TV connected!" >> /var/log/hotplugtv.log;
        /bin/sleep 2s;
        /usr/bin/xrandr --verbose --output HDMI1 --auto 2>&1 >> /var/log/hotplugtv.log;
else
        echo "TV disconnected!" >> /var/log/hotplugtv.log;
fi

在您对脚本进行任何更改时,请不要忘记重新加载udev规则(这让我疯狂!):

udevadm control --reload-rules

请注意在Kodi中禁用任何屏幕保护程序,因为当您最终开启电视时,它们将永久激活。 另一方面,节能/ DPMS似乎工作正常。


2
假设您正在运行X,一个脚本可以解析xrandr的输出以查看连接了哪些显示器。这应该适用于任何图形卡。这是您很可能使用的相同工具来利用脚本中的更改。
它不能解决自动运行脚本的通知问题。虽然我没有一个很好的通用解决方案,但一个常见的情况是检测外部监视器是否存在,因为笔记本电脑连接到了一个扩展坞。在这种情况下,您可以让脚本触发其他触发器,例如连接到扩展坞时添加或删除USB键盘或以太网。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接