1 Punkte von GN⁺ 2025-08-16 | 1 Kommentare | Auf WhatsApp teilen
  • Das Ghostty-Team hat die GTK-Anwendung vollständig neu geschrieben und das GObject-Typsystem aktiv genutzt
  • Dabei spielten die Integration mit der Programmiersprache Zig und die Überprüfung von Speicherproblemen mit Valgrind eine wichtige Rolle
  • Durch die Einführung des GObject-Systems wurden Speicherverwaltung und die Implementierung benutzerdefinierter Widgets gegenüber bisher vereinfacht
  • Der Einsatz von Valgrind zeigte aus praktischer Erfahrung, dass sich die Speichersicherheit von Ghostty deutlich verbessert hat
  • Das neue Ghostty GTK ist nun der Standard für Builds aus dem Quellcode und soll in Release 1.2 enthalten sein

Einleitung

  • Ghostty ist ein plattformübergreifender Terminal-Emulator mit Unterstützung für macOS, Linux und FreeBSD
  • Für jede Plattform nutzt es ein natives GUI-Framework als Unterscheidungsmerkmal
    • macOS: umfangreiche Anwendung auf Basis von Swift und Xcode
    • Linux und BSD: GTK-basierte Anwendung mit direkter Integration in X11/Wayland usw.
    • Der gemeinsame Kern ist in Zig geschrieben und stellt eine API mit C-ABI-Kompatibilität bereit
  • Warum die GTK-Anwendung in der bisherigen Struktur neu geschrieben wurde, kann im ursprünglichen PR nachgelesen werden
  • Dieser Beitrag konzentriert sich insbesondere auf die Anbindung an das GObject-Typsystem sowie auf mit Valgrind verifizierte Speicherprobleme

GObject-Typsystem und Zig

  • Wer GTK verwendet, muss strukturell mit dem GObject-Typsystem und seinen Interfaces arbeiten
  • Früher wurde versucht, das GObject-System zu umgehen und den Lebenszyklus von Zig-Objekten ohne Reference Counting und GObject-Objekten direkt aufeinander abzustimmen, doch dabei traten wiederholt Probleme mit nicht korrekt freigegebenem Speicher auf
    • Beispiel: Der Speicher auf Zig-Seite war bereits freigegeben, während der Speicher auf GTK-Seite noch lebte, oder umgekehrt
  • Dieser Ansatz erschwerte nicht nur die Korrektheit, sondern auch die Nutzung GTK-spezifischer Funktionen wie Event-Signale, Property Binding und Actions
  • Ein konkretes Beispiel war das Neuladen der Config-Struktur: Alle verbundenen GUI-Elemente mussten dabei konsistent aktualisiert werden, was komplex und fehleranfällig war
    • Heute wird dies als reference-counted GhosttyConfig GObject verwaltet, das die Zig-Config-Struktur umhüllt; Benachrichtigungen über Property-Änderungen propagieren Änderungen nun auf natürliche Weise durch die gesamte Anwendung
  • Auch das Erstellen benutzerdefinierter GObject-Widgets wurde einfacher, sodass moderne GTK-UI-Technologien wie Blueprint genutzt werden können
    • In letzter Zeit wurde mit der Einführung von Blueprint die Umsetzung neuer Funktionen wie GTK-Tabs in der Titelleiste und animierte Bell-Ränder erleichtert

