In unserer Schule haben wir einige Rechner, an denen ein Beamer fest angeschlossen ist. Der Beamer wiederum hängt an einem HDMI-Switch, damit man bequem zwischen verschiedenen Eingängen umschalten kann, z.B. eigener Laptop oder Dokumentenkamera. Ubuntu erweitert standardmäßig den Desktop, wenn man einen Bildschirm anschließt. In unserer Situation wollen wir aber etwas anderes. Der Desktop des Rechners sollte ebenfalls am Beamer gezeigt werden. Es gibt verschiedene Wege, wie man Bildschirme automatisch spiegelt, wenn ein HDMI-Kabel eingesteckt wird. Ich möchte beschreiben, wie man udev verwenden kann, um dieses Problem zu lösen.
Bildschirme automatisch spiegeln
Potenzielle Möglichkeiten
Es gibt verschiedene Stellen, an denen man ansetzen kann, um Bildschirme zu spiegeln. Einige möchte ich kurz auflisten:
- keine Automatisierung. Der Benutzer (in unserem Fall die Lehrkaft) muss sich nach jeder Anmeldung selbst den Bildschirm so einstellen, wie er/sie es haben will.
- Symbol auf dem Desktop. Eine weitere Möglichkeit wäre, dass man ein Skript erstellt, welches den Bildschirm spiegelt und eine Verknüpfung auf den Desktop legt. Mit einem Klick könnte man dann die Bildschirme spiegeln.
- Autostart eines Skripts nach dem Anmelden. Das war bisher unsere Lösung. Ein kleines Skript spiegelt die Bildschirme nach der Anmeldung des Benutzers.
- LightDM / GDM so konfigurieren, dass es schon beim Anmeldeschirm die Bildschirme spiegelt
- In Gnome3 / Unity kann man auch die monitor.xml verwenden, um Bildschirmkonfigurationen zu speichern.
- Ein Skript, was überprüft, ob ein Bildschirm angeschlossen ist oder nicht und entsprechend die Bildschirme spiegelt, auch im laufenden Betrieb.
Die meisten Möglichkeiten hier lösen das Problem, dass die Bildschirme nach dem Anmelden / beim Anmelden gespiegelt sind. Bei uns trat aber zusätzlich das Problem auf, dass die Bildschirmeinstellungen zurückgesetzt wurden, sobald man ein eigenes Gerät eingesteckt oder eine andere Quelle am HDMI-Switch ausgewählt hatte. Wenn man dann zurückgeschaltet hat, war der Desktop wieder erweitert, statt gespiegelt.
Daraus folgt die Frage: Gibt es eine Möglichkeit, eine Aktion auszulösen, sobald ein HDMI-Kabel eingesteckt oder abgezogen (physisch oder über den HDMI-Switch) wird?
udev
Ja! udev ist ein Hintergrunddienst, der sich genau darum kümmert. Er kümmert sich um alle Einträge unterhalb von /dev/, d.h. Festplatten, externe Medien, USB-Geräte und auch Grafikkarten. Wenn ein Kabel angesteckt oder abgezogen wird, löst udev ein Ereignis („Event“) aus, auf das man mit Hilfe einer udev-Regel reagieren kann. Bevor wir jedoch so eine Regel erstellen, wollen wir uns anschauen, was passiert, wenn ein Kabel eingesteckt oder abgezogen wird. Dazu können wir das udev-Management-Tool (udevadm) verwenden.
$ udevadm monitor --environment --udev
Wenn wir jetzt das HDMI-Kabel einstecken oder abziehen, bekommen wir folgende Ausgabe:
UDEV [2247.166677] change /devices/pci0000:00/0000:00:02.0/drm/card0 (drm) ACTION=change DEVNAME=/dev/dri/card0 DEVPATH=/devices/pci0000:00/0000:00:02.0/drm/card0 DEVTYPE=drm_minor HOTPLUG=1 ID_FOR_SEAT=drm-pci-0000_00_02_0 ID_PATH=pci-0000:00:02.0 ID_PATH_TAG=pci-0000_00_02_0 MAJOR=226 MINOR=0 SEQNUM=2120 SUBSYSTEM=drm TAGS=:seat:master-of-seat:uaccess: USEC_INITIALIZED=15405142
udev sagt uns hier, um welches Gerät es sich handelt („card0“), welche Aktion ausgelöst wurde („CHANGE“) und welches Subsystem betroffen ist („DRM“). Diese Angaben brauchen wir gleich für unsere udev-Regel. Diese erstellen wir unter /etc/udev/rules.d/95-hotplug-hdmi.rules:
$ nano /etc/udev/rules.d/95-hotplug-hdmi.rules
Dort fügen wir folgende Zeile ein:
ACTION=="change", SUBSYSTEM=="drm", RUN+="/bin/bash /usr/sbin/setHDMIStatus"
Wir reagieren also auf ein „change“ Ereignis im Subsystem „drm“ und wollen, falls dieses Event eintritt das Skript „/usr/sbin/setHDMIStatus“ ausführen. Das Skript schreibt den aktuellen Status in eine Datei. Es kann auch irgendetwas anderes in die Datei geschrieben werden, Hauptsache, die Datei verändert sich, wenn der Status sich ändert. Warum werden wir gleich noch sehen. Das Skript setHDMIStatus hat folgenden Inhalt (Benutzernamen anpassen, im Falle von linuxmuster.net kann man den Account des Vorlagenbenutzers bzw. lokalen Admins nehmen):
#!/bin/bash STATUS="$(/bin/cat /sys/class/drm/card0-DVI-D-1/status)" /bin/echo $STATUS > /home/user/.hdmi_status
/sys/class/drm/card0-DVI-D-1/status ist der Pfad zum Status unseres HDMI-Anschlusses. In diesem Fall ist es ein DVI-Anschluss.
Hinweis: Das Skript ist ein Workaround, da zum Zeitpunkt des udev-Ereignisses xrandr den Status disconnected liefert, obwohl das Kabel angeschlossen war.
inotify
Bis jetzt haben wir lediglich erreicht, dass der Status (connected, disconnected) in eine Datei geschrieben wird. Wir wollen jetzt diese Datei auf Änderungen überwachen und je nach Status die Bildschirme spiegeln. Dazu erstellen wir folgendes Skript (z.B. mit dem Namen /usr/sbin/mirrorDisplays):
#!/bin/bash HDMI_STATUS=/home/user/.hdmi_status PROJECTOR=/sys/class/drm/card0-DVI-D-1/status touch $HDMI_STATUS dmode="$(cat $PROJECTOR)" function mirror { xrandr --output DVI1 --mode "1280x1024" --same-as VGA1 --output VGA1 --mode "1280x1024" } #Initial Login (Spiegeln beim Anmelden) if [ "${dmode}" = connected ];then mirror fi # watch HDMI connected status and setup screens while inotifywait -q -e modify,create,delete,open,close,close_write,access $HDMI_STATUS &> /dev/null; dmode="$(cat $PROJECTOR)" do if [ "${dmode}" = connected ];then sleep .5s mirror fi done
Damit das Skript auch funktioniert, müssen die inotify-tools installiert sein und das Skript ausführbar gemacht werden.
$ sudo apt install inotify-tools $ sudo chmod +x /usr/sbin/mirrorDisplays
Das Skript überwacht unserer angelegte Datei (/home/user/.hdmi_status) und löst, wenn ein HDMI-Kabel angeschlossen ist, den xrandr-Befehl zum Spiegeln der Bildschirme aus.
Autostart
Nun muss das Skript nur noch als Startprogramm festgelegt werden, damit es bei der Anmeldung mit gestartet wird.
Fazit
Wir nutzen dieses Setup nun für alle unsere Rechner, an denen ein Beamer hängt. Da wir dank linuxmuster.net nur ein einziges Ubuntu-Image für alle unsere Rechner verwenden, verteilen wir die oben aufgeführten Skripte per postsync an die entsprechenden Rechner.
Bisher funktioniert alles recht zuverlässig, auch wenn wir weiterhin auf der Suche nach einer Lösung sind, die noch etwas universeller ist und keinen Umweg mit einer extra Datei braucht. Wer einen Vorschlag hat, nur her damit 🙂
hi,
ob es eine lösung ist weiß ich nicht – hab’s nicht probiert.
aus meiner sicht aber ein timing issue – daher die frage, ob es nicht reicht im setHDMIStatus script ein sleep einzubauen.
z.b:
STATUS="$(/bin/cat /sys/class/drm/card0-DVI-D-1/status)"
if [ "${STATUS}" = connected ];then
sleep 2s
xrandr --output DVI1 --mode "1280x1024" --same-as VGA1 --output VGA1 --mode "1280x1024"
fi
nachtrag:
vermutlich fehlt noch
export DISPLAY=:0.0
sowie
export XAUTHORITY=/home/$USERNAME/.Xauthority
fehlt noch, dann sollte es klappen.
Ja, das habe ich auch schon probiert, aber selbst das Timing mit sleep hat nichts gebracht. xrandr hat da ebenfalls gewartet. Irgendwie scheint da udev am schnellsten zu sein und der Status unter /sys/… scheint erst danach aktualisiert zu werden.
[…] den meisten Räumen haben standardmäßig den Bildschirm gespiegelt, sobald ein Beamer oder Bildschirm über den HDMI-Switch angeschlossen wird. Im konkreten Fall […]