- GhosttyチームはGTKアプリケーションを全面的に書き直し、GObject型システムを積極的に活用した
- この過程では、Zig言語との統合とValgrindによるメモリ問題の確認が重要な役割を果たした
- GObjectシステムの採用により、従来よりもメモリ管理とカスタムウィジェット実装が簡素化された
- Valgrindを活用した結果、Ghosttyのメモリ安全性が大きく向上したことを実感した
- 新しいGhostty GTKはソースビルドのデフォルトとなり、1.2リリースに含まれる予定
序論
- GhosttyはmacOS、Linux、FreeBSDをサポートするクロスプラットフォームのターミナルエミュレータ
- 各プラットフォームごとにネイティブGUIフレームワークを使用して差別化している
- macOS: SwiftおよびXcodeベースの大規模アプリケーション
- LinuxおよびBSD: GTKベースのアプリケーションで、X11/Waylandなどと直接統合
- 共通コアはZigで書かれており、C ABI互換APIを提供する
- 既存構造でGTKアプリケーションを書き直した理由は、原文のPRを参照可能
- 本文では、GObject型システムとの連携とValgrindで検証したメモリ問題に重点的に触れる
GObject型システムとZig
- GTKを使う場合、基本的にGObject型システムとインターフェースする構造になる
- 過去にはGObjectシステムを避けつつ、参照カウントのないZigオブジェクトとGObjectオブジェクトのライフサイクルを直接合わせようとしたが、メモリ解放が正しく行われない問題が繰り返し発生した
- 例: Zig側のメモリは解放されたのにGTK側のメモリはまだ生きている、あるいはその逆の状況が繰り返された
- このアプローチは正しさの問題だけでなく、**GTK固有機能(イベントシグナル、プロパティバインディング、アクション)**の利用も難しくした
- 具体例として、設定(config)構造体をリロードする際に接続されたすべてのGUI要素が一貫して更新される必要があったが、この過程は複雑でエラーも多かった
- 現在はZigの
Config構造体をラップする参照カウント付きGhosttyConfig GObjectとして管理しており、プロパティ変更通知によってアプリケーション全体に自然に変更が伝播する
- カスタムGObjectウィジェットの作成も容易になり、BlueprintなどのモダンなGTK UI技術が利用可能になった
- 最近ではBlueprintの導入により、GTKタイトルバータブ、アニメーションするベル枠線などの新機能を導入しやすくなった
ValgrindとGTK、Zig
- 開発過程全体でValgrindを使い、メモリリークや未定義メモリアクセスなどの問題を体系的に検証した
- GTKアプリケーションのValgrind検査は難しく、大量のsuppressionファイルが必要だった(80%はGTK自体、残りはサードパーティライブラリおよびGPUドライバ)
- 繰り返しの検査により、特定のケースでのみ発生する複雑なメモリバグを事前に発見できた
- 例: GObject
WeakRefを正しく初期化しないと、対象オブジェクトが後で解放される際に未定義メモリアクセスが発生するが、Valgrindで事前に検出できた
- 実際の経験では、**Zigコードベース内部の問題は合計2件(リーク1件、未定義アクセス1件)**にすぎず、それもサードパーティC APIとの連携過程で発生したものだった
- Zigのデバッグ用アロケータおよびValgrind統合機能の有効性も証明された
- その他に見つかったメモリ問題の大半は、C API境界とGObjectシステムの複雑なライフサイクル管理に起因していた
- 結論として、複雑なライブラリのC APIを安全に使うにはValgrindのようなツールが必要
- Zigのメモリ安全性支援機能は理論的な議論にとどまらず、実証的なプロジェクト経験でも効果が確認された
結論
- 今回はGhosttyのGUI部分を5回目としてゼロから作り直した経験だった
- GLFW、macOS SwiftUI、macOS AppKit+SwiftUI、Linux GTK(手続き型)、Linux GTK+GObject型システムの順
- 繰り返しの再実装の過程で、毎回新しい教訓と技術的成長を得てきた
- 今回の経験をmacOSプロジェクトにも一部適用する計画
- Ghostty GTKシステム保守チームの積極的な協力も強調した
- 新たに書き直されたGhostty GTKアプリケーションはすでにソースビルドのデフォルトとなっており、1.2正式リリースに適用される予定
1件のコメント
Hacker Newsのコメント
GTK を直接触った経験はありませんが、今の説明を聞く限りでは、Zig で Godot バインディングを作るときに直面する問題ととても似ていると感じます。Godot にはクラス、仮想メソッド、プロパティ、シグナルなど、OOP の概念が非常に多くあります。そして、それらすべての概念を扱え、ユーザー定義のオブジェクトや属性を作れる C API を提供しています。エンジンオブジェクトのライフタイム管理を自分で行う必要があり、参照カウントされるオブジェクトのツリー構造もあります。特にライフタイムの問題を Zig の慣用句に合わせて最適な API にまとめようとすると、非常に複雑になります。こうしたことを考える中で、oopz ライブラリ も作りました。API の現状はまだこの程度ですが、実際の例はこちらで見られます。Ghostty フロントエンドを Godot 拡張としても作ってみたいです
良いプログラミングとは、結局はシステムが提供するやり方に合わせることだとよく分かる例です。OOP やメモリ管理をどう考えるにせよ、GTK を使うなら GObject 型システムと何らかの形でインターフェースを組むしかありません。避けたくても結局は避けられません。ですが私たちはそれを避けようとし、その結果、参照カウントされるオブジェクトと非参照オブジェクトのライフタイムを結び付けるところでとんでもない混乱が起きました。Ghostty GTK アプリでは、Zig のメモリを解放すると GTK のメモリが解放されない、あるいはその逆といったバグが繰り返し発生しました
OOP とメモリ管理に関する私の立場はさておき、GTK を使えば GObject 型システムに絡まざるを得ないという点には同意します。だからこそ、私は最初から GTK を直接使わないことにしました。統一された UI テーマの価値は理解していますが、私には GTK の利点が、その対価を払ってまで使うほど大きいとは感じられませんでした。オープンソースアプリで GTK 周辺を触った経験から、GTK と GObject の考え方は自分の性格にあまり合わないという確信を持っています。GTK が存在すること自体は嫌いではありません。自分は使わない側を選べば済むので問題ありませんが、その選択を私の権利だと見なさない人が一部いるのは奇妙です。数ある GUI ツールキットのひとつにすぎず、技術的には非常によく磨かれたツールキットであるにもかかわらず、もし GTK のシェアがもう少し低ければ、その polish が構造的にもっと優れた別のツールキットに注がれていたのではないかとも思います。もちろん、自分に良いものが他の全員にとって良いとは限りません。GTK を使っている人のうち、どれくらいが仕方なく使っていて、どれくらいが最高のツールだと感じているのか気になります
面白いことに、Ghostty や一部の他の GTK アプリでは、マウスがウィンドウの外に出て戻ってきたあと、最初のスクロールクリックが無視される現象があります。2015 年に最初に報告された非常に古いバグが原因です。バグリンク。現在に至るまで修正予定はなく、メンテナーは Wayland を待てという立場です
「Valgrind ですべての段階を検証した」という部分で、実はあまりに当たり前に聞こえるものの、自分では一度もやったことがなく、他の開発者がそうしているのもあまり見たことがありません。普通、Valgrind は特定のバグや性能低下が現れたときにだけ使われていました。開発の全過程を通して Valgrind、特に Memcheck や Helgrind のようなツールを能動的に使えば、ツールの安定性は大きく向上し、バグも導入された時点ですぐに潰せるので、あとから何百ものコミットを掘り返す苦労も減らせるのではないかと思います
Ghostty を使っていて、Mac で nano に複数行を貼り付けられないのがとても不便です。端末が “bracketed pasting” をどう処理するかによるようですが、不思議なことに iTerm2 や Term ではこの問題がありません
Zig の代わりに Rust を使っていたら、メモリエラーは防げていたのか気になります。大半は Zig/C の相互運用から出た問題なので、Rust でも同じようなものだった気がします。Go 開発者の立場からの推測ですが、実際に C と大規模に連携するとき、もっと多くの安全性ツールを提供する言語があるのかも気になります
Ghostty などの GPU ベースのアプリ(Alacritty、WezTerm、Zed など)を使って、より速くて良いと感じました。しかし皮肉なことに、こうしたアプリは Nvidia ドライバの限界をより鮮明に見せてくれます。以前は GPU をほとんど使っていなかったので気づきませんでしたが、Regolith i3wm のようなコンポジタなし環境でも、sway/wayland 環境でも、画面共有、スリープ復帰、クラッシュなどで Nvidia ドライバがあまりにひどかったです。複数のバージョン(550/560/575/580)を試しても全部同じでした。昔からこんなに悪かったのだと最近になって気づきました
GTK の型システムがコードに影響しないまま、大きなアプリをひとつ作ることはできました。ただしその代わり、クラス継承や拡張ではなく、ラムダをバインドする形ですべてのコンポーネント同士を接続しました。結果としてそこまで汚くはなりませんでしたが、伝統的な GTK スタイルに慣れた開発者には混乱したかもしれません
Ghostty に対する大げさな盛り上がり自体が理解できません。タブとコンテキストメニューしかない UI に、ここまでの統合作業やリライトをする価値があるのか疑問です。iTerm2 のような強力な GUI 環境まで追加しようとしているのではないかと推測しています。Kitty は OpenGL で直接タブを描画するので完全なカスタマイズも可能ですし、複雑なフレームワークへの統合に時間を使わず、とても実用的な機能(直前のコマンド結果をページャでラップして出力するなど)も素早く実装しています。Kitty はリモートもよくサポートしています
libghosttyの登場がゲームチェンジャーだと思います。WebKit のように、誰でも drop-in するだけですぐに動かせる強力な端末実装です