Valgrind, GTK und Zig

  • Während des gesamten Entwicklungsprozesses wurde Valgrind eingesetzt, um Speicherlecks, Zugriffe auf undefinierten Speicher und ähnliche Probleme systematisch zu prüfen
  • Die Prüfung einer GTK-Anwendung mit Valgrind ist anspruchsvoll und erfordert große Suppression-Dateien (80 % für GTK selbst, der Rest für Third-Party-Bibliotheken und GPU-Treiber)
  • Durch wiederholte Prüfungen konnten komplexe Speicherfehler, die nur in bestimmten Fällen auftreten, frühzeitig entdeckt werden
    • Beispiel: Wird ein GObject-WeakRef nicht korrekt initialisiert, kann es später beim Freigeben des Zielobjekts zu Zugriffen auf undefinierten Speicher kommen; Valgrind erkannte dies im Voraus
  • In der Praxis gab es im Zig-Codebestand insgesamt nur zwei Probleme (ein Leak, ein undefinierter Zugriff), und selbst diese traten bei der Anbindung an eine Third-Party-C-API auf
    • Auch Zigs Debug-Allocator und die Valgrind-Integrationsfunktionen haben ihre Wirksamkeit belegt
  • Die übrigen gefundenen Speicherprobleme stammten überwiegend von den C-API-Grenzen und der komplexen Lebenszyklusverwaltung im GObject-System
    • Das Fazit lautet: Um die C-API komplexer Bibliotheken sicher zu nutzen, sind Werkzeuge wie Valgrind erforderlich
  • Die unterstützenden Funktionen von Zig für Speichersicherheit erwiesen sich nicht nur in theoretischen Diskussionen, sondern auch in konkreter Projekterfahrung als wirksam

Fazit

  • Dies war das fünfte Mal, dass der GUI-Teil von Ghostty von Grund auf neu erstellt wurde
    • In der Reihenfolge: GLFW, macOS SwiftUI, macOS AppKit+SwiftUI, Linux GTK (prozedural), Linux GTK + GObject-Typsystem
  • Aus dem wiederholten Rewrite wurden jedes Mal neue Erkenntnisse und technisches Wachstum gewonnen
    • Teile dieser Erfahrung sollen auch auf das macOS-Projekt übertragen werden
  • Hervorgehoben wird außerdem die aktive Zusammenarbeit des Wartungsteams für das Ghostty-GTK-System
  • Die neu geschriebene Ghostty-GTK-Anwendung ist nun der Standard für Source-Builds und soll mit dem offiziellen Release 1.2 eingeführt werden

