Talk: Introducing Ghostty and Some Useful Zig Patterns

Mitchell Hashimoto

トーク:Ghosttyの紹介と役立つZigパターン

原文は Mitchell Hashimoto により に公開されました。 このブログを購読する

これはZig Showtimeで行ったトークのテキスト版です。動画でご覧になりたい方はYouTubeで視聴できます:Zig Showtime: Ghostty。動画の最後にはQ&Aセッションも収録されていますが、本記事では省略しています。

スライド1

こんにちは!今日はGhosttyについてお話しできるのをとても楽しみにしています。GhosttyはZigでゼロから作られた、まったく新しいターミナルエミュレータです。

補足:執筆時点では、Ghosttyはまだ一般公開されていません。このことはトーク中でも何度か触れていますが、誤解のないよう最初にお伝えしておきます。Ghosttyは無料かつオープンソースとして、2024年中にリリースする予定です。現在はソースコードへのアクセスを含むクローズドベータを実施中です。この点についてはトークの各所でも触れています。


スライド2

Mitchellと申します!私はとにかく、とにかく、とにかくコードを書くのが大好きな人間です。

過去にはいくつか有名なソフトウェアプロジェクトを立ち上げました。ご存知の方もいるかもしれません——Vagrant、Terraform、Vaultなどです。ただ、どれももう何年も携わっていません。

私はいつも何かしらのサイドプロジェクトを抱えているタイプでもあります。本当に、いつもです。10年ほど前にはHearthstoneのログ解析・リプレイソフトを作ってプロのeスポーツチームに売却しました。数年前には自分でメールサーバーをゼロから作り、今でもいくつかのドメインのメール運用に使っています。そして今は——ターミナルエミュレータを作っています。

このスライドは夕食どきに作ったのですが、とてもお腹が空いていたので、食べている写真をたくさん並べてみました。別に食べ物に特別な情熱があるわけではありません。ただ…今この瞬間、とてもお腹が空いていただけです。そして、部屋全体をまとめるようなテーマ要素があるのが好きなんです。


スライド3

でも今回のトークはGhostty、ターミナルエミュレータについてです!

先に進む前に、Ghosttyがどんな見た目かお見せしましょう!この1枚のスクリーンショットからも、Ghosttyがかなり多機能であることが分かります!

GhosttyはmacOSとLinux向けのネイティブアプリです。画面分割、トゥルーカラー対応、vimも問題なく動作し、複数のフォントスタイル(ボールド、イタリックなど)やKittyグラフィックスプロトコルなどに対応しています。しかも、これはこのスクリーンショットで見えている範囲だけの話です!


スライド4

ターミナルエミュレータが何かご存知ない方のために説明すると、おそらく一度は使ったことがあるはずです。ここにいくつか例を挙げます。他にも数十、数百と存在します。


スライド5

これだけたくさんあるのに、なぜまた新しいものを作るのでしょうか?


スライド6

根本的に見ると、これが私から見た現在のターミナルエミュレータの状況でした。高速なターミナル、多機能なターミナル、ネイティブなターミナルがあります。最大でも2つまでしか同時に満たせません。


スライド7

Ghosttyは——私の考えではすでに達成していますが——すべての特性を満たすことを目指しています。


スライド8

高速、高機能、ネイティブな使い心地は決して排他的ではありません

Ghosttyは現時点でも十分に良い出来で、表の緑のチェックマークに値すると考えていますし、まだまだ改善できる点はたくさんあります。

また、注意点として、私が考えるネイティブなプラットフォーム体験の基準は、アプリがそのプラットフォーム専用に作られたかのように感じられ、動作することです。この基準は決して厳しすぎると思っていません。例えば、Alacrittyは新しいウィンドウごとに新しいプロセスが立ち上がるので、ある意味ネイティブではないと言えます。Kittyはタブがネイティブでないウィジェットを使っているので、ある意味ネイティブではないと言えます。といった具合です(それぞれ他にも例はたくさんあります)。


スライド9

つまり「基盤」のレベルで私が目指しているのは、Alacritty並みの速度、Kitty並みの多機能さ、iTerm(macOS向け)並みのネイティブ統合、といったところです。


スライド10

しかし、さらに先を目指したいと思っています。ターミナルを、テキストアプリケーション開発のためのモダンなプラットフォームにしたいのです。それは、ブラウザが(良くも悪くも)GUIアプリケーション開発のモダンなプラットフォームであるのと同様です。

