空いていたので穴埋め
libvterm とは
libvterm は libtermkey の作者でおなじみの Paul Evans 氏によって作られた、VT220/xterm/ECMA-48 といった端末エミュレータの仕様を抽象化し疑似する為のライブラリです。このライブラリの仕様に従ってプログラミングするとプラットフォームや GUI/CUI に依存せず端末エミュレータが扱えるという物です。neovim や vim の :terminal
機能として使われています。
libvterm はシェルではありません。またユーザインタフェースでもありません。純粋な仮想端末エミュレータの実装です。ですのでキーも読み取りませんし、何も描画しません。出来るのは端末のセルに対するエスケープシーケンス等の書き込みと、それによって制御された端末の出力結果を取得出来るのみです。
ですので例えばシェルを起動しユーザと対話できる端末エミュレータを作る場合、プログラムは子プロセス(シェル)を起動し、その pty 出力を読み取って libvterm に流し込みます。また libvterm からのコールバックイベントに伴い端末のセル情報を読み取って独自 UI にレンダリングする事になります。
libvterm をGo言語から使いたい
以前 libvterm をGo言語から扱う為のパッケージを書きました。
このパッケージを使って、80x25 の端末に赤字で「Hello」緑色で「World」と表示するプログラムを書いてみます。
vt := vterm.New(25, 80)
defer vt.Close()
vt.SetUTF8(true)
screen := vt.ObtainScreen()
screen.Reset(true)
_, err := vt.Write([]byte("\033[31mHello \033[32mGolang\033[0m"))
if err != nil {
log.Fatal(err)
}
screen.Flush()
このコードを実行しても実際には何も表示されません。「これのどこが良いか分からない」そう思われるかもしれません。では続けて以下のコードを書いてみます。
rows, cols := vt.Size()
img := image.NewRGBA(image.Rect(0, 0, cols*7, rows*13))
draw.Draw(img, img.Bounds(), &image.Uniform{color.Black}, image.ZP, draw.Src)
for row := 0; row < rows; row++ {
for col := 0; col < cols; col++ {
cell, err := screen.GetCellAt(row, col)
if err != nil {
log.Fatal(err)
}
chars := cell.Chars()
if len(chars) > 0 && chars[0] != 0 {
drawChar(img, (col+1)*7, (row+1)*13, cell.Fg(), string(chars))
}
}
}
f, err := os.Create("output.png")
if err != nil {
log.Fatal(err)
}
defer f.Close()
err = png.Encode(f, img)
if err != nil {
log.Fatal(err)
}
こうする事で、80x25 の端末のスクリーンショットが撮れる訳です。
プログラム全体のコードを Gist に貼り付けておきます。
go-libvterm で出来ること
アプリケーションがデバイスに依存しない端末用キャンバスを内蔵できる事になるので、例えば Windows の仮想端末実装である winpty と合わせて使うと、コマンドプロンプトに tmux の様な枠を作りその中でさらにコマンドプロンプトを住まわせる事も出来るのです。
自分で作ったウィンドウアプリケーションの一部分に、Visual Studio Code の端末機能や Vim/neovim の :terminal
機能を付けたい場合には libvterm が便利です。そしてそのアプリケーションが Go で実装されているならば go-libvterm を使う事が出来ます。
宜しければご利用下さい。