wasix/tui: WASIX 端末制御と TUI 描画補助ライブラリ
- WASIX の
tty_get/tty_setを使って端末状態を取得・更新します。 - ANSI エスケープシーケンスを用いたカーソル移動・色制御を提供します。
- 行バッファを使った差分レンダリング機能を提供し、再描画コストを削減します。
- 端末状態は WASIX ABI の
Tty構造体レイアウトに合わせてメモリ上で操作します。 - 行バッファは
currとprevの2面を比較し、差分行のみprintします。 - 文字列の表示幅は UTF-8 先頭バイトを用いて概算し、ANSI/OSC8 制御列は幅0として扱います。
- 表示幅計算は厳密な East Asian Width 判定ではなく、実用上の近似です。
- 端末依存の表示差(フォント、絵文字幅、結合文字など)は吸収しきれません。
- 差分判定は行単位です。1文字だけの更新でも、その行全体を再出力します。
TtyState: WASIX TTY 構造体のメモリレイアウト
目的:
- WASIX ABI の
Tty構造体を扱います。
レイアウト:
- offset 0: cols (u32)
- offset 4: rows (u32)
- offset 8: width (u32)
- offset 12: height (u32)
- offset 16: stdin_tty (u8/bool)
- offset 17: stdout_tty (u8/bool)
- offset 18: stderr_tty (u8/bool)
- offset 19: echo (u8/bool)
- offset 20: line_buffered (u8/bool)
- (24 bytes allocated for safety)
TerminalSize: 端末の列数と行数
get_terminal_sizeの戻り値を名前付き field で扱い、呼び出し側の意図を明確にします。
colsとrowsは 0 のことがあります。TTY 取得に失敗した場合も 0,0 を返します。
enter_raw_mode: ターミナルを RAW モードにする
目的:
- echo と line_buffered を無効化します。
- 成功時は以前の TtyState ポインタを返します。
注意(重要):
- 返されたポインタは restore_mode に渡して解放する必要があります。
restore_mode: ターミナルの状態を元に戻す
目的:
- enter_raw_mode で保存した状態を復元します。
get_terminal_size: ターミナルのサイズを取得する
- 現在のターミナルの列数と行数を
TerminalSizeとして取得します。
- WASIX の TTY 状態を読み、
cols/rowsを取り出して構造化して返します。
- 取得に失敗した場合は
TerminalSize 0 0を返します。
--- ANSI 画面制御ヘルパー ---
clear_screen: 画面全体をクリアしてカーソルを左上に移動する
move_cursor: カーソルを (col, row) に移動する (1-indexed)
cursor_home: カーソルを左上 (1,1) に移動する
hide_cursor: カーソルを非表示にする
show_cursor: カーソルを再表示する
clear_line: カーソル行を消去する
set_bg_color: 背景色を設定する (0-7: 標準色)
0=黒 1=赤 2=緑 3=黄 4=青 5=マゼンタ 6=シアン 7=白
set_fg_color: 前景色を設定する (0-7: 標準色)
reset_color: 前景色と背景色をリセットする
repeat_text: 文字列を指定回数だけ連結して返す
- 罫線や空白など、同じ文字列を繰り返す行生成を簡潔に記述します。
outに対してconcat out sを n 回繰り返します。
concatの繰り返しなので、長大文字列ではコストが大きくなります。
- O(n^2)(文字列再確保の影響を含む)
str_display_width: 文字列の表示幅を概算する
- 行の右パディングを行う際に、日本語を含む表示の崩れを減らします。
- UTF-8 の先頭バイトから文字境界を推定し、ASCII は幅1、非ASCIIは幅2で加算します。
- ANSI CSI(
ESC [)と OSC8(ESC ] ... ESC \\)は幅0としてスキップします。
- 厳密な文字幅計算ではなく、端末依存差を含む近似です。
- ゼロ幅結合や一部絵文字の幅は環境ごとに差が出ます。
- O(n)
line_pad_to_cols: 行文字列を指定表示幅まで右側空白で埋める
- 以前の長い行が残る問題を防ぎ、行更新を安定させます。
str_display_widthで現在幅を求め、足りない分だけ空白を連結します。
- 入力が指定幅を超える場合は切り詰めず、そのまま返します。
- O(n)
line_clip_to_cols: 行文字列を指定表示幅以内に切り詰める
- 横幅制限がある領域へ安全に描画するため、表示幅ベースで文字列を切り詰めます。
- UTF-8 先頭バイトから 1文字ごとのバイト長と概算表示幅を求め、
次の文字を追加すると cols を超える時点で切り出しを終了します。
- ANSI/OSC8 を含む文字列の厳密処理は対象外です(通常テキスト向け)。
- 幅計算は厳密な East Asian Width 判定ではなく近似です。
- O(n)
text_wrap_lines: 表示幅で折り返した行配列を作る
- 長文を端末上で読みやすく表示するため、表示幅単位で複数行へ折り返します。
- 改行文字
を明示的な行区切りとして扱います。 - それ以外は UTF-8 先頭バイトから文字幅を推定し、
colsを超える前で折り返します。
- 幅計算は近似です。結合文字や一部絵文字は端末依存差が出ます。
cols <= 0の場合は空配列を返します。
- O(n)
buffer_set_wrapped_text: 折り返し文字列を行バッファへ描画する
- 指定矩形(開始行と高さ)に、表示幅ベースで折り返したテキストを描画します。
text_wrap_linesで行配列を作成し、上から順にbuffer_set_lineで反映します。- 各行は
line_clip_to_colsとline_pad_to_colsを通し、幅を安定化します。 - 高さを超える分は切り捨て、不足行は空白行を入れて残像を防ぎます。
- 行番号は 1-origin です。
- 文字色/背景色は、この関数に渡す前に
style_textで付与してください。
- O(n + h) (n: テキスト長, h: 描画高さ)
line_box: 罫線付きの1行(│ ... │)を作る
- 枠線付きUIの行構築を簡潔に行います。
- 内側幅
cols-2に合わせてcontentを右パディングし、両端に縦罫線を付けます。
cols < 2のケースは呼び出し側で回避してください。
line_box_styled: 罫線付きの1行を色付き背景で作る
- 行全体の背景色を確実に塗りたい場合に使います。
- 先に
contentを内側幅までパディングし、その結果全体にstyle_textを適用します。
- 罫線(
│)自体は無色です。内側領域のみ色がつきます。
line_top: 上枠(┌──┐)を作る
- ボックス描画の先頭行を生成します。
line_bottom: 下枠(└──┘)を作る
- ボックス描画の最終行を生成します。
style_text: 前景色/背景色を付与した文字列を作る
- 文字列単位で色付けし、差分レンダリング行に埋め込めるようにします。
ESC[<fg>;<bg>mを前置し、末尾にESC[0mを付加します。
fg,bgは 0..7(ANSI標準色)を前提とします。- 色シーケンス自体は
str_display_widthで幅0として扱われます。
hyperlink_text: OSC8 でハイパーリンク付き文字列を作る
- 端末が対応している場合にクリック可能なリンクを表示します。
ESC]8;;urlESC\\+text+ESC]8;;ESC\\を連結します。
- OSC8 非対応端末ではリンクとして機能しません。
---- 行バッファ差分レンダリング ----
Buffer レイアウト:
- +0 : cols (i32)
- +4 : rows (i32)
- +8 : curr_lines_ptr (i32) // i32[row] (str ポインタ)
- +12 : prev_lines_ptr (i32) // i32[row] (str ポインタ)
buffer_new: 行バッファを作成する
- 差分レンダリングのための
curr/prev行配列を初期化します。
- 行数ぶんの
strポインタ配列を2面確保し、空文字列で初期化します。
- 返り値は
buffer_freeで解放してください。
buffer_free: 行バッファを解放する
buffer_newで確保したメモリを解放します。
buffer_set_line: row(1-origin) の行文字列を設定する
- 次フレームの行内容を
curr側へ登録します。
- 行番号が範囲外の場合は何もしません。
line_has_escape: 行に ESC 制御文字が含まれるか判定する
- 文字単位差分時に、ANSI/OSC8 を含む行を安全に全行更新へフォールバックするために使います。
buffer_present_diff: 差分のある行だけ再描画する
currとprevを比較し、変更行のみ端末へ出力します。- 同一行内では先頭差分位置から末尾のみを書き換え、更新量を削減します。
- まず
str_eqで同一行を判定します。 - 差分行では「最初に異なるバイト位置」を求め、そこから行末までを再描画します。
- ANSI/OSC8 を含む行は安全性のため行全体更新にフォールバックします。
- 描画後は該当行の
prevを更新します。
- UTF-8 文字幅は
str_display_widthによる近似です。 - 差分開始位置はバイト基準で探索し、カーソル列は表示幅換算で計算します。