ブラウザは1年に数十(いや数百?)もの新機能をリリースします。猛スピードで革新し、アプリケーション開発者をワクワクさせます。これはやりすぎかもしれませんが、ターミナルも今よりずっと速く革新できると思っています。

ここにいくつか例を挙げます:

  • プログレスバー!なぜ毎回(しかも大抵は出来の悪い)プログレスバーを再描画しなければならないのでしょう?
  • ドラッグ&ドロップ——エディタなどのアプリケーションがよりネイティブらしく振る舞えるように。
  • タブ/分割の制御——マルチプレクサがモダンなターミナルエミュレータの性能や機能を活用できるように。
  • マウスジェスチャー——TUIがフリックやマルチタッチ、慣性スクロールなどに反応できるように。
  • セキュリティ機能:ターミナルのエスケープシーケンスは今や恐ろしい領域です。これはまた別の機会に。もっと改善できます。
  • その他いろいろ!

スライド11

Ghosttyは速いのか?どうやって分かるのか?ターミナルエミュレータをベンチマークする方法はたくさんありますが、このトークで多くの時間を割くつもりはありません。言いたいのは、Ghosttyは「速い」ということです。「最速」だと主張するつもりはありません。

ここでは、FPSを計測しながらDOOMの炎のアニメーションをGhosttyで実行しています。これは非常に良いストレステストで、大量のセルを書き換えつつ、スクロールバックも発生させるからです。

ご覧の通り、Ghosttyは480〜500 FPS前後を維持し、アニメーションは非常に、非常に滑らかです。比較として、公式のmacOSのTerminal.app(素晴らしいコミュニティ製プロジェクトをあえて引き合いに出さないために選びました)は10 FPS未満で極めてカクカクしています。「高速」なターミナルエミュレータであるAlacrittyやKittyをお使いであれば、Ghosttyと同程度の数値が出るはずです。

重要なのは、Ghosttyが最速だと言いたいわけではなく、「高速」というカテゴリーに入るということで、私の表で緑のチェックマークを付ける根拠は十分にあると考えています。

ぜひご自身のターミナルでも試してみてください:https://github.com/const-void/DOOM-fire-zig/

テキスト版についての補足:スライドの画像は静止画なので動画は再生されません。Ghosttyではリンク先のプログラムがおよそ480〜500 FPSで動作します。


スライド12

Ghosttyは多機能なのか?これを定量的に測る方法は分かりませんが、ここでは私たちが対応している機能を定性的にリストアップしました。その多くは比較的珍しいものです。


スライド15

macOSでは、メインのGUIはSwiftでAppKitとSwiftUIを使って書かれています。タブはネイティブなタブ、分割はネイティブなUIコンポーネントで、マルチウィンドウも期待通りに動作します。Linuxでは、GUIはGTKで、実際のGTKウィンドウやその他のウィジェットを使っています。

エラーメッセージなどの機能も、専用のターミナルビューで実装しているわけではなく、実際にネイティブなUIコンポーネントを使っています。つまり、ターミナルの描画領域やコアロジックはクロスプラットフォームですが、ユーザーインタラクションはすべて、各OS向けに真のネイティブ体験を提供するよう専用に作り込まれているということです。


スライド16

では、技術スタックとプロジェクト自体についてざっと概要を見ていきましょう。

まずはプロジェクトについて。現在、プロジェクトはソースコードへのアクセスを伴うクローズドベータ段階にあります。クローズドベータにしている理由は、これが個人プロジェクトであり、自分自身を疲弊させたくないからです。クローズドベータなら、ゆっくりと人を招待し、問題を修正して、また繰り返す、という進め方ができます。

プロジェクトが最終的にリリースされる際には、無料かつオープンソースで、ライセンスはおそらくGPLになる予定です。「おそらく」と言うのは、テスターからの意見も歓迎しているからです。現在活発に議論されているのは、MITのようなより寛容なライセンスを選ぶかどうかという点です。

今すぐ参加できる公開Discordがあります。ベータテスターはそのDiscordから募集しています。また、個人ブログで時々開発日誌(devlog)も書いています。

技術的な面については、このトークでそのほとんどを詳しく説明するのでここでは時間をかけません。ただ、Zigで書かれており、ネイティブな部分は本当にネイティブである(macOSならAppKitなど)こと、そしてイベントループなど多くの依存関係を自前で書いていることだけ覚えておいてください。


スライド17

では、当然の疑問から始めましょう:なぜZigなのか?


スライド18

一言で言えば、コミュニティ、言語、そしてビルドシステムが気に入っているからです。言語とビルドシステムの機能は、このターミナルプロジェクトに非常に適していると思いますし、このトークではその点を強調したいと思います。


