Mit SnapForX Aerosnap für beliebige Linux-Fenstermanager nachrüsten
Monday, 21. March 2022
Die von Windows 7 eingeführten Aerosnap-Fensterkontrollen fand ich schon immer großartig. Mit ihnen werden zur linken Monitorseite gezogene Fenster auf die linke Hälfte des Monitors maximiert, die rechte maximiert sie auf die rechte Hälfte, zieht man sie an den obere Rand bedecken sie den ganzen Bildschirm. Sollen die Fenster wieder klein werden zieht man sie einfach nach unten, schon springen sie zurück, das gefiel mir besonders.
In der Linuxwelt ist das auch schon länger angekommen, aber hauptsächlich in den größeren Desktopumgebungen. Kleinere Fenstermanager wie das von mir genutzte IceWM beherrschen solche dynamischen Kontrollen normalerweise nicht. Bei ihnen sind die Fenster so starr wie bei Windows 95. Tiling-Fenstermanager können zwar auch die Fenster geschickt anordnen, aber ist ihr Ansatz dafür schon sehr anders. Und an Kontrollen über die Tastatur wie durch quicktile konnte ich mich nie gewöhnen.
Deshalb habe ich mit SnapForX ein Skript geschrieben, das den Ansatz der Aerosnap-Kontrollen für X-Fenstermanager nachrüsten kann. Perfekt ist es nicht, als angenehm empfinde ich es trotzdem.
SnapForX in aller Kürze
So sieht es in Bewegung aus, der gezeigte Fenstermanager ist IceWM:
SnapForX ist ein Bashskript. Es benutzt eine wilde Mischung aus Hilfsprogrammen um diese Fensterkontrollen nachzubilden – besonders xdotool und wmctrl, aber auch xinput, awk, cut und xprop werden gebraucht. Das Skript lässt sich dann direkt ausführen. Die alternative Installation ist aber auch nicht kompliziert, das Skript einfach irgendwo in den PATH schieben und starten:
snapforx -l -r -t -iof
-l
, -r
und -t
aktivieren die Reaktion auf die linke, rechte und obere Monitorseite, -iof
lässt das Skript Vollbildanwendungen ignorieren.
Der Code ist ein Fork von cornora, einem Skript um Befehle durch Verharren des Mauszeigers in den Bildschirmecken auszuführen.
Der Ansatz
Der X-Server lässt zwar den Zugriff auf viele Informationen zu und ermöglicht auch Fensterkontrollen von außerhalb des Fenstermanagers, aber um Fensterkontrollen im Stil von Aerosnap umzusetzen waren ein paar Tricks erforderlich.
Um zu erkennen, ob der Mauszeiger am Rand des Bildschirms ist, wird regelmäßig die Mauszeigerposition abgegriffen. Ist deren X-Wert nahezu 0 ist er links, ist er fast so groß wie der Monitor breit ist sind wir am rechten Rand, ist der Y-Wert kleiner 1 ist der Zeiger ganz oben. Ob der Mauszeiger dort verharrt wird getestet, indem diese Prüfung mit einer kleinen Verzögerung zweimal hintereinander ausgeführt wird. Das ist so ziemlich wie cornora funktioniert, nur leicht erweitert.
Aber hieran knabberte ich lange: Wie erkennen, dass ein Fenster bewegt werden soll? Die üblichen Werkzeuge wie xdotool geben da keine brauchbaren Informationen. Ich könnte mir alle Fenster und ihre Position ausgeben lassen, aber wenn der Nutzer ein maximiertes Fenster nach unten zu schieben versucht ändert sich keine Fensterposition.
Der Ansatz jetzt kombiniert mehrere Ideen:
-
Schaue mittels
xinput
, ob die linke Maustaste gedrückt wird. -
Wenn ja, gucke ob der Mauszeiger im oberen Bereich eines Fensters ist. Das ist ein Vergleich von
xdotool getmouselocation
mit der Y-Koordinate ausxdotool getactivewindow getwindowgeometry
. Das aktiviert für 5 Takte à 0,1 (einer unbekannten Einheit, weil ich die delay-Funktion nicht verstehe) den Bewegungsmodus, ab jetzt können Fenster maximiert werden. - Zum Unmaximieren kommt die letzte Erkennung hinzu: Wenn der Mauszeiger jetzt, also bei gedrückter Maustaste auf einer Fensterdekoration startend, zwei Takte à 0,5 unbekannter Einheit nach unten bewegt wird, dann wird das aktive Fenster demaximiert.
Dass diese Erkennungen zusammengreifen und tatsächlich halbwegs ordentlich funktionieren, damit hatte ich nicht wirklich gerechnet. Entsprechend zufrieden bin ich gerade mit dem Ergebnis.
Limitierungen
Allerdings hat dieser Ansatz natürlich seine Probleme. Ich sehe keinen Weg die Animationen einzubauen, die bei Windows und den Desktopumgebungen vorankündigen wie das Fenster beim Loslassen der Maustaste sich bewegen wird. Stattdessen wird das Fenster direkt bewegt, was dem Nutzer weniger Kontrolle gibt. Zudem springen die Fenster gerne mal völlig unflüssig hin und her, z.B. wenn man nach dem vertikalen Maximieren an einer Monitorecke dann nochmal bei gedrückter Maustaste den Mauszeiger bewegt. Vielleicht lässt sich das noch etwas besser glattbügeln, aber dass es perfekt wird scheint unmöglich, weil da der Fenstermanager und das Skript sich praktisch um die Kontrolle des Fensters streiten. Vielleicht könnte das Skript irgendwie anders markieren, wie es das Fenster bewegen will, und es ebenfalls erst beim Loslassen der Maustaste durchführen.
Kleinere Unfertigkeit: Da xinput die ID einer bestimmten Maus braucht sucht sich das Skript am Anfang eine aus. Der Code unterstützt also weder Mauswechsel während des Betriebs noch mehrere Mäuse an einem System, das dürfte besonders manche Laptopnutzer blockieren. Wer da Ideen für eine bessere Lösung hat möge einen PR einsenden.
Vielleicht ein weiterer Baustein für den eigenen Linuxdesktop. Zumindest empfand ich es als nette Trickserei. Vielleicht geht es ein paar weiteren hier mitlesenden Linuxern auch so. Verbesserungsvorschläge und vor allem Codeverbesserungen wären gern gesehen.
- IceWM: Tooltip umpositionieren
- IceWM unkonfigurierbar?
- IceWM-Design: n-iceflame
- Temperaturanzeige in der Taskbar
- Ice-win 0.6: spricht nun deutsch