Caluga - Java blog

Ghostty - jetzt doch

Ghostty - jetzt doch

Das Ghostty-Terminal hat vor einer Weile echt Furore gemacht in der Bubble der Linux/Un*x-Consolen-Jockeys. Diejenigen, die gerne in der Shell gearbeitet haben (und noch arbeiten) sind immer auf der Suche nach dem ultimativen Terminal Emulator.

Natürlich hat mich das genauso "beschäftigt" und da ich gerne mit Neovim rumspiele (zu viel vermutlich) war WezTerm quasi der natürliche Kandidat: wird komplett in Lua konfiguriert, kann sogar Events an deine Config weitergeben, so dass du deinen WezTerm total personalisieren kannst. Noch dazu ist er extrem flott, unterstützt Kittys IMG-Protokoll und bringt sogar einen Multiplexer mit, so dass deine Shells nicht mehr sterben, wenn man das Fenster schließt.

Vieles davon kann auch Kitty - den hatte ich auch eine Weile als primäres Terminal. Ich hatte zu Ghostty schon mal was hier geschrieben, damals hatte ich mich besonders auf Speed fokussiert - und weil das nicht alles ist, hatte ich dennoch eine ganze Weile WezTerm als meinen Daily Driver.

Warum jetzt doch Ghostty?

Gute Frage. Also, WezTerm ist wirklich super. Kitty auch. Beide haben ihre Daseinsberechtigung. Aber, und das hat mich genervt: sowohl Kitty als auch Ghostty nutzen eine eigene TERM-Variable, um zu wissen, was in der Shell gemacht werden darf.

Das kann besonders beim Zugriff auf entfernte Systeme (ssh) zu Problemen führen. Bei Kitty gibt es die Lösung, anstelle von ssh den Wrapper kitten ssh zu verwenden - dann werden alle Terminal-Settings kopiert. Was allerdings wieder andere Probleme verursacht hat.

Für mich, der ich täglich mit zig verschiedenen Systemen "kämpfen" 😉 muss, war das schon ein Thema. Weshalb ich dann doch wieder bei WezTerm gelandet bin.

Aber auch da ist nicht alles rosig. Das Ding kann viel, und vieles davon ist super. Die Lua-Unterstützung ist toll. Ich konnte da einige Dinge bauen, die echt witzig sind. Ich hab mich vor allem an EyeCandy versucht. Ein Background-Chooser, Halbtransparenz, Parallax-Scroll-Effekt beim Hintergrundbild. Letzterer ist wirklich genial, den gibt es sonst nirgends. Ist schon ein Hingucker.

Nur - und das ist die ehrliche Version der Geschichte - benutzt habe ich das kaum. Als erstes habe ich immer auf transparent oder Focus Mode gestellt. Ein schönes Feature ohne praktischen Nutzen. Und die halbe Infrastruktur drumherum war eigentlich Symptombekämpfung: mit dem WebGpu-Backend sahen die Fonts bei mir irgendwie faserig aus, und um den Text lesbar zu halten, habe ich die Transparenz hoch und runter gedreht. Deswegen der Keybind, deswegen der ganze Kram. Die eigentliche Lösung war am Ende ein Einzeiler: front_end = "OpenGL". Damit war das Problem weg - und der Grund für die Bastelei auch.

Dazu kam, dass die Lua-Config zu pflegen immer zeitaufwendiger wurde, weil man ja auch immer wieder etwas erweitern und verbessern will. 1000 Zeilen Lua für ein Terminal. Ghostty ist da eher minimalistisch. Und das ist gut so. Man stellt alles einmal so ein, wie man es braucht - fertig.

Ein Unterschied, und der ist etwas blöd: wenn man z.B. via Teams ein Fenster teilt, dann ist das in WezTerm inklusive aller Tabs. Erstaunlicherweise ist es in Ghostty nur das aktuell offene Tab. Sehr seltsames Verhalten, liegt wohl an den macOS-APIs (wie üblich). Muss man wissen, wenn man damit arbeitet.

Die TERM-Geschichte ist erledigt

Das war mein Hauptargument gegen Ghostty, und das ist inzwischen weg. Ghostty bringt das mit, man muss es nur einschalten - der Default ist nämlich aus. Ein Blick in die Defaults verrät:

shell-integration-features = cursor,no-sudo,title,no-ssh-env,no-ssh-terminfo,path

Die beiden no-ssh-* sind genau die interessanten. Wer es einfach haben will, schreibt in die Config:

shell-integration-features = true

Dann sind alle an, und ghostty +show-config zeigt einem, was daraus effektiv geworden ist:

shell-integration-features = cursor,sudo,title,ssh-env,ssh-terminfo,path

ssh-terminfo installiert Ghosttys Terminfo beim Verbinden automatisch auf dem Zielhost, per infocmp und tic. ssh-env fängt den Fall ab, dass das schiefgeht, und schaltet auf xterm-256color zurück. Der sudo-Wrapper sorgt dafür, dass einem sudo vim das Terminfo nicht wieder wegnimmt. Was früher mein manuelles infocmp -x | ssh SERVER -- tic -x - war, passiert jetzt von selbst.

