ncurses への誘い

この記事は C / C++ プログラマーを対象として書かれています.ncurses というライブラリの紹介なのですが,調べてみると pythonRuby でも使えるようです(ここら辺の言語, C 用のライブラリなら何でも持ってますね……).

CUI vs GUI

C プログラムには, CUI (キャラクタユーザインターフェース) のものと,GUI (グラフィカルユーザインターフェース) のものがあります.ユーザにとっては GUI の方が圧倒的に直感的で分かりやすいため,何か開発する際に GUI の方が良いなぁと思うこともあるでしょう.しかし,プログラミング学習において GUI より先にまず CUI を学ぶことが多いことからも分かる通り,プログラマの目線から見れば CUI の方が簡単です.とくに入出力に関しては, CUI なら printf,scanf などの関数だけで事が大体済みます.一方 GUI だと,何かを表示したり,キーボード操作を受け付けたりするためには,少々長いコードが必要となってしまいます(どれくらい長くなるかはライブラリにもよりますが……).そのため,GUI であることが望ましいようなプログラムを作る際にも,面倒だからという理由で CUI を選択してしまう人がいることでしょう.

……ちょっと待って下さい.もう 1 つの選択肢を忘れていませんか.
CUIGUI と来たら,そうです, TUI (テキストユーザインターフェース) が残っています.

第3の選択肢,TUI

TUI は,CUI と同じくコンソール上で動きます.CUI では基本的に左上から順番に文字を出力することしかできませんが,TUI ではターミナル画面の好きな位置に文字を表示したり,ユーザの入力をリアルタイムで受け取ったりすることができます.コンソールを使って実質 GUI と同じことができるのです.しかも,ncurses というライブラリを使えば, CUI と同じくらい簡潔なコードで済むのです.

ざっと使い方の説明

とりあえず ncurses で画面の左上に Hello, world! と表示してみましょう. ncurses を使うには,ncurses.h ヘッダをインクルードし,コンパイル後に ncurses ライブラリとリンクします.

/* main.c */
#include <ncurses.h>
int main(){
    initscr();                  // 最初に書く
    addstr("Hello, world!");    // カーソルの位置に"Hello, world!"と表示する
    getch();                    // 何かのキーが押されるまで待つ
    endwin();                   // 最後に書く
    return 0;
}
$ gcc main.c -lncurses

実行すると,"Hello, world!"と表示されるはずです.(環境によっては,押されたキーの文字がプログラム終了後に残るかもしれません.)
カーソルは最初,画面左上(座標 (0, 0))の位置にあります.addstr 関数によって,最初の'H'が(0, 0)の位置に来てその右に残りの"ello, world!"が続くように,文字列"Hello, world!"が表示されます.このときカーソルは文字数分だけ右に動いています.
カーソルを特定の位置に動かすには,move 関数を使います.

move(5, 10);
addstr("Hello, world");

今度は'H'が上から5行目,左から10文字目(ただし上端を0行目,左端を0文字目とする)の位置に表示されます.
printf のようなフォーマット出力は printw 関数でできます.

printw("%d is prime.", 57);

getch 関数は,キーボード入力を1文字ずつ受けとります.

while(getch() != 'a');

'a' が押されるとループを抜けます.その間押されたキーの文字が画面に表示され続けます.これは noecho 関数を使うと表示しないようになります.

noecho();
while(getch() != 'a');

押されたキーは表示されず,カーソルも動きません.echo 関数でもとに戻ります.
また上下左右キーや特殊キーを受けとるためには keypad 関数を事前に呼び出しておきます.

keypad(stdscr, TRUE);
while(getch() != KEY_DOWN);

KEY_DOWN などの定数一覧は man getch で見られます.また今使った stdscr の意味について知りたい人は man ncurses から読んで下さい.
curs_set 関数を用いるとカーソルの表示非表示を切り替えられます.

curs_set(0); // invisible(表示されない)
curs_set(1); // normal(表示される)
curs_set(2); // very visible(環境によってはカーソルの形状が変わる)

getch(); はそのままだとキー入力を待っている間プログラム全体が止まってしまいます.nodelay 関数を使うと待たないようになります.

nodelay(stdscr, TRUE);
for(int i = 0;; ++i){
    move(0, 0);
    printw("%d", i);
    move(1, 0);
    if(getch() == 10) break;
}

また CUI のような入力受付けも可能です.

char buf[256];
getstr(buf);

実質 GUI

上で紹介したことを組み合わせれば,GUI っぽいことがかなりできるようになります.ゲームも作れます.特に学業・仕事の合間にプログラミングをする趣味グラマーは,労力をかけずにすむ TUI を第3の選択肢として頭に入れておくのも悪くないと思います.

この記事があなたの役に立てば幸いです.