1 Kommentare

 
GN⁺ 2025-08-16
Hacker-News-Kommentare
  • Ich habe nicht direkt mit GTK gearbeitet, aber nach deiner Beschreibung fühlt sich das sehr ähnlich zu den Problemen an, die ich beim Erstellen von Godot-Bindings für Zig hatte. Godot hat extrem viele OOP-Konzepte wie Klassen, virtuelle Methoden, Properties und Signale. Und es bietet eine C-API, die all diese Konzepte behandelt und es ermöglicht, benutzerdefinierte Objekte und Eigenschaften zu erstellen. Die Lebensdauer der Engine-Objekte muss direkt verwaltet werden, und es gibt auch Baumstrukturen mit referenzgezählten Objekten. Gerade die Lebensdauerprobleme in eine optimale API zu packen, die zu Zig-Idiomen passt, ist extrem komplex. Während ich darüber nachgedacht habe, habe ich auch die oopz-Bibliothek erstellt. Der API-Status ist noch ungefähr auf diesem Stand, und ein reales Beispiel gibt es hier. Ich würde den Ghostty-Frontend auch gern als Godot-Erweiterung bauen

    • Früher habe ich einmal direkt sprachspezifische GTK-Bindings benutzt und erinnere mich daran, dass sie unbequem waren. 98 % funktionierten gut, aber in den restlichen 2 % gab es Stellen nach dem Muster „Ob diese Funktion eine Referenz auf das Objekt übernimmt oder nicht, hängt von einem anderen Argument ab“, und dadurch war die Analyse des Objektlebenszyklus ziemlich unerquicklich
    • Ich möchte mich bedanken und gleichzeitig sagen, dass ich beim Versuch, performanten C#-Godot-Code zu schreiben, damit zu kämpfen hatte, dass es zu viele Konvertierungen zu und von Engine-Typen gab und dadurch wiederholt Allokationen entstanden. Mich würde interessieren, ob dir solche Probleme beim Schreiben der Bindings auch begegnet sind
    • Ich wusste nicht, dass es ein laufendes Projekt für Godot-Bindings in Zig gibt. Ich mag sowohl Godot als auch Zig sehr und freue mich darauf. Ich werde das weiter verfolgen
  • Das ist ein gutes Beispiel dafür, dass gutes Programmieren letztlich bedeutet, sich der Art und Weise anzupassen, die das System vorgibt. Unabhängig davon, wie man über OOP oder Speicherverwaltung denkt: Wenn man GTK benutzt, muss man irgendwie mit dem GObject-Typsystem interagieren. Man kann ihm letztlich nicht ausweichen. Wir haben es trotzdem versucht, und das Ergebnis war ein enormes Chaos beim Verknüpfen der Lebensdauer referenzgezählter und nicht referenzgezählter Objekte. In der Ghostty-GTK-App traten immer wieder Bugs auf, bei denen Zig-Speicher freigegeben wurde, GTK-Speicher aber nicht — oder umgekehrt

    • Der Grund, warum GTK so aufgebaut ist, ist auch der Entstehungshintergrund von Vala. Vala ist von C# inspiriert, nutzt GObject und transpiliert den Code nach C. Deshalb sind ziemlich viele GTK-Apps tatsächlich in Vala geschrieben. Manchmal denkt man, D wäre vielleicht die bessere Wahl gewesen. D fühlt sich in vieler Hinsicht wie kompiliertes C# an
    • Sich einem schlechten System zu beugen ist nicht gut, sondern eine pragmatische Entscheidung
  • Meine Haltung zu OOP und Speicherverwaltung sei dahingestellt, aber ich stimme zu, dass man sich mit dem GObject-Typsystem verheddert, sobald man GTK verwendet. Deshalb habe ich mich entschieden, GTK gar nicht erst direkt zu verwenden. Ich verstehe den Wert eines einheitlichen UI-Themas, aber aus meiner Sicht sind die Vorteile von GTK nicht groß genug, um diesen Preis zu zahlen. Nach meiner Erfahrung mit dem Randbereich von GTK in Open-Source-Apps bin ich überzeugt, dass die Sichtweisen von GTK und GObject nicht besonders gut zu meinen Vorlieben passen. Es stört mich nicht, dass GTK existiert. Ich entscheide mich einfach dagegen, und das ist für mich in Ordnung, aber es ist seltsam, dass manche Leute diese Entscheidung anscheinend nicht als mein Recht ansehen. Es ist nur eines von vielen GUI-Toolkits, und obwohl es technisch sehr ausgereift ist, frage ich mich manchmal, ob die Sorgfalt, die in GTK geflossen ist, nicht auch einem strukturell besseren Toolkit hätte zugutekommen können, wenn GTK nur ein wenig weniger Marktanteil hätte. Natürlich ist das, was ich gut finde, nicht für alle gut. Ich frage mich, wie viele GTK-Nutzer es eher widerwillig einsetzen und wie viele es wirklich für das beste Toolkit halten

    • Ich stimme auch dem Punkt zu, dass der stark meinungsgeprägte Stil von GTK und GObject nicht gut zu meiner Denkweise passt. Ich habe auch das Gefühl, dass ich mit der Richtung des GNOME-Ökosystems oft nicht übereinstimme. Dass Ghostty unter Linux GTK verwendet, ist eine ziemlich pragmatische Entscheidung. Was Ghostty unter „plattformnativ“ versteht, insbesondere unter Linux, ist hier definiert. GTK ist unter Linux am weitesten verbreitet und fügt sich am natürlichsten in den Großteil des App-Ökosystems ein, daher war diese Entscheidung kaum zu vermeiden. Ich hoffe, dass über libghostty künftig verschiedene Frontends von Dritten entstehen. Ein Beispiel ist Wraith, ein Wayland-natives Ghostty-Frontend. Ziemlich cool
    • Ich denke, der Hauptgrund, warum GTK unter Linux so weit verbreitet ist, ist genau, dass es „C-Bindings“ gibt. Dadurch gibt es für fast jede Sprache entweder standardmäßig Bindings oder sie lassen sich leicht automatisch generieren. Qt dagegen ist zu stark an C++ und Python gebunden, was die Zugänglichkeit deutlich senkt. Wichtig ist, Entwickler dort abzuholen, wo sie mit ihrer Sprache bereits sind. Außerdem ist bei komplexen Desktop-Apps ein altmodisches imperatives UI-Toolkit oft sogar praktischer, und mit vielen bewährten Widgets sind auch die Muster vertraut. Neuere Ansätze dagegen verlangen oft, dass man schon die kleinen Dinge selbst baut, und sobald es etwas komplexer wird, wird es ziemlich mühsam
    • Mich würde interessieren, welche Gegenreaktionen du vor allem auf den Punkt bekommen hast, dass „man GTK zwar nicht benutzen muss, manche Leute aber so tun, als sei das keine freie Entscheidung“. Aus meiner Sicht ist GTK gerade bei Themen wie Barrierefreiheit oder nicht-lateinischer Eingabe ziemlich gut, also bei Bereichen, die Entwickler eigener Lösungen oft nicht beachten. Das scheint mir ein wesentlicher Wettbewerbsvorteil zu sein
  • Eine interessante Tatsache: In Ghostty und einigen anderen GTK-Apps gibt es das Phänomen, dass der erste Scroll-Klick ignoriert wird, wenn die Maus das Fenster verlässt und dann wieder hineinbewegt wird. Das liegt an einem uralten Bug, der erstmals 2015 gemeldet wurde. Link zum Bug. Bis heute ist keine Behebung geplant, und die Maintainer vertreten die Haltung, man solle auf Wayland warten

    • Tatsächlich scheint dieses Problem nicht in GTK selbst zu liegen, sondern in XInput2. Natürlich könnte GTK es mit einer Umgehung wie der in Chromium verwendeten Heuristik abfedern, aber grundlegend ist es ein Problem der darüberliegenden Schicht XInput2
    • Wenn man diesen Bug-Report und die verlinkten Issues liest, sieht man, dass mehrfach versucht wurde, das Problem zu beheben, man aber letztlich auf gewisse Heuristiken angewiesen war und dabei ständig schwerwiegendere Nebenwirkungen auftraten als das ursprüngliche Problem. Da die Ursache grundsätzlich im Unterbau von X11 liegt, scheinen weitere Verbesserungen erst dann sinnvoll möglich, wenn dort eine grundlegende Korrektur erfolgt. Da X11 inzwischen faktisch im Wartungsmodus ist, ist das schwer zu erwarten, solange Fans darauf beharren, dass „es perfekt funktioniert und keine weitere Arbeit nötig ist“. Am Ende bleibt wohl nur, auf den Wechsel zu Wayland zu warten
  • Bei der Aussage „Ich habe jeden Schritt mit Valgrind verifiziert“ dachte ich: Eigentlich ist das selbstverständlich, aber ich habe das in der Praxis nie selbst gemacht und auch selten gesehen, dass andere Entwickler es so handhaben. Normalerweise wurde Valgrind nur eingesetzt, wenn ein bestimmter Bug oder ein Performance-Einbruch auftrat. Wenn man Werkzeuge wie Valgrind, besonders Memcheck und Helgrind, während der gesamten Entwicklung aktiv nutzt, steigt die Stabilität des Tools enorm, und Bugs lassen sich direkt beim Einführen beheben, statt später mühsam Hunderte Commits durchsuchen zu müssen

    • Ich selbst habe bei C und C++ immer regelmäßig valgrind verwendet. Die Fehler, die valgrind und asan finden, äußern sich oft nicht als unmittelbarer Crash, sondern als unauffällige, aber intermittierende und extrem lästige Bugs, deren Ursache sehr schwer zu finden ist. Darunter sind auch Sicherheitslücken. Und wenn sich viele kleine Memory Leaks nach und nach ansammeln und später ein wirklich großes Problem auftritt, wird die Ursachenanalyse durch all diese kleinen Lecks noch schwieriger. Deshalb ist ein proaktiver Einsatz sinnvoll
    • Ich habe Valgrind, insbesondere memcheck, proaktiv genutzt, um erst einmal die leicht behebbaren Probleme aus dem Weg zu räumen, bevor ich Bug-Reports im Detail debugge. Das größte Problem ist allerdings der hohe Performance-Overhead, sodass es sich nicht gut für interaktive Nutzung eignet. Tests hin und wieder einmal unter Valgrind laufen zu lassen, bringt aber sehr viel
    • Allerdings ist Valgrind extrem langsam und teuer, sodass es schwer direkt in den Editier-Kompilier-Test-Zyklus passt. Für Testzyklen wie Nightlies oder Automatisierung kann man es einsetzen, aber für eine gute Integration ist zusätzliche Arbeit nötig
  • Bei der Nutzung von Ghostty ist es auf dem Mac sehr störend, dass man in nano nicht mehrere Zeilen einfügen kann. Es scheint damit zusammenzuhängen, wie das Terminal „bracketed pasting“ behandelt, aber seltsamerweise gibt es dieses Problem bei iTerm2 oder Terminal nicht

    • Ich bin mit Ghostty als Terminal-Ersatz zu 99 % zufrieden, aber die Copy-and-paste-Probleme sind wirklich frustrierend und begegnen mir täglich
    • Seit ich Ghostty auf einem neuen Computer als Standardterminal verwende, fehlt mir am meisten die Suchfunktion. Normalerweise nutze ich oft ein Tastenkürzel, um in der Ausgabe etwas Bestimmtes zu finden, aber das geht hier nicht. Tatsächlich ist das auch in diesem Issue das am häufigsten genannte Problem
    • Bei einer Ubuntu-Fernverbindung lässt sich nano innerhalb von Ghostty gar nicht starten
      $ nano
      Error opening terminal: xterm-ghostty.
      
      In derselben Umgebung funktioniert es im macOS-Terminal oder im integrierten VS-Code-Terminal problemlos
    • Das könnte tatsächlich ein echter Bug sein, daher würde ich empfehlen, einen Bug-Report einzureichen
    • Dass es keine Befehlssuche wie mit Cmd+F gibt, ist der größte Schmerzpunkt
  • Ich frage mich, ob Rust anstelle von Zig Speicherfehler verhindert hätte. Da das meiste aus der Zig/C-Interaktion entstand, wäre es wahrscheinlich ähnlich gewesen. Ich spekuliere hier als Go-Entwickler und frage mich, ob es überhaupt eine Sprache gibt, die bei umfangreicher Interaktion mit C deutlich mehr Sicherheitswerkzeuge bietet

    • Rust hätte ein Problem verhindert, aber beim Rest wäre es gleich gewesen. Wie du schon angemerkt hast, lagen alle Probleme an den Grenzen und der Semantik der C-API, daher hängt die tatsächliche Sicherheit von der Qualität des Wrappers ab. Rust hat bereits ein gut erprobtes Wrapper-Ökosystem, sodass das Risiko dort geringer wäre als bei einem selbstgebauten Wrapper in Zig, aber grundsätzlich wäre es nicht so anders. Ein Beispiel für einen undefined memory access, den Rust vermutlich abgefangen hätte, ist der Teil, der in diesem PR behoben wurde. Tatsächlich wurde ungültiger Speicher in den ersten Frame kopiert, aber er wurde nirgendwo verwendet oder weitergegeben, sodass es nicht gravierend war. Trotzdem war es eindeutig nicht korrekt
    • Auch Rust erfordert an der FFI-Grenze zu C/GObject manuelle Speicher- und Lebensdauerverwaltung. Der Borrow-Checker von Rust kann die Speichernutzung externen Codes nicht überprüfen
    • Einer der Kerngedanken des Artikels ist, dass es mit der Kombination aus zig + valgrind viel weniger Speicherprobleme gab als erwartet
    • C-Bindings in Rust zu schreiben ist deutlich schwieriger. Deshalb könnte es sogar sein, dass sich GTK-Bindings in Rust gar nicht schreiben ließen
  • Bei der Nutzung GPU-basierter Apps wie Ghostty, Alacritty, WezTerm oder Zed hatte ich den Eindruck, dass sie schneller und besser sind. Ironischerweise machen solche Apps aber die Grenzen der Nvidia-Treiber noch deutlicher sichtbar. Früher ist mir das nicht aufgefallen, weil ich kaum GPU verwendet habe, aber sowohl in Umgebungen ohne Compositor wie Regolith i3wm als auch unter sway/Wayland waren Bildschirmfreigabe, Aufwachen aus dem Sleep, Abstürze usw. mit dem Nvidia-Treiber wirklich miserabel. Ich habe verschiedene Versionen (550/560/575/580) ausprobiert, und es war überall dasselbe. Erst kürzlich ist mir klar geworden, dass das schon lange so schlecht ist

    • Ich habe unter Wayland Ähnliches erlebt. Unter X11 läuft sowohl eine 1050Ti als auch eine alte AMD-Karte, die den radeon-Treiber braucht, problemlos, wenn man Compositing-Effekte deaktiviert. Unter Wayland hatte ich dagegen Probleme wie Ruckeln, Abstürze oder Bildfehler
  • Ich konnte eine größere App bauen, ohne dass das GTK-Typsystem den Code beeinflusst hat. Statt Klassenvererbung und Erweiterung habe ich aber alle Komponenten nur dadurch verbunden, dass ich Lambdas gebunden habe. Dadurch war das Ergebnis nicht allzu chaotisch, aber Entwickler, die an den klassischen GTK-Stil gewöhnt sind, hätten es vielleicht verwirrend gefunden

  • Ich verstehe den übertriebenen Hype um Ghostty an sich nicht. Bei einer UI, die im Grunde nur Tabs und Kontextmenüs hat, frage ich mich, ob all diese Integrationsarbeit und Umschreibungen den Aufwand wert sind. Ich vermute, dass man vielleicht eine leistungsfähigere GUI-Umgebung wie bei iTerm2 hinzufügen will. Kitty zeichnet Tabs direkt mit OpenGL und erlaubt vollständige Anpassung, spart dadurch Zeit bei der Integration in komplexe Frameworks und implementiert sehr praktische Funktionen schneller, etwa die Ausgabe des letzten Befehls in einen Pager zu wrappen. Auch Remote-Unterstützung funktioniert in Kitty gut

    • Die UI von Ghostty besteht nicht nur aus Tabs, sondern bietet mehr als man denkt: Splits, Banner wie „Prozess beendet“, Bestätigungsdialoge zum Schließen, Dialoge zum Ändern des Titels, Erkennung unsicherer Einfügevorgänge, eine animierte Benachrichtigungsglocke, ein Dropdown-Terminal, Fortschrittsbalken und mehr. Auf dem Mac gibt es außerdem Apple-Shortcuts- und Spotlight-Integration. Natürlich hätte man all das auch ohne GUI-Toolkit rein selbst bauen können, aber die Mission von Ghostty ist es, die nativen Toolkits der jeweiligen Plattform zu nutzen, damit sich die App „wirklich nativ“ anfühlt. Wenn einem dieser Ansatz nicht gefällt, ist etwas wie Kitty mit textbasierten Tabs ebenfalls eine gute Wahl. Man kann je nach Werten und Prioritäten wählen. Für die Zukunft sind außerdem verschiedene GUI-Erweiterungen geplant sowie eine tiefere Integration nativer Plattformfunktionen wie iCloud-Synchronisierung
    • Bei der Behauptung „Kovid hat Features schneller implementiert“ ist Vorsicht geboten, weil dieses Konto eine Vorgeschichte hat, die den Verdacht nahelegt, dass es sich um Kovid selbst handelt. Ich habe tatsächlich auf HN und Reddit gesehen, wie Kitty scheinbar neutral vorgestellt wurde, während gleichzeitig der Entwickler kritisiert wurde. Es gibt dazu sogar einen Verweis auf frühere Kommentare hier
    • Zusätzlich zu der positiven Beschreibung oben halte ich das Auftauchen von „libghostty“ für einen echten Gamechanger. Wie WebKit ist es eine leistungsfähige Terminal-Implementierung, die sofort funktioniert, wenn man sie einfach per Drop-in einbindet
    • Ich selbst bin bei der Suche nach einem Terminal auch viel herumgesprungen. Ghostty ist nicht mein perfektes Ideal, aber ich bin dorthin gewechselt, als ich nichts halbwegs Zufriedenstellendes gefunden habe. Auch das ist für mich schon eine durchaus sinnvolle Wahl. Oft ist es gerade ein Vorteil, dass man es nicht wegen eines einzelnen Killer-Features nutzt, sondern einfach dabeibleibt, weil es keine großen Probleme macht
    • Die Schriftarten werden in Ghostty deutlich schöner gerendert als in Kitty. Neovide sieht noch besser aus, unterstützt aber noch keine Tabs und verbraucht auch mehr Akku