Und das Beste: Ghostty führt Buch darüber, welche Hosts schon versorgt sind.

$ ghostty +ssh-cache
Cached hosts (35):
  ...

35 Kisten, der älteste Eintrag 310 Tage alt. Deswegen merke ich davon nichts mehr - der Extra-Roundtrip fällt nur beim allerersten Connect zu einem Host an. Ein Fallstrick bleibt, und der beißt genau uns Admins: wird eine Kiste neu aufgesetzt und behält ihren Namen, glaubt der Cache weiterhin, alles sei installiert. Dann sind die Symptome zurück, ohne erkennbaren Grund. Hilft:

ghostty +ssh-cache --remove=user@host

Übrigens: diese "komischen Timeouts", die ich früher hatte, waren genau das. Cursortasten senden Sequenzen, die mit ESC anfangen. Kennt das Terminfo sie nicht, kann readline oder vim nicht entscheiden, ob du ESC gedrückt hast oder eine Sequenz anfängt - und wartet erstmal ab. Fühlt sich an wie ein hängendes Terminal, ist aber nur eine Warteschleife.

Ghostty-Shader

Was mich dann letztendlich zum Wechsel bewegt hat, war die Config und das dann doch super praktische EyeCandy, was ich mir mit den Shadern bauen konnte.

Mir war vorher nicht klar, wofür man diese Shader brauchen kann und inwieweit die was Sinnvolles tun (naja, sinnvoll ist eh Definitionsfrage). Aber was ich damit realisiert habe, hat mich erstaunt.

Vielleicht erinnern sich ja einige an den Smooth-Cursor-Effekt aus Word: beim Tippen ist der Cursor nicht "gesprungen", sondern langsam ans Ziel animiert. Das sah irgendwie cool aus, und genau das habe ich jetzt mit einem Shader nachbauen können. Sehr witziges Verhalten, echt. Und es gibt gleich mehrere Cursor-Shader zur Auswahl, dazu jede Menge Unsinn von Schneefall bis CRT-Simulation.

Ein paar Dinge, über die ich dabei gestolpert bin - alle drei haben gemeinsam, dass ghostty +validate-config sie kommentarlos durchwinkt:

  • Inline-Kommentare gibt es nicht. Ein custom-shader = foo.glsl # nette Notiz macht die Notiz zum Teil des Dateinamens. Der Shader lädt dann einfach nicht. Kommentare müssen in eine eigene Zeile.
  • Relative Pfade werden relativ zu der Datei aufgelöst, in der die Zeile steht - nicht relativ zur Hauptconfig. Wer seine Shader-Zeilen per config-file aus einem Unterordner einbindet, sucht sich sonst dumm und dämlich. Absolute Pfade sind da die ruhigere Variante.
  • Kompilierfehler stehen nur im Log. Ein Shader, der nicht baut, wird stillschweigend ignoriert. Nachsehen kann man mit log show --last 5m --predicate 'process == "ghostty"' | grep -i shader.

Das Gute an der Sache: ein kaputter Shader kostet dich höchstens den Effekt, nicht die Session.

Eine Grenze hat Ghostty allerdings, und die merkt man genau dann, wenn man vom WezTerm-Basteln kommt: die Config ist eine statische Key-Value-Liste. Keine Logik, kein Zustand, keine Events. Ein Hintergrundbild kann man setzen, aber nicht an die Transparenz koppeln. Und für die Transparenz gibt es nur toggle_background_opacity - also an oder aus, keine Stufen. Der Wert selbst braucht auf macOS sogar einen kompletten Neustart. Nach meiner WebGpu-Erfahrung brauche ich den Regler zwar nicht mehr, aber wissen sollte man es.

Und was den Speed angeht: in meinen vtebench-Läufen ist Ghostty deutlich schneller als WezTerm. Auch mit aktivem Shader, was mich zuerst überrascht hat. Ergibt aber Sinn - vtebench misst den Parser-Durchsatz, und der Shader ist ein GPU-Pass, der an der Bildwiederholrate hängt. Der Renderer zeichnet seine 60 bis 120 Bilder pro Sekunde, egal wie viele Megabyte der Parser wegarbeitet.

Fun to play

Und klar, das kann sich wieder ändern. Irgendwann in den nächsten Monaten wird mir Ghostty wieder zu blöd und ich wechsle woanders hin. Aber bis dahin ist Ghostty meine Spielwiese.

Ok, das ist alles eine Spielerei, das kann ich sagen. Klar. Aber eine schöne. Und ein Terminal, mit dem man auch spielen kann, ist doch super. Wichtig ist am Ende ja doch nur: ist der Text lesbar, funktioniert meine Software so, wie sie soll, sieht alles richtig aus. Und das tut es. Grafische Tools laufen fehlerfrei (viu z.B. oder imgcat oder auch morpheus).

Ghostty ist ein echter All-Rounder. Gut zu gebrauchen.