Monitor-Orientierung unter Windows via CLI steuern: Eine Vorstellung von xbmon

Die Verwaltung mehrerer Bildschirme gehört in vielen Arbeitsumgebungen zum Standard. Gelegentlich ist es notwendig, die Ausrichtung einzelner Monitore anzupassen – etwa für die Anzeige von Quellcode im Hochformat (Pivot-Modus) oder bei der Verwendung spezieller Halterungen. Während Windows diese Einstellungen standardmäßig in der grafischen Benutzeroberfläche anbietet, existieren für die Automatisierung über Skripte oder Tastatur-Shortcuts CLI-basierte Alternativen.

Das in Go geschriebene Kommandozeilen-Werkzeug xbmon ermöglicht die Abfrage und Änderung der Bildschirmorientierung direkt über das Terminal. Dieser Beitrag erläutert die Funktionsweise des Tools und analysiert die technische Umsetzung auf Basis der Windows-API.

Funktionsumfang von xbmon

xbmon ist ein eigenständiges CLI-Tool für Windows, das ohne externe Laufzeitumgebungen kompiliert wird. Es bietet folgende Kernfunktionen:

  • Gezielte Auswahl: Bildschirme können entweder über eine Indexnummer (1, 2, …) oder über den systeminternen Gerätenamen (z. B. \\.\DISPLAY1) adressiert werden.
  • Feste Rotationswerte: Das Tool unterstützt die Ausrichtungen 0°, 90°, 180° und 270°.
  • Toggle-Funktion: Über den Parameter -t lässt sich zwischen der Standardausrichtung (0°) und der Überkopf-Ausrichtung (180°) wechseln.
  • Statusabfrage: Mit den Parametern -list und -status können die aktuellen Auflösungen und Orientierungen der angeschlossenen Monitore ausgelesen werden.
  • Zustandsprüfung: Vor einer Änderung wird die aktuelle Orientierung überprüft. Stimmt diese bereits mit dem Zielwert überein, wird der Systemaufruf übersprungen, um Bildschirmflackern zu vermeiden.

Technische Umsetzung

Die Implementierung des Tools erfolgt in Go. Um die Abhängigkeiten minimal zu halten, verzichtet das Projekt auf CGO und externe Pakete. Stattdessen wird die Interaktion mit dem Betriebssystem über direkte Systemaufrufe (Syscalls) an die native user32.dll realisiert.

Laden der Windows-Prozeduren

Für die Interaktion mit dem Windows-Subsystem verwendet das Tool das Standardpaket syscall. Über ein Lazy-Loading-Verfahren werden die benötigten Funktionen zur Laufzeit bereitgestellt:

var (
	user32                = syscall.NewLazyDLL("user32.dll")
	enumDisplayDevices    = user32.NewProc("EnumDisplayDevicesW")
	enumDisplaySettings   = user32.NewProc("EnumDisplaySettingsW")
	changeDisplaySettings = user32.NewProc("ChangeDisplaySettingsExW")
)

Ermittlung der Anzeige-Metadaten

Um den aktuellen Zustand der Monitore zu bestimmen, ruft das Tool EnumDisplaySettingsW auf. Da Windows intern mit UTF-16-Strings arbeitet, müssen die Gerätenamen für die Weiterverarbeitung in Go entsprechend konvertiert werden.

Die Funktion füllt eine DEVMODEW-Struktur, welche die aktuelle Auflösung, Farbtiefe und Orientierung des jeweiligen Monitors enthält:

var device DEVMODEW device.Size = uint16(unsafe.Sizeof(device))

ret, _, _ := procEnumDisplaySettingsW.Call(
    uintptr(unsafe.Pointer(sysDeviceName)),
    ENUM_CURRENT_SETTINGS,
    uintptr(unsafe.Pointer(&device)),
)

Dynamische Anpassung der Dimensionen bei Rotation

Ein kritischer Punkt bei der Rotation von Bildschirmen ist das korrekte Vertauschen von Breite und Höhe. Wenn ein Monitor von der horizontalen in die vertikale Ausrichtung (oder umgekehrt) gedreht wird, müssen die Pixelwerte für Breite (PelsWidth) und Höhe (PelsHeight) in der Konfiguration gespiegelt werden.

Das Tool implementiert diese Logik über eine Bedingungsprüfung vor dem eigentlichen Systemaufruf:

// Zuweisung der neuen Orientierung über Bitmasken
device.Fields |= DM_DISPLAYORIENTATION
device.DisplayOrientation = dmOrientation

// Dimensionen vertauschen, wenn von Quer- zu Hochformat gewechselt wird (und umgekehrt)
if (currentOrientation == DMDO_DEFAULT || currentOrientation == DMDO_180) &&
   (dmOrientation == DMDO_90 || dmOrientation == DMDO_270) {
    device.PelsWidth, device.PelsHeight = device.PelsHeight, device.PelsWidth
} else if (currentOrientation == DMDO_90 || currentOrientation == DMDO_270) &&
          (dmOrientation == DMDO_DEFAULT || dmOrientation == DMDO_180) {
    device.PelsWidth, device.PelsHeight = device.PelsHeight, device.PelsWidth
}

Nachdem die Struktur angepasst wurde, wird die Änderung mittels ChangeDisplaySettingsExW angewendet. Der Parameter CDS_UPDATEREGISTRY sorgt dafür, dass die Konfiguration in die Windows-Registrierungsdatenbank geschrieben wird und sofort wirksam ist.

Kompilierung und Syntax

Das Tool kann mit den Standard-Go-Werkzeugen gebaut werden. Durch das Weglassen von Symbol- und Debug-Informationen lässt sich die Dateigröße der resultierenden Binärdatei reduzieren:

go build -ldflags "-s -w"

Befehlsbeispiele

  • Auflistung aller aktiven Monitore samt Spezifikationen:
    xbmon -list
    
  • Drehung des zweiten Monitors auf 90 Grad:
    xbmon -m 2 90
    
  • Zurücksetzen des Primärmonitors auf die Standardausrichtung:
    xbmon
    

Fazit

xbmon demonstriert die direkte Nutzung von Win32-API-Schnittstellen innerhalb einer Go-Anwendung ohne CGO-Abhängigkeiten. Durch die Implementierung als Kommandozeilen-Tool eignet sich die Anwendung für die Einbindung in Skripte, Verknüpfungen oder Automatisierungslösungen im Windows-Umfeld. Das Projekt ist unter den Bedingungen der GNU Affero General Public License v3 lizenziert.