- ターミナル版 Emacs で Solarized のようなテーマを正しく表示するには、24ビットカラーそのもの以上に、ターミナル、terminfo、tmux、mosh が同じ色機能を最後まで理解しているかが重要
- RGB カラーは
ESC[38;2;<r>;<g>;<b>m の形で広く使われているが、標準に近いコロン構文と、実装上広まったセミコロン構文が併存しており、互換性を複雑にしている
xterm-256color と xterm-direct はそれぞれ 256 色パレットと 16,777,216 色を広告するが、terminfo のパレット中心モデルでは 直接 RGB カラーをきれいに表現できない
- 実際の環境では
ncurses-term の -direct エントリ、tmux-direct、mosh 1.4 系、vscode-direct のような代替 TERM 値が必要になることがあり、一部のレイヤーはいまだにセミコロン方式しか受け付けない
- 現代的なターミナル機能を安定して使うには TERM と terminfo だけでは不十分で、ターミナル名・バージョン別の機能をより速く更新できる別の表現方式が必要
ターミナル版 Emacs で 24 ビットカラーが必要な理由
- グラフィカル環境の Emacs は自動的に 24ビットカラーをサポートし、Solarized のようなテーマやフォントを正しく表示する
- ターミナルで同じテーマを使うと、色表現が制限され、画面の見栄えが落ちることがある
- 人気のターミナルはかなり前から 24 ビットカラーをサポートしていたが、プログラム側でそれを使うには機能検出と設定の両方が合っている必要がある
- 目的は 2 つある
- 現在のターミナル環境で truecolor サポートを有効にする方法を見つける
- Emacs の色問題が 1970~1990 年代のターミナル標準とどうつながっているかを確認する
ANSI エスケープコードと色拡張の歴史
- 初期のハードウェアターミナルはそれぞれ異なる制御コード体系を使っており、移植可能なソフトウェアを作るには ANSI による標準化が必要だった
- ANSI escape codes は 1970 年代から続いており、色においては SGR(Select Graphics Rendition) が中核となる
- 太字または明るさ
- イタリック
- 点滅
- 前景色と背景色
- その他の文字表示属性
- 当初の色は RGB キューブの 8 つの頂点に対応する 3ビットカラーだった
- その後、明るい色または太字ビットが追加され、16 色へと拡張された
- “bright black” は濃いグレーに相当する
- 1999 年、Todd Larason が xterm に 256 色サポートのパッチを追加した
- 6x6x6 の RGB カラーキューブのサンプリング
- 24 段階のグレースケールランプ
- まれではあるが今もサポートされる 88 色バリアントも存在する
24 ビットカラー構文が生まれた経緯
- 8 ビットカラーと 24 ビットカラーは、互換ターミナルでは次の形式で設定される
ESC[38;5;<n>m: パレットの色番号 n を前景色に設定
ESC[38;2;<r>;<g>;<b>m: RGB 値 r, g, b を前景色に設定
- 数字の
5 と 2 は ISO 8613-6、すなわち ITU T.416 の拡張色モードに対応する
2: RGB 空間の直接色
5: インデックスカラー
- 標準化の流れは次のとおり
- 1970 年代: ANSI がターミナルエスケープシーケンスを標準化し、ANSI X3.64 と ECMA-48 へとつながった
- 1979 年: ECMA-48 第 2 版が SGR 30~37、40~47 を 3 ビットの前景色・背景色に割り当てた
- 1984 年: ECMA-48 第 3 版が既定の前景色・背景色という概念を導入し、39 と 49 を割り当てた
- 1991 年: ECMA-48 第 5 版は 38 と 48 を「将来の標準化用」として残し、ISO 8613-6 への手がかりを置いた
- 1993 年: ISO 8613-6 が 38 と 48 を拡張前景色・背景色モードとして定義した
- 下位パラメータの区切りにはコロンを使うほうが標準に近いが、実装ではセミコロンが広く採用された
ESC[38;5;3m は、SGR 38 を知らないターミナルでは 38, 5, 3 という別々のパラメータとして解釈されうる
- Thomas Dickey の xterm FAQ では、当時 ITU T.416 の写しが高価で、RGB 値を他の SGR パラメータと同様にセミコロンで区切ってしまったのは誤りだったと説明している
- 関連するタイムラインは次のとおり
- 1999 年: Thomas Dickey が Todd Larason の 256 色パッチを xterm にマージし、曖昧なセミコロン構文が入った
- 2006 年: Konsole が xterm と同じセミコロン構文で 256 色と 24 ビット truecolor をサポート
- 2012 年: xterm が標準に近いコロン構文も受け付けるよう修正された
- 2016 年: Windows 10 の組み込みコンソールが ANSI エスケープコードと 24 ビットカラーをサポートしたが、セミコロン構文を使用した
- 2019 年: Windows Terminal も ANSI エスケープコードをサポートするが、セミコロン構文を使用する
- 2022 年: Microsoft がレガシー VGA スタイルのコンソールサブシステムから ANSI ターミナルエミュレーションへのエコシステム移行を発表した
- 2022 年: Konsole が標準互換のコロン構文をサポートした
terminfo が色機能を表現する方法
- terminfo はターミナル機能データベースと、適切なエスケープシーケンスを生成する機能を提供する
TERM 環境変数はプログラムが使う terminfo レコードを指し、ssh 接続でも自動的に渡される
toe はインストール済みの terminfo レコード一覧を表示し、infocmp は特定レコードの機能確認に使う
- 色に関して重要な機能は 3 つある
colors: ターミナルがサポートする色数
setaf: 前景色の設定
setab: 背景色の設定
xterm-256color の setaf は、色番号に応じて異なるシーケンスを出力する
- 0~7: ANSI 30~37 の SGR パラメータを使用
- 8~15: 非標準の 90~97 の明色を使用
- それ以上:
38;5;<n> 形式の 256 色パレットを使用
xterm-direct は colors を 16,777,216 と広告する、24 ビット RGB 用の terminfo エントリである
ccc を無効にし、色インデックスへ新しい RGB 値を割り当てられないことを示す
initc, oc, rs1 のような実行時色変更関連の項目もない
setaf と setab はコロンベースの 38:2::... 形式を使う
terminfo の直接色モデルの限界
- terminfo と ncurses のプログラミングモデルは、基本的に パレットエントリ中心である
- N 個のパレットエントリがある
- 各エントリには既定の RGB 値がある
- ターミナルがその値を変更できるというモデルを前提としている
-direct ターミナルは 16,777,216 個のパレットエントリがあるかのように表現することで、24 ビットカラーを表している
- 各エントリは 8:8:8 の RGB キューブに対応する
- 値は変更できない
- 互換性のため、
xterm-direct 方式は最も暗い青 7 色を既定の ANSI 8 色との互換性確保に使っている
- 黒は除外される
- 既存プログラムが
setaf の意味を前提にして、ほとんど見えない濃紺を出力してしまうのを防ぐための仕組みである
- このため、
-direct 方式と -256color 方式は相互互換ではない
- プログラムは 256 色がインデックスカラーであり、16,777,216 色が直接色であることを理解する必要がある
- 最も暗い青 7 色を避けるべきという例外もある
- termwiz 関連の問題では、プログラムが xterm-256color パレットを期待していたが、実際には読みにくい濃紺系を出力していた
- その issue は執筆時点では open だったが、@quark-zju が修正を取り込み、現在の termwiz は妥当に動作する
- 要点はターミナル自体よりも terminfo の制約に近い
- 24 ビットカラーをサポートするターミナルは、xterm の 256 色パレットと RGB 値の動的変更もサポートしているように見える
- terminfo は現代のターミナルエミュレータのエコシステムを正確かつ迅速に表現するには不十分である
TERM 設定とセミコロン互換性
- terminfo は多くのプログラムが使うため、
TERM 値は正しく設定しなければならない
- コロンベースの SGR 構文に統一したくても、多くの環境はいまだにセミコロン構文しかサポートしない
- terminfo エントリはビルディングブロックで構成される
xterm+indirect を調べた結果、vscode-direct が最も適しているように見えた
- Microsoft 系ターミナル向けであるため、Windows Terminal と Windows Console に十分近いと判断された
- すべての機能を監査したわけではないが、動作はした
- 多くのサーバーには
-direct terminfo エントリがインストールされていない可能性がある
- ほとんどのシステムでは、既定の terminfo データベースは
ncurses-base パッケージにある
- 拡張ターミナルエントリには
ncurses-term パッケージが必要になる
- ncurses には執筆中に
winconsole terminfo エントリが追加されたが、24 ビットカラーはサポートせず、まだどの ncurses バージョンにもリリースされていない
Emacs が truecolor を検出する方法
- Emacs は TTY で truecolor サポートを検出する方法を文書化している
M-x eval-expression で (display-color-cells) を実行すると、Emacs が 16,777,216 色を認識しているか確認できる
- Emacs の文書では
-direct モードの terminfo の限界にも触れている
RGB 機能を持つターミナルでは、#000001 から #000007 までを直接 RGB ではなくインデックスカラーのように扱う場合がある
- これは direct color を知らないアプリケーションとの後方互換性を保つためである
- 問題になるなら、
setb24, setf24 を持つカスタムターミナル定義を使える
- Emacs はまず
setf24 と setb24 文字列を探し、なければ代替機能として RGB を使う
- 確認したシステムの terminfo エントリには
setf24 は含まれていなかった
入れ子のターミナルでは全レイヤーが一致する必要がある
- 一般的な作業フローでは、複数のターミナルレイヤーが入れ子になる
- ローカルデスクトップでグラフィカルなターミナルエミュレータを開く
- リモートマシンや VM に mosh で接続する
- tmux を起動する
- Emacs 内部ターミナル、Asciinema、GNU Screen などをさらに使う
- 各レイヤーはそれぞれ独自に ANSI エスケープシーケンスの状態機械を実装している
- 24 ビットカラーを動作させるには、すべてのレイヤーが内側の
TERM 値の terminfo が生成したエスケープシーケンスを理解し、外側の terminfo に合わせて正確に通過させる必要がある
- そのため、関連ソフトウェアは十分に新しい必要がある
- 現在の Ubuntu LTS は mosh 1.3 を提供するため、mosh-dev PPA を有効にする必要があった
- 各レイヤー内の
TERM も正確でなければならない
- tmux 内では
tmux-direct を使う
- mosh には標準 terminfo がないため、十分に近い値を選ぶ必要がある
環境ごとの設定
最終結果と残る問題
- 設定後、tmux と mosh の中の Emacs でもテーマが 24ビットカラーで表示されるようになった
- 残る問題は 3 つに整理できる
- ターミナル同士で構文と機能に完全な合意がない
- terminfo は機能問い合わせの標準的な経路である
- terminfo は限定的で、ときに不正確であり、新しいバージョンのリリースも頻繁ではない
terminfo の先にある方向性
- 現代的なターミナル機能を十分に活用するソフトウェアなら、terminfo から離れる必要がある
- あり得る方向性は次のとおり
- 広くサポートされている
TERM 変数は引き続き使う
- OS やディストリビューションの古い terminfo だけに依存せず、プログラムがターミナル機能を独立して把握できるようにする
- 頻繁に更新されるライブラリに、ここ 10 年ほどのターミナルエミュレータ情報を名前とバージョン基準で持たせる
- もはや扱いにくいハードウェアターミナルのサポートは含めない
- OS 提供の terminfo ファイルと terminfo ファイル形式は引き続きサポートしつつ、どの情報が最新か判断できるプロトコルを用意する
- 任意の
TERMVERSION により、2022 年の Konsole と 2023 年の Konsole のようなバージョンごとの機能差を区別する
- 24 ビットカラー、256 色パレットアニメーション、URL リンク、Kitty グラフィックプロトコル のような現代的機能を曖昧さなく表現する
- レガシープログラム向けの後方互換方式も考えられる
.bashrc で実行するプログラムが TERM と TERMVERSION を使って $HOME/.terminfo/ にバイナリ terminfo ファイルを生成する
RGB, setf24, setb24 のような明確な 24 ビットカラー機能を生成する
- RGB を知らないプログラムは 256 色パレットを前提にするとみなし、
colors#0x100, initc, oc を維持する
1件のコメント
Hacker News のコメント
terminfo を掘り返すのは、深い地下墓地でちらつく松明を使って象形文字を照らしているような感じがする
ターミナルアプリひとつにどれだけ多くの複雑さが詰め込まれているのかを考えると不思議なほどで、iTerm や Kitty のようなターミナルの開発者たちにいっそう感謝したくなる
iTerm+zsh でタブの色を設定する関数をまとめておいた: https://gist.github.com/aclarknexient/84ebe33c1879f921685304...
昔のように telnet で HTTP や SMTP に接続するやり方と比べると、テキスト自体は単純でも、そのテキストを表示する側はさまざまな機能をサポートする複雑なアプリケーションになっている
今はそのターミナルのユーザーが実質的に自分だけなので可能な選択だが、現代のターミナルは共通部分集合をかなりサポートしているので、見慣れない
TERM値をアプリが認識できず混乱するよりは苦痛が少ないもちろん、新しいシステムに頻繁に SSH で入らないなら、それほど重要な問題ではない
最速のターミナルエミュレータではないが、見た目は圧倒的にいちばん良い
さまざまなターミナルアプリごとに 16 色パレットを調整するほうが、アプリケーション側のターミナルコードを正確に合わせるより簡単だった
~/.Xdefaultsで rxvt-unicode の色を設定し、Emacs テーマの.elではグラフィック表示かどうかに応じて色名を変え、それを複数の face に適用しているおまけに、色付きの
ls出力も見やすくなる。デフォルトの 16 色パレットは彩度が高すぎたり、コントラストが悪かったりすることが多い(if (display-graphic-p)が肝だEmacs は実際のグラフィック出力をサポートしており、X11/Cocoa/Win32 では Esc が本当の Esc として動作し、Ctrl+Shift や Cmd のバインディング、複数フォントの同時使用、画像表示、カーソル形状の変更などが可能だ
まるで 20 年後の 1989 年に連れていってくれるタイムマシンのようだ
赤の線は強い赤かオフだけで、intensity 線が加わると 100% の赤になる、という具合だった
DIRCOLORSにシーケンスを入れれば、lsも実際に 24 ビット色をサポートするデフォルトの色は暗い背景に対して暗すぎるので、すべての色の輝度を上げている
著者が
export COLORTERM=truecolorを知っていたら、ずっと苦労は少なかったと思うリンク先の Emacs ドキュメントにある truecolor 検出の脚注にも出てくるし、Konsole のスクリーンショットにも見えているが、ターミナルマルチプレクサを何重にも通るうちに消えてしまった可能性が高い
この値を設定すれば、アプリはトゥルーカラーを求めていることをすぐ理解するので、難しい部分を飛ばせるし、terminfo データベースも不要になる
RGBterminfo 機能のような試行錯誤も入れるべきだったただし、すべてのプログラムが COLORTERM を尊重するわけではないので、
.bashrcでlinux | screen*のときはCOLORTERMを unset し、それ以外ではCOLORTERM=truecolorを export していたことがある最近の Screen は 24 ビット色をサポートしているので、おそらく Ubuntu 18 時代の設定だったのだろう
trampやsshfsと一緒に使えばいい一方では、ncurses のメンテナがかなり保守的で、新しいターミナルの開発者たちは素早く動きたがる、という理由もある
それより古い Emacs では、依然として terminfo データベースが必要だ
たいていは定義する必要がないのに、screen セッションで
COLORTERM=truecolorが設定されていると突然画面が非常におかしくなり、環境変数を消すとまた正常に見えるEmacsとやり取りするときに、必ずしもターミナルを使う必要はない
MacOS、Windows、X.org、Wayland向けのグラフィカルフロントエンドを使える
グラフィカル版Emacsはツールバーをオフにしておくと、ターミナル上で動くEmacsと非常によく似ているため、Emacsを知らない人は同僚のコンピュータに表示されているグラフィカル版Emacsのウィンドウをターミナルウィンドウだと誤解しやすい
Emacsユーザー調査では、80%がグラフィカルフロントエンドを使っていると回答している
xterm-mouse-modeだけでも利点の80%は得られるmouse、xterm-mouse-mode、mouse-wheel-mode、スクロール設定などを入れればかなり使い物になるし、auto-save-defaultは神の御心のままにnilにしておくすべてのターミナルプログラムはグラフィカル版Emacsのshell-modeバッファで実行し、全画面制御が必要なまれなプログラムだけterm-modeを使う
最も多機能でも高速でもないが、ほかのバッファと同じように移動でき、編集中の内容とシームレスに一緒に扱える
リモートマシンでグラフィカル版Emacsを起動できない場合は、trampでリモートファイルを編集するか、ヘッドレスEmacsサーバーを立ち上げてからグラフィカル版emacsclientで接続する
人々が私がEmacsをターミナルで動かしていると思うかもしれない、ということを考えたことがなかった
グラフィカル版Emacsのほうがはるかに良いが、ターミナルでも90%くらいは同じように動作し、同じように見えるのはやはり素晴らしい
体感はターミナル並みに軽く、VNCのような感じではない
フォント、複数フォントの同時利用、メニュー、マウス、色、複数フレーム/ウィンドウといった利点がある
グラフィックはローカルで動かし、trampでリモートシステムにアクセスすれば、実質的にbusyboxしかないリモートLinuxベースのルーターの設定ファイルも、あらゆる便利機能付きで編集できる
90年代からX11を断続的に使ってきたが、フォントはいまだに検索して、たまたま動くコマンドを見つけたらそのまま触らない、という程度だ
企業環境で使うことになるX11設定よりも、ほとんどのターミナルプログラムの表示性能はいまだに目に見えて速い
最近は最新のEmacsをインストールし、24ビットxtermをサポートするターミナルを開いて
emacs -nwを実行すればよいテーマを適用して見た目が問題なければそれで終わりで、そうでなければ
.bashrc、.zshrc、.fishrcなどにexport COLORTERM=truecolorを追加してからシェルを再読み込みするか、その行だけ実行してemacs -nwを再起動すればよいこの記事はterminfoを掘らなければならないかのように見せているが、普通はその必要はない。10年前ならともかく、今は違う
MacOSのiTermとLinuxのAlacritty、Kitty、Gnome Terminalでざっと再確認したが、最近は24ビットxterm対応が一般的だ
今日、Windows 10のコマンドプロンプトでも標準のANSIエスケープシーケンスが動作することを知った
24ビットカラーも問題なく使える
echo ^[[48;2;255;0;0mHello^[[0mを実行すると、Helloが赤いRGB(255,0,0)背景で出力され、^[[0mで色が元に戻る^[はCTRL-[で入力する必要があり、^と[を別々に打ってはいけない\x1bエスケープコードや\eエイリアスを使える例えば
echo -e '\e[48;2;255;0;0mHello\e[0m'のようにできるただしスクリプトでは
echo -eは非標準で、通常のechoも境界ケースで挙動が異なるため、常にprintf '\e[48;2;255;0;0mHello\e[0m\n'を使うほうがよいC:\CONFIG.SYSファイルにDEVICE=C:\DOS\ANSI.SYSを追加するのを忘れてはいけないvim/neovimと比べたときのEmacsの最大の良さは、単なるウィンドウ内のターミナルエミュレーターではなく、第一級のGUIサポートがあることだ
gVimや数多くの中途半端なneovimフロントエンドがあることは知っているが、Emacsのデフォルトの実行形態は本物のGUIであり、そこが良い
ターミナルも悪くないが、歴史的な残滓と制約が多すぎて、今の時代には筋が通らない部分も多い
リモートサーバーでEmacsを実行するのが問題なら、TRAMPとemacsclientがある
GUIアプリが表示中のテキストをコピーできないようにしていて、手で入力し直さなければならないときは苛立つ
https://github.com/neovim/neovim/wiki/Related-projects#gui
モーダル編集とvimは本当に好きだが、ターミナルエディタがもたらす中途半端な解決策には耐えがたかった
豊かなグラフィカルプラットフォームがあるのに、なぜ使わないのか
例えばvimはほぼファイル編集にだけ使い、
git、リンター、フォーマッター、デバッガー、ファイルツリー操作のような外部ツールはシェルに戻って使うそのため自分のメインアプリケーションはすでにターミナルエミュレーターであり、このワークフローでは専用GUIがないことはほとんど重要ではない
私の Emacs はむしろこう見えてほしい: https://imgur.com/a/h0jA1ro
真面目な話ではなく冗談です。Cool Retro Term は発表するときだけ使っています
見たところ、私の Emacs では 24ビット色がデフォルトで動作しています: https://imgur.com/a/BM5OTxp
ただ、シンタックスハイライトは少し気になります
Cool Retro Term は素晴らしく、自分も発表で使ってみたいです
個人的には、ほかのレトロ端末はこれほど見栄えがよくありません
標準をあれほど高額な価格の壁の後ろに閉じ込めているのは、本当に恥ずべきことです
人々が買わずに済ませようとしてここまで迂回しなければならず、こうした結果まで生じ得ます
標準 であるならアクセス可能であるべきです。本としてしか出ていなかった時代には印刷代を取るのも筋が通っていたかもしれませんが、粗末な PDF が 200 ドルだったというのは信じ難いです
xterm の 24ビット色パッチを書きましたが、今日になってようやく
xterm-directで ncurses を動かす方法を知りましたすでに Lisp 製の独自ターミナルライブラリを使っていましたが、ncurses で代替経路を用意できるのは今でも有用です
Stack Overflow の質問に答えてくれた Mr. Dickey と、この話題を持ち出してくれた Chad に感謝します