スライド19

あれこれ推測するのはご自由にどうぞ。私はこの質問自体が好きではないし、答える気もありません。私はZigを選び、Zigが好きです。先に進みましょう。


スライド20

ここでは、サブシステムごとのコードアーキテクチャの概要を示します。これらがGhosttyの主要なサブシステムとその説明です。次のスライドで視覚的な図をお見せします……


スライド21

これが、それらを組み合わせたランタイムアーキテクチャです。この図にあるものは、macOS上の「apprt」を除き、すべて100% Zigです。apprtは一部Swiftで書かれています。

基本的な考え方は次の通りです:

  1. アプリが起動し、エントリーポイントは「apprt」にあります。
  2. Apprtは1つ以上の「surface」の作成を担当します。surfaceとは、1つの対話的なターミナルを表すものです。Apprtはこれをウィンドウ、タブ、分割など、どこに配置しても構いません。コア側にとっては関係ありません!
  3. 各surfaceはIOスレッドとレンダラースレッドを起動します。
  4. IOスレッドはptyのファイルディスクリプタを作成し、設定されたコマンド(通常はシェル)を実行します。IOスレッドはptyの読み書き、エスケープシーケンスなどのターミナルイベントの処理を担当します。
  5. レンダラースレッドはターミナルの状態を変換し、一定のフレームレートでピクセルを描画します。レンダラーはフォントの整形やレンダリングも担当します。

スライド22

ターミナルエミュレータについて語るのはとても楽しいのですが、ここはZig Showtimeなので、トークの焦点をGhosttyで使っているZigのパターンに移していきます。

これらに特定の順序はありません。

オンライン読者への補足:このセクションの大部分では、実際にGhosttyのソースコード(もちろんGhosttyターミナル内で)の中でこれらのパターンを紹介しました。動画で該当のスライドまで早送りしていただければ、その様子をご覧になれます。


スライド23

まずは、comptimeインターフェースです!


スライド24

comptimeインターフェースとは、コンパイル時に判明する情報に応じて実装が切り替わる値のことです。

ここに示したソースコードは、Ghosttyから直接抜粋した実際の例です。ここで示しているのは、あるビルドオプションに依存する「フォントフェイス」の定義です。ご覧の通り、ビルド時の設定によってはCoreTextを、別の設定ではFreeTypeを、そしてWebベースのCanvas実装にも対応しています!

下部のペインでは、そのインターフェースが実際に使われている様子を示しています。これらのインターフェースを使うコードは、どのように動いているかを気にする必要はありません。定義されたインターフェースがあり、それを呼び出したり使ったりするだけです。


スライド25

comptimeインターフェースは、Ghosttyがプラットフォーム固有の機能を実装する主な手段であり、フォント、レンダラー、アプリランタイムなどで使われています。

主な利点は、実行時に決して変わらない実装について、ランタイムオーバーヘッドなしで実装を切り替えられることです。すべてのフィールドアクセスや関数ディスパッチはコンパイル時に決定されます。


スライド26

Zigコンパイラの特徴の一つは、実際に参照されたコードだけを解析することです。これはバグではなく仕様です。なぜなら、厄介な#ifdefのようなガードで不可視にしなくても、状況依存のコードを存在させることができるからです。

欠点は、comptimeインターフェースでは、すべてのビルドオプションをテストしないと、簡単にビルド失敗を引き起こしてしまうことです。

GhosttyではCIですべてのビルドオプションを網羅してテストしています。


スライド27

これは先ほどお見せしたのと同じスライドです。これらの機能やパターンが何を可能にするかを示すために、もう一度お見せします。


スライド28

ここでは、同じサブシステムについて、現在存在する実装と、将来的に可能な実装を示しています。これらの代替実装のほぼすべてがcomptimeインターフェースです。


スライド29

次は、もう少しcomptimeの話ですが、今度はデータテーブルについてです。


スライド30

これはデータテーブルの例です。ここでは、Kittyキーボードプロトコルの入力エンコーディング情報を見ています。

RawEntryのタプル形式は実際の利用にはあまり適していませんが、テーブルを簡単に構築するにはとても便利です。これらのどちらもpubではないことに注目してください——このファイルの外で使われることを意図していないのです。

これらの非公開(non-pub)なエントリを、次のように処理します……


スライド31

comptimeを使って、生のエントリを処理できます。ここでは、生のエントリのタプルを、より実行時に扱いやすい構造体に変換しています。

この変換処理はすべてコンパイル時に行われるため、ランタイムコストはかかりません。さらに、生のエントリはランタイムコードでは一切使われないため、最終的なバイナリのサイズにも影響しません。

