モニターが接続または切断された時期をどのように検出しますか?

モニターが接続または切断された時期をどのように検出しますか?

お持ちですか?どの外部モニタをノートブックのDisplayPortに接続または切断すると、イベントが発生しますか? ACPIDとUDEVはまったく反応しません。

私はIntelチップに内蔵グラフィックを使用しています。ここ同様の議論が長年行われてきた。

ポーリングを使用したくありませんが、モニターが接続されているかどうかに応じてディスプレイ設定を自動的に設定するには、いくつかの構成が必要です。

答え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}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.52

編集#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情報が提供されますが、状態をポーリングする必要があります。

すべての/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。約1時間かかります。0.140秒~0.200秒プラグには何も変更されていませんが、ほとんど0.700秒以前に何かが接続または切断されたときはいつでも(メモ:資源をつかむ人ではないようです。)

編集#1

私が何か間違ったことを教えていないことを確認するためにウェブと文書を検索しましたが、関連するものが見つかりませんでした。DBusと画面

dbus-monitor --system最後に、私が書いたこの小さなスクリプトを使って2つの異なるウィンドウ(オプションも使用しています)で実行しています。

$ 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

Rennis+19 $$ >/dev/null

睡眠$START_DELAY

OLD_DUAL="仮想"

そして[1];
    DUAL=$(cat /sys/class/drm/card0-DP-2/状態)

    if [ "$OLD_DUAL" != "$DUAL" ];
        if ["$ DUAL" == "接続済み"];
            echo 'デュアルモニター設定'
            xrandr --output $SCREEN_LEFT --auto --rotation Normal --pos 0x0 --output $SCREEN_RIGHT --auto --rotation Normal --below $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依存するという点で非常にきれいです。randrawkスクリプトは、正しいイベントを解析して選択するために使用されます。

ノート: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)は基本的に私のラップトップなので、絶対に切断されません。

これを拡張して他のスクリプトなどを呼び出すことができます。これは上記と同等のC/C++の作成を始めたくない人のためのPOCだけです。

関連情報