Bei Monitoren, die über
DisplayPort angeschlossen sind, tritt in vielen Konstellationen das Problem auf, dass der Monitor kein Bild zeigt, wenn er nach vorübergehendem Ausschalten wieder eingeschaltet wird, oder nachdem vorübergehend kein Videosignal am Eingang anlag (z.B. wenn man mit einem
KVM-Umschalter vorübergehend auf eine ausgeschaltete Maschine schaltet). Da mich dieses Verhalten erheblich stört, habe ich mir nun die Zeit genommen, eine Lösung auszuarbeiten, die ich hier dokumentieren möchte.
Die Lösung besteht in einem automatischen Reset des DisplayPort-Videoausgangs. Die technische Umsetzung hat die Form einer
udev-Regel:
ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", PROGRAM=="/bin/cat /sys/class/drm/card0-DP-1/status", RESULT=="connected", ENV{DISPLAY}=":0", ENV{XAUTHORITY}="/home/user/.Xauthority", RUN+="/usr/bin/xrandr", RUN+="/usr/bin/xrandr --output DP-1 --off", RUN+="/usr/bin/xrandr --output DP-1 --auto"
Diese Zeile muss (ohne Zeilenumbrüche) -- ggf. mit einigen Änderungen, die ich im folgenden erläutere -- als Datei im Verzeichnis /etc/udev/rules.d gespeichert werden. Der Dateiname sollte mit einer zweistelligen Zahl >= 90 beginnen und muss die Extension .rules haben, geeignet wäre z.B. 90-drm-reset-displayport.rules. Der Owner der Datei muss root sein, und die Permissions müssen auf 644 gesetzt werden (wichtig!). Der Abschnitt "/home/user" muss durch den Pfad eines Verzeichnisses ersetzt werden, in dem das X-Window-Authorisierungs-Cookie .Xauthority gespeichert ist (üblicherweise durch Ersetzen von "user" durch den eigenen Usernamen).
Anschliessend muss die neue Regel mit dem folgenden Befehl dem udev-System bekannt gemacht werden (auch nach jeder Änderung der Regel erforderlich):
udevadm control --reload-rules
Bei Anstecken nach vorübergehendem Abstecken des Monitors oder bei KVM-Umschaltungen ist das Bild des Monitors nach meinen Tests 100% zuverlässig wieder da, bei Einschalten nach vorübergehendem Ausschalten des Monitors ist die Zuverlässigkeit u.U. etwas geringer. Das liegt daran, dass der Reset des DisplayPort-Videoausgangs u.U. etwas zu früh stattfindet, wenn der Monitor noch nicht richtig hochgefahren ist. Die Zuverlässigkeit lässt sich nach meinen Tests auch in diesem Fall auf 100% steigern, wenn man den folgenden Abschnitt nach RESULT=="connected" einfügt (mit Komma von den angrenzenden Abschnitten getrennt):
RUN+="/bin/sleep 5"
Es handelt sich um eine einfache Verzögerung bis der Monitor vollständig hochgefahren ist. Der Wert 5 Sekunden kann nach Bedarf rauf- oder runtergesetzt werden. Für Anwender, die -- wie ich -- das Problem hauptsächlich bei KVM-Umschaltungen haben, ist diese Variante jedoch weniger geeignet, da die 5 Sekunden Verzögerung in diesem Fall störend sind.
Zu den Einzelheiten: Eine udev-Regel besteht, vereinfacht betrachtet, aus einem Prüfungs-Teil und einem Aktions-Teil. Der Prüfungs-Teil prüft, ob eine bestimmte Situation eingetreten ist, und der Aktions-Teil legt die Aktionen fest, die bei Eintreten dieser Situation ausgeführt werden sollen. Allgemein ist dabei zu beachten, dass für Programmaufrufe in udev-Regeln einige wesentliche Einschränkungen gegenüber der Ausführung in einer Shell gelten. U.a. müssen alle Programmaufrufe (auch von Standardbefehlen) mit vollständiger Pfadangabe erfolgen.
ACTION=="change"
Das Hinzufügen, Entfernen oder Verändern eines Geräts während des Betriebs (oft in der Form von Hotplugging) löst üblicherweise ein Signal aus, das über das udev-System ausgewertet werden kann. Grafikkarten liefern beim Ein- oder Ausschalten oder dem An- oder Abstecken eines Monitors i.d.R. ein "change"-Signal. Dieser Abschnitt prüft, ob das aktuell aufgetretene Signal ein "change"-Signal ist. Handelt es sch um ein anderes Signal, wird die Verarbeitung der Regel an dieser Stelle abgebrochen.
SUBSYSTEM=="drm"
Dieser Abschnitt prüft, ob das Gerät, das das aktuelle Signal ausgelöst hat, zum Subsystem "drm" =
Direct Rendering Manager gehört. Das gilt üblicherweise für alle Grafikkarten. Ist das nicht der Fall, wird die Verarbeitung der Regel an dieser Stelle abgebrochen.
KERNEL=="card0"
Dieser Abschnitt prüft, ob der Kernel-Name des Geräts, das das aktuelle Signal ausgelöst hat, "card0" lautet. Das ist die übliche Bezeichnung für die erste Grafikkarte des Systems. Dieser String muss geändert werden, falls die Regel für eine Grafikkarte mit einem anderen Namen gelten soll. Entspricht der Name des Geräts nicht dem angegebenen String, wird die Verarbeitung der Regel an dieser Stelle abgebrochen.
PROGRAM=="/bin/cat /sys/class/drm/card0-DP-1/status"
Dieser Abschnitt gibt mittels des Standardbefehls cat den Inhalt der als Argument angegebenen Datei aus. Die Ausgabe wird dabei für die weitere Verwendung automatisch in der Variablen RESULT gespeichert. Es handelt sich in diesem Fall nicht um eine normale Datei, sondern um den dynamischen Status ("connected" oder "disconnected") des DisplayPort-Anschlusses, an dem der Monitor angeschlossen ist. Das funktioniert natürlich nur, wenn sowohl der Kernel-Name der Grafikkarte ("card0") als auch des DisplayPort-Anschlusses ("DP-1") im Pfad korrekt angegeben ist. Diese beiden Strings müssen also ggf. geändert werden. Den Namen des aktiven DisplayPort-Anschlusses kann man mittels des folgenden Befehls ermitteln:
xrandr | grep -w "connected"
Am Anfang der Ausgabezeile vor "connected" steht der Name des aktuell aktiven Video-Anschlusses.
Neben der Speicherung der stdout-Ausgabe des aufgerufenen Programms in der Variablen RESULT prüft dieser Abschnitt den Returncode des Programms. Trat bei der Ausführung ein Fehler auf (Returncode ≠ 0), z.B. weil der angegebene Pfad nicht existiert, wird die Verarbeitung der Regel an dieser Stelle abgebrochen.
RESULT=="connected"
Dieser Abschnitt prüft mit Hilfe des im vorhergehenden Abschnitt ermittelten Werts, ob aktuell Verbindung zwischen dem DisplayPort-Anschluss und dem Monitor besteht. Damit wird Ein- von Ausschalten bzw. An- von Abstecken des Monitors unterschieden. Sofern keine Verbindung zum Monitor besteht (der Inhalt von RESULT ist in diesem Fall "disconnected") wird die Verarbeitung der Regel an dieser Stelle abgebrochen.
Damit ist der Prüfungs-Teil der Regel abgeschlossen. Nun folgt der Aktions-Teil, der den DisplayPort-Anschluss resettet.
ENV{DISPLAY}=":0"
ENV{XAUTHORITY}="/home/user/.Xauthority"
Diese beiden Abschnitte dienen der Vorbereitung der nachfolgenden Aufrufe des Befehls
xrandr. xrandr braucht zum Zugriff auf das X-Window-System die X-Window-Umgebungsvariablen DISPLAY und XAUTHORITY. DISPLAY hat im einfachsten Fall den Wert ":0", der Wert muss jedoch ggf. geändert werden. XAUTHORITY gibt den Pfad zu einer Datei mit dem X-Window-Authorisierungs-Cookie an. Der Pfad muss entsprechend geändert werden (üblicherweise durch Ersetzen von "user" durch den eigenen Usernamen).
RUN+="/usr/bin/xrandr"
Dieser Dummy-Aufruf von xrandr ist theoretisch nicht notwendig, in der Praxis aber -- zumindest im Rahmen der Tests, die ich gemacht habe -- zwingend erforderlich. Das habe ich durch Zufall herausgefunden. Anscheinend verändert dieser Aufruf in irgendeiner Weise den Systemzustand, so dass die nachfolgenden Aufrufe von xrandr so funktionieren, wie sie sollen. An der kurzen zeitlichen Verzögerung, die dieser Aufruf bewirkt, liegt es nach meinen Tests nicht. Wer möchte, kann diesen Abschnitt versuchsweise weglassen. Es kann gut sein, dass es Konstellationen gibt, in denen er tatsächlich überflüssig ist.
RUN+="/usr/bin/xrandr --output DP-1 --off"
Dieser Aufruf von xrandr schaltet den DisplayPort-Anschluss aus. Falls der Name des DisplayPort-Anschlusses nicht "DP-1" lautet, muss der String entsprechend ersetzt werden.
RUN+="/usr/bin/xrandr --output DP-1 --auto"
Dieser Aufruf von xrandr schaltet den DisplayPort-Anschluss wieder ein. Falls der Name des DisplayPort-Anschlusses nicht "DP-1" lautet, muss der String entsprechend ersetzt werden.