このケースでは変換は主に素直なデータ変換ですが、もっと面白いこともできます……


スライド32

こちらはプラットフォーム固有のキーコードのテーブルからの例です。生データには各プラットフォーム(Mac、Windows、xkb経由のLinux、さらにはUSBコード)のキーコードが含まれています。しかし実行時には、ビルド対象のプラットフォームだけを含むentriesに変換します。

こうすることで、構造体はよりタイトになり、最終的なバイナリも小さくなり、実行時に条件分岐して検索する必要もなくなります。


スライド33

さらに一歩進めることもできます。こちらは、特定のモードで特定のキー入力に対してターミナルが実行中のプログラムに送るべきエスケープシーケンスを定義するデータテーブルからの例です。

その多くは、明確で繰り返しのパターンに従っています。すべてを手作業で入力する代わりに、comptimeで実行する関数を書いてデータをプログラム的に構築できます。

多くのプロジェクトでは、データ生成のためにシェルやPythonなどのスクリプトを用意することがよくあります。Zigなら、それらをすべてcomptimeで完結させられます。

補足:pcStyle内の関数本体を囲むcomptime {}ラッパーは、この関数が決して実行時に呼び出されないようにするためのトリックです。


スライド34

次は、comptimeデータテーブルを発展させて、comptimeでの型生成についてお話しします。


スライド35

まずは@Typeビルトインについて30秒で解説します。多くの人がこの存在を知りません。@Typeビルトインを使うと、comptimeに型を生成できます。驚くほど強力ですが、同時に驚くほど恐ろしいものでもあります。


スライド36

注意してください!これは本当に意味のある場面に限定して使うべきものです。「完全なメタプログラミング」に走らないよう注意が必要です。可読性を損ない、コンパイルも遅くなるからです。


スライド37

この例は、Ghosttyが対応しているさまざまな「モード」をどのように定義しているかを示しています。モードとは、実行中のプログラムが問い合わせたり設定したりできるターミナルモードで、ターミナルエミュレータの動作を変えるものです。モードは数百種類あります。

この例では、おなじみの非公開のentriesデータテーブルがあります。このテーブルを処理する方法の一つとして、すべてのキーを@Typeビルトインを使って網羅的なenumに変換しています。

enumを最初から定義しない理由は、各エントリに関連する追加のデータを、編集や参照がしやすいように一箇所にまとめておきたいからです。このケースでは、各モードが値を持っています。


スライド38

これはFontIndex構造体の生成を示しています。この構造体はGhosttyで特定のフォントを参照するために使われます。上位ビットでスタイル(regular、bold、italicなど)を、下位ビットで配列へのインデックスを表します。

スタイルに必要なビット数を求めることで、インデックスに必要な正確なビット数を決定できます。

もしスタイルを増やしてビット数が増えれば、インデックスで表現できるフォント数は制限されます。

補足:これは、特定のサイズが確保されていることを保証するテストと組み合わせています。そのため、万が一Styleのビットサイズを増やす場合でも、非常に意識的に行うことになります。


スライド39

次は、GhosttyがSwiftとどのように統合しているかについてお話しします。実際には、これはSwiftに限らず、CのAPIを呼び出せるあらゆる言語に適用できるより汎用的な解決策です。


スライド40

私たちが直面した問題は、モダンなmacOS開発では実質的にSwiftが必須だということです。確かにObjective-Cでもまともなアプリは作れますが、Appleがリリースしている新しい魅力的な機能やフレームワークはすべてSwift専用で、ObjCの先行きは見えています。


スライド41

Linuxでは、Zigは単にGTKのC APIを使います。ZigがCのAPIを呼び出せることはよく知られています。問題は、Swift(Macアプリ向け)はCのAPIをエクスポートできず、macOSアプリケーションはmainを自分で所有することを期待している点です。

しかし……SwiftはCを呼び出せます。そしてZigはCのAPIをエクスポートできます。


スライド42

そこで、Ghosttyを組み込み用のC APIとしてもパッケージ化することにしました。これを静的ライブラリとしてコンパイルし、SwiftベースのmacOSアプリがそれに依存し、Swift側からそのライブラリを呼び出す形です。


スライド43

例えば、Ghosttyの設定を初期化するSwiftのコードがこちらです。


スライド44

この方法なら、Linuxや将来的な他プラットフォームとの移植性を損なうことなく、極めてネイティブなmacOS体験を実現できます。

