curses gem

ターミナル上で動作するインタラクティブなアプリケーションを開発するための古典的なライブラリに cursesがあります。
サーバーはLinuxの普及度が高いこともあり、cursesベースのアプリを作るとssh越しに操作できてポータブルです。

Rubyのcursesバインディングはかつて標準ライブラリでしたが、いまはgem配布になっています。
公式docに情報が集約されています。

Window

基礎的な使い方は、 Windowにサンプルがあります。

  • Curses::Window.new()でWindowオブジェクトを作成し、インスタンスに文字列を書き込む
    • 引数はheight, width, top, leftの順。すべて0を指定するとフルスクリーンになる
    • addstr()で書き込むほか、配列のように<<でも書ける
    • refresh()で描画する
  • subwin()でモーダル描画できる
    • close()で閉じると親ウインドウの処理に戻る
  • getch()で入力を受け付ける
    • 入力キーの定数は Keyにリストされている

下のレイヤの描画を隠す

サブウインドウはオーバーレイ表示ではありますが、何も文字列を書き込んでいない部分はバックグラウンドではなく下のウインドウの文字列が表示されます。
たとえば、10文字分の幅のサブウインドウに4文字入力して描画すると6文字は下の内容がそのまま見えています。また、setpos(0, 3)の後に書き込むと、先頭2文字分は下の内容がそのまま見えます。

下の描画を隠すための基本形は単にスペースを書き込むことです。また、行末で\nを書き込んだ場合や、clrtoeol()を呼ぶと行末までクリアします。

下部余白のクリア

コンテンツが縦幅を使い切らない場合、素朴に出力すると、下の余白部分に古い描画が残ります。
余白部分に\nを書き込むとクリアできます。

ウインドウ枠の考慮

box()を使うとウインドウの枠を描画できます。
この場合、上下左右の1文字は枠にとられるため、描画範囲は外側1文字を避けて書き込む必要があります。 妥当な左上始点はsetpos(1, 1)になります。下1行は書けない点にも注意が必要です。

また、clrtoeol()は枠部分も上書きするため素朴に使うと右枠が消えます。
以下のようにrefresh直前に枠を描画すると消えません。

window << "Text Message."
window.clrtoeol
window.box("|", "-")
window.refresh

文字列が描画可能な範囲より長いと折り返す

ターミナルの幅は実行環境により様々です。
高さについてはmaxy()でおおむね制御できますが、横幅が実際のターミナル幅を超えると行が折り返し表示となり、行数もmaxy()の範囲を突き抜けていくため全体的に描画が破綻します。

日本語を使う場合、横幅の制御が複雑であるため、適切に詰めるのは至難です。
各行を書き始める前に\nではなくsetpos(i, 0)を使うと次の行の描画が折り返した部分を上書きするため、結果的に一行に収まらなかった部分を切ったような表示になり、右端を犠牲にしてある程度成立します。

⁋ 2022/03/16↻ 2024/12/18
中馬崇尋
Chuma Takahiro