そしてさらに素晴らしいのは、Zigがいかに簡単に(特にZigビルドシステムのおかげで)プログラムをexeとしてもlibとしてもコンパイルできるようにしてくれたかということです。


スライド45

将来的には、このCライブラリ成果物を公式な成果物としてサポートし、誰でも完全に機能し、多機能で高速なモダンターミナルを自分のアプリケーションに組み込めるようにしたいと考えています。

ただし今のところ、これは未公開で、macOSアプリケーションでのみ使われています。


スライド46

以上が、Ghosttyで使っている主要なパターンのツアーでした。他にもたくさんありますが、1回のトークとしてはこれで十分でしょう。これらのパターンの中には、それだけで1つのトークになるものもあるかもしれません……

最後に、Ghosttyについてもう少しお話しして、このトークを締めくくりましょう!


スライド47

では、プロジェクトの今後はどうなるのでしょうか?

ターミナルエミュレーションの機能はかなり完成しています。Ghosttyで動かない、正しく描画されないといったプログラムを見つけることは極めて稀で、そういったものが見つかれば最優先のバグとして対応しています。数十人のテスターがGhosttyを日々の仕事で一日中使っており、信頼性は高いです。

現在の焦点は主にネイティブなプラットフォーム体験にあり、アプリをそのプラットフォーム専用に作られたかのようにより感じられるようにしています。例えばmacOSでは、ファイルベースの設定だけでなくGUIの設定ウィンドウに取り組んでいます。また、Appleデバイス間でのiCloudによる設定同期(データは自分で所有)なども進めています。

他にもいろいろあります……


スライド48

Ghosttyは高速ですが、まだまだ改善の余地は大きく残っています。すでに分かっている手軽なパフォーマンス改善もたくさんあり、Ghosttyはすでにかなり高速なので、これはワクワクすることです。

さらに、VTストリームのパーサーやハンドラをファズテストしたいと考えています。少しファズテストを行っただけでいくつかのバグが見つかったので、まだまだ潜んでいるはずです。

起動時間や入力レイテンシなど、これまでまったく計測してこなかったパフォーマンス領域もさまざまあります。これらのコードパスを劣化させないよう注意してきましたが、計測すれば大きな改善が見つかるはずです。

Ghosttyのメモリ使用量は今のところあまり良くありません。ひどいというほどではないですが、良くもありません。CPUやレンダラーのパフォーマンスを素晴らしいものにするために多大な時間と労力を費やしましたが、メモリ使用量については少し甘くなっていました。簡単に改善できる点もあります。例えば現在はスクロールバックバッファ全体を事前に確保しており、数メガバイトを消費していますが、これを動的に確保するようにするのは比較的簡単でしょう。


スライド49

現在、活発なベータプログラムを実施中です。プログラムに参加するにはDiscordへの参加が必要です。

私たちのプロセスは安定性を重視しています。一定数のテスターを招待し、未解決の問題がすべて解消されるまで次のグループは招待しません。ここで言う「問題」とは、バグや重大な機能不足のことを指します。すべての機能リクエストを実装するわけではありません。


スライド50

Ghosttyは来年(2024年)のどこかのタイミングで一般公開される予定です。それまでにベータプログラムを拡大し、最終的には数百人規模にしたいと考えています。

最初にリリースされるバージョンはバージョン1.0、あるいは少なくとも1.0のリリース候補になる予定です。この徹底的なテストプログラムの狙いは、安定していて多機能なプロジェクトとして、最初から良いスタートを切ることです。

先ほども述べましたが、GhosttyはFOSSとなり、ライセンスはおそらくGPLになる予定です。「おそらく」と言うのは、テスターからの意見をまだ受け付けているためですが、現時点での計画はGPLです。他の多くのターミナルもGPLです。特にlibghosttyとGPLに関する懸念があり、そこは現在活発に議論されている領域です。

安定したソフトウェアを提供したいということに加え、公開が遅れている理由の一つは、あと数週間で赤ちゃんが生まれる予定です 赤ちゃんが生まれたばかりです。とても、とても忙しくなることが分かっているので、フルスケールのOSSコミュニティを育て上げ、立ち上げることまで同時に自分に過度なプレッシャーをかけたくないのです。

新生児が家にいる中で、どうやって新しいトークを書き、発表し、この記事まで作成する時間があったのか、と何人かの親御さんから聞かれました。トークとこの記事(このような追記を除く)は、赤ちゃんが生まれる前に書き上げました。トーク自体は授乳の合間にこなすのはそれほど難しくありませんでした。そして、ええ、眠いです。😊


スライド51

❤️

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント