[LinuxFocus-icon]
ホーム  |  マップ  |  一覧  |  検索

ニュース | アーカイブ | リンク | LFについて
[an error occurred while processing this directive]
convert to palmConvert to GutenPalm
or to PalmDoc

[RehaGerceker]
by Reha K. Gerçeker
<gerceker/at/itu.edu.tr>

著者紹介:

Reha はトルコのイスタンブールでコンピューターエンジニアリングを勉強している学生です。 ソフトウェア開発のプラットフォームとして Linux が与えてくれる自由を愛しています。 長いことコンピューターの前で時間を過ごし、プログラムを書いています。 いつか洗練されたプログラマーになるのが目標です。



日本語訳:
須藤 賢一 <deep_blue/at/users.sourceforge.jp>

目次:

 

Ncurses 入門

[ncurses]

要約:

Ncurses は、ファンクションキーの割り当てやスクリーン表示、オーバーラップしない複数のウィンドウの作成などを、テキストベースのターミナル上で行うためのライブラリです。

_________________ _________________ _________________

 

Ncurses とは

自分のプログラムにターミナルベースのカラフルなインターフェースを持たせたい? ncurses というライブラリを使えば、テキストベースのターミナル上でウィンドウ機能を持たせることができます。 ncurses を使ってできることは次の通りです。



ncurses は、ANSI/POSIX に準拠した UNIX システムであれば、どれでも動作します。 加えて、システムのデータベースからターミナルの属性を読み込んで適切に動作しますので、ターミナルに依存しないインターフェースを提供してくれます。 ですから、いろいろなプラットフォームやターミナル上で動作するシステムを設計する際にも利用することができます。

Midnight Commander も ncurses を使って書かれています。 カーネルのコンフィギュレーションをコンソール上で行うためのインターフェースもそうです。 以下にスナップショットを載せておきます。


[Midnight Commander]

[kernel config]  

ダウンロード方法

ncurses は GNU/Linux 上で開発されています。 最新版のダウンロード、詳細情報、関連するリンクは www.gnu.org/software/ncurses/ にあります。

 

基本事項

ncurses ライブラリを使うには、ソースコード中で curses.h をインクルードし、curses ライブラリを忘れずに自分のコードにリンクする必要があります。 gcc に -lcurses パラメータを渡すことでライブラリがリンクされます。

ncurses を使うには、基本的なデータ構造体を知っておく必要があります。 WINDOW 構造体は、名前からも分かるとおり、作成するウィンドウをあらわします。 ライブラリの関数は、WINDOW 構造体へのポインタが引数になっているものがほとんどです。

ncurses で最も良く使われる要素はウィンドウです。 自分ではウィンドウを作らない場合でも、スクリーンはそれ自体がウインドウになっています。 標準入出力ライブラリで stdout というファイルディスクリプタがスクリーンをあらわすのと同様に (ただしリダイレクトが行われていない場合)、ncurses にも 同様の働きを持つ stdscr という WINDOW 構造体へのポインタがあります。 それ以外にも curscr という WINDOW 構造体へのポインタがライブラリ中に定義されています。 stdscr がスクリーンをあらわしていたのと同様、curscr はライブラリ内での現在のスクリーンをあらわします。 何が違うのか疑問に思うでしょうが、とりあえず先に進んでください。

ncurses の関数や変数をプログラム中で使えるようにするには、initscr 関数を呼ぶ必要があります。 この関数は、stdscr や curscr などの変数に領域を割り当て、ライブラリを利用可能な状態にします。 すなわち、全ての ncurses の関数は、initscr の後で呼ぶ必要があるということです。 同様に、ncurses を使い終えたら endwin を呼ぶ必要があります。 この関数は、ncurses が使っていたメモリを開放します。 endwin を呼んだ後は、もう一度 initscr を呼ばない限り、ncurses の関数を呼んではいけません。

initscr から endwin までの間は、標準入出力ライブラリを使ったスクリーンへの出力は行わないようにしてください。 でないと、スクリーン表示がぐちゃぐちゃになってあわてることになります。 ncurses が有効な間は、スクリーンへの出力は ncurses の関数を使うようにしてください。 initscr を呼ぶ前か、endwin を呼んだ後でなら好きにして構いません。

 

スクリーンの更新 : refresh

WINDOW 構造体は、ウィンドウの高さ、幅、位置といった情報を保持していますが、 加えてウィンドウの内容も保持しています。 ウィンドウに対して書き込みを行うとウィンドウの内容は更新されますが、スクリーンもすぐに更新されるという訳ではありません。 スクリーンを更新するには、refresh か wrefresh を呼ぶ必要があります。

ここで stdscr と curscr の違いが出てきます。 curscr はスクリーンの現在の内容を保持していますが、書き込みを行う ncurses 関数を呼んだ後の stdscr は、それとは違う内容になっている可能性があります。 stdscr に対して行った変更内容を curscr に反映するには refresh を呼ぶ必要があります。 すなわち、curscr に対して操作するには refresh を呼ぶしかないということです。 curscr を更新するのは refresh に任せて、自分で何かしようとはしないことです。

refresh 関数は、できるだけ高速な方法でスクリーンを更新するための機構を持っています。 関数が呼ばれると、ウィンドウ中で変更のあった行だけが更新されます。 これにより、同一の情報をスクリーンに再度表示することがなくなり、CPU 時間の節約になります。 ncurses の関数と標準入出力関数を一緒に使うとおかしな結果になるのは、これが原因です。 ncurses 関数が呼ばれるとフラグがセットされます。 refresh 関数はそのフラグを見ることでその行が変更されたことが分かるようになっていますが、標準入出力関数を使ってしまうとフラグのセットが行われず、どの行が更新されたかが分からないためです。

refresh と wrefresh は基本的に同じことをします。 wrefresh は WINDOW 型のポインタを引数として受け取り、そのウィンドウの中だけを更新します。 refresh() は wrefresh(stdscr) と同じ意味です。 後で説明しますが、wrefresh の例のように、ほとんどの ncurses 関数には、stdscr に対してその操作を行うマクロが用意されています。

 

新しいウィンドウの作成

では次に subwin 関数と newwin 関数を説明しましょう。 これらは新しいウィンドウを作成します。 どちらも新しいウィンドウの高さ、幅、左隅の座標を引数として取り、作成したウィンドウを指す WINDOW 型のポインタを返します。 このポインタを、後で説明するように wrefresh などの関数に渡します。

どうして同じ機能を持った関数が2つもあるのかと思うことでしょう。 そう、実はちょっと違うのです。 subwin は新しいウィンドウを別のウィンドウの子ウィンドウとして作成します。 このようにして作ったウィンドウは親ウィンドウから属性を引き継ぎます。 この属性はあとで変更することもでき、親ウィンドウの属性には影響を与えません。

親ウィンドウと子ウィンドウは他にも関係があります。 実は、ウィンドウの内容を保持している文字配列が、親と子で共有されるのです。 すなわち、2つのウィンドウが重なっている部分については、どちらのウィンドウも内容を変更することができます。 もし親ウィンドウがそのマスに書き込みを行うと、子ウィンドウの中も変更されます。 逆も同様です。

subwin とは違って、newwin は全く新しいウィンドウを作成します。 そうやって作られたウィンドウは、自身の子ウィンドウを持っていない限り他のウィンドウと文字配列を共有することはありません。 文字配列が共有されるためにメモリ消費が少なくて済むというのが、subwin を使うことのメリットでした。 しかし、複数のウィンドウが互いに書き込みを行うような状況では、newwin を使う方が良いでしょう。

作成する子ウィンドウはいくらでも深くすることができます。 子ウィンドウはさらに子ウィンドウを持つことも可能です。 ただし、3つ以上のウィンドウで文字配列が共有されることになる点に注意してください。

作ったウィンドウが不要になったら、delwin 関数を使って破棄することができます。 ここで説明した関数の引数一覧については man ページを見てください。

 

ウィンドウへの書き込みと読み出し

ここまでで stdscr と curscr、スクリーンの更新と新規ウィンドウの作成について説明してきました。 では、ウィンドウに書き込みを行うにはどうしたらいいのでしょうか。 読み込みはどうやるのでしょう。

そのための関数は、標準入出力ライブラリで同じことをする関数に似ています。 printf の代わりに printw が、scanf の代わりに scanw が、putc や putchar の代わりに addch が、getc や getchar の代わりに getch があります。 使い方はいたってわかりやすく、名前が違っているだけです。 また addstr はウィンドウに文字列を書き込むのに、getstr はウィンドウから文字列を読み込むのに使います。 これら関数の先頭に 'w' をつけたものは第1引数が WINDOW 型のポインタで、stdscr 以外のウィンドウに対して操作を行います。 たとえば、refresh() は wrefresh(stdscr) と同じことですし、printw(...) は wprintw(stdscr, ...) と同じことです。

これら関数について詳しく説明すると長くなってしまうので割愛することにします。 説明やプロトタイプ宣言、返却値などについては、man ページを調べるのがもっとも良いでしょう。 関数を使う際には必ずその関数の man ページを読むことをお勧めします。 man ページには、役に立つ情報が詳しく載っています。 この記事の最後の章にあるサンプルプログラムも、上記関数の使い方の手引きとなるでしょう。

 

物理カーソルと論理カーソル

ウィンドウへの読み書きを説明したら、次は物理カーソルと論理カーソルの話をしないといけません。 物理カーソルとはスクリーン上で点滅しているカーソルで、たった 1 つしか存在しません。 対して論理カーソルは ncurses のウィンドウに含まれるもので、各ウィンドウがそれぞれ論理カーソルを持っています。 よって、論理カーソルは複数存在することになります。

ウィンドウの中で、次に書き込みや読み込みが始まるマスの位置が論理カーソルです。 論理カーソルは任意の位置に動かすことができますから、いつでもスクリーンやウィンドウの好きな位置に書き込めるということになります。 これは ncurses が標準入出力ライブラリより優れている点です。

論理カーソルを移動させるための関数は move と、ご想像の通り wmove です。 move は wmove へのマクロで、stdscr 用のものです。

物理カーソルと論理カーソルは座標という点でも違っています。 書き込み操作を行った後に物理カーソルがどの位置にくるかは、WINDOW 構造体の中にある _leave フラグによって決まります。 もしも _leave フラグが立っていると、論理カーソルは物理カーソルの位置 (最後の文字が書き込まれた位置) になります。 _leave フラグが立っていない状態で書き込みを終えると、物理カーソルは論理カーソルの位置 (最初の文字を書き始めた位置) に戻ります。 _leave フラグは leaveok 関数で変更します。

物理カーソルを移動させる関数は mvcur です。 他の関数は次の refresh 呼び出しを行って初めて結果が反映されますが、mvcur はそれとは違い、カーソルはすぐに移動します。 物理カーソルを目に見えなくするには curs_set 関数を使います。 この関数の詳細は man ページを参照してください。

書き込みと移動を 1 回の呼び出しで行うようなマクロもあります。 その手の関数については、addch、addstr、printw、getch、getstr、scanw 等と同じ man ページに載っています。

 

ウィンドウのクリア

ウィンドウへの書き込みは分かりました。 では、ウィンドウや行、文字をクリアするにはどうしたら良いのでしょうか。

ncurses では、クリアするということはマスや行、ウィンドウの内容などを空白で塗りつぶすことに相当します。 以下で説明する関数は、必要な領域を空白で埋め、結果的にスクリーンをクリアすることになります。

まずは 1 文字もしくは 1 行をクリアする関数を説明します。 関数 delch と wdelch はウィンドウ内の論理カーソルの位置にある文字を削除し、その行の後続の文字を左にシフトします。 deleteln と wdeleteln は論理カーソルのある行を削除し、後続する行を上方向にシフトします。

wclrtoeol 関数は論理カーソルのある行の、カーソルより右の部分の文字をすべて削除します。 clrtobot と wclrtobot は、まず wclrtoeol を呼んで論理カーソルより右の部分を消し、 それ以降の行を全て消していきます。

これ以外にも、スクリーン全体やウィンドウ全体をクリアする関数もあります。 スクリーン全体をクリアするには 2 つの方法があります。 1 つ目は全てのマスを空白スペースで埋めて refresh を呼ぶ方法で、2 つ目は組み込みのターミナル制御コードを使う方法です。 前者はスクリーンの全ての部分を再書き込みするのに対し、後者はスクリーン全体を一気にクリアすることから、前者の方が後者よりも遅くなります。

erase 関数と werase 関数は、ウィンドウの文字配列を空白スペースで埋めます。 次回リフレッシュしたときに、ウィンドウがクリアされることになります。 しかし、クリアするウィンドウがスクリーン全体を覆っているなら、これらの関数を使うのは利口ではありません。 これらの関数は上で説明した 1 つ目の方法を使っているからです。 クリアしようとするウィンドウがスクリーンいっぱいを覆っているのであれば、次に説明する関数を使う方が有利です。

それを説明する前に、_clear フラグについて話さないといけません。 このフラグは WINDOW 構造体の中にあり、これがセットされていると、次回 refresh が呼ばれた時にターミナルに対して制御コードが送られます。 refresh は呼ばれるとまずウィンドウがスクリーン全体を覆っているかを判断し (_FULLWIN フラグを使用)、 もしそうならターミナル固有の手段を使ってスクリーンをクリアします。 その後、空白文字でない文字だけをスクリーンに書き込んでいきます。 これによりスクリーン全体を高速にクリアすることができます。 ウィンドウがスクリーン全体を覆っている場合にだけターミナル固有の手段を使うのは、ターミナル制御コードを使うとそのウィンドウだけでなくスクリーン全体がクリアされてしまうからです。 _clear フラグは clearok 関数で変更します。

clear 関数と wclear 関数は、スクリーン一杯に広がったウィンドウをクリアするのに使います。 実際にはこれらの関数は werase と clearok を呼ぶのと同じです。 まず、ウィンドウの文字配列を空白で埋めます。 次に、_clear フラグを立てることで、ウィンドウがスクリーン全体を覆っているならターミナル固有の手段で、そうでないならウィンドウ全体を空白で埋めることでスクリーンをクリアします。

ですから、もしクリアするウィンドウがスクリーン全体を覆っていることが分かっているなら、clear か wclear を使ってください。 その方が高速にクリアすることができます。 しかし、ウィンドウがスクリーン全体を覆っていない場合には、wclear も werase も同じになります。

 

色の使用

スクリーン上で見える色は実は色のペアです。 どのマス目も背景色と前景色を持っているからです。 ncurses においてカラーで書き込みを行うということは、 自分で色を作って、 ウィンドウに書き込みを行う際にそれを使うということになります。

ncurses を使い始めるにあたって initscr を呼ぶ必要がありましたが、 色を使いはじめるには、同じように start_color を呼ぶ必要があります。 自分用の色を作るための関数は init_pair です。 init_pair を使って色を作ると、その色は関数の第 1 引数で渡した番号に関連付けられます。 それ以降、この色を使いたいときには、その番号に対して COLOR_PAIR を呼ぶことで参照することができます。

色を作る以外にも、色つきで書き込みを行うのに必要な関数があります。 それが attron と wattron です。 これらの関数は、attroff と wattroff が呼ばれるまでは、そのウィンドウに対する書き込みは選択された色を使って行われます。

また、bkgd と wbkgd という関数があり、これらはウィンドウ全体に関連づけられた色を変更します。 これらの関数を呼ぶと、ウィンドウのすべてのマスの背景色と前景色が変更されます。 その結果、次にリフレッシュするときには、ウィンドウのすべてのマスが新しい色で再表示されることになります。

使うことができる色や、ここで説明した関数の詳細については、man ページを参照してください。

 

ウィンドウのまわりの枠

ウィンドウのまわりに枠をつけて、 プログラムの見栄えを良くすることができます。 ライブラリの中にある box というマクロを使うことで枠を付けることができます。 他の関数と違って、wbox はありません。 box 関数は WINDOW 型のポインタを引数として取ります。

man ページを見れば box 関数の詳細がわかりやすく説明されています。 ただし、補足説明しておくことがあります。 ウィンドウに枠をつけるということは、ウィンドウの文字配列の枠の境界に当たる部分に対して、必要な文字を書き込むということです。 もし後でその枠の部分に書き込みを行うと、枠は壊れてしまいます。 そうならないようにするには、元のウィンドウの中に subwin 関数を使って子ウィンドウを作ります。 そして、元のウィンドウのほうに枠をつけて、書き込みは子ウィンドウのほうにするのが良いでしょう。

 

ファンクションキー

ファンクションキーを使えるようにするには、入力させたいウィンドウの _use_keypad フラグを立てる必要があります。 keypad 関数を使って、_use_keypad フラグを操作できます。 _use_keypad を立てると、入力関数を使って入力を得ることができます。

ただし、たとえば getch を使って入力データを得るのであれば、char 型の変数でなく int 型の変数に値を保持するようにしてください。 これは、ファンクションキーの数値が char 型の変数で保持できる値よりも大きいためです。 ファンクションキーの値自体は知らなくても良く、 代わりにライブラリ内で定義されているデファイン名を使います。 デファイン名は getch の man ページに載っています。

 

サンプル

では、簡単でわかりやすいサンプルを見てみることにしましょう。 このプログラムでは、ncurses を使ってメニューを作り、一覧から候補を選択するようになっています。 このプログラムで面白いのは、ncurses のウィンドウを使ってメニューを実現している点です。 以下に画面例を示します。

[example program]

プログラムはヘッダーファイルのインクルードから始まります。 次に、エンターキーとエスケープキーに相当する ASCII コードの値を定義します。

#include <curses.h>
#include <stdlib.h>

#define ENTER 10
#define ESCAPE 27

プログラムが動き出すとすぐに以下の関数が呼ばれます。 その中ではまず initscr を呼んで curses の初期化を行い、次に start_color を呼んで色を使えるようにします。 後で色を定義して、それを以降のプログラム中で使います。 curs_set(0) を呼び出していますが、これは物理カーソルを見えなくするためのものです。 noecho はキーボードから入力された内容をスクリーンに表示しないようにするためのものです。 noecho 関数を使えば、 キーボードからの入力の一部だけを表示するといったこともできます。 echo 関数を呼べば再度入力が表示されるようになります。 最後に keypad 関数を呼んで、stdscr でファンクションキーが使えるようにしています。 後で F1 キーと F2 キーとカーソルキーを使う予定なので、これが必要です。

void init_curses()
{
    initscr();
    start_color();
    init_pair(1,COLOR_WHITE,COLOR_BLUE);
    init_pair(2,COLOR_BLUE,COLOR_WHITE);
    init_pair(3,COLOR_RED,COLOR_WHITE);
    curs_set(0);
    noecho();
    keypad(stdscr,TRUE);
}

次の関数はスクリーン最上部にメニューバーを作成します。 後で説明する main 関数を見ると、スクリーン最上部の  1 行からなるメニューバーは、実は 1 行だけの、stdscr の子ウィンドウになっていることが分かります。 下の関数はそのウィンドウへのポインタを引数としてとり、まず背景色を変えて、メニュー名を書き込みます。 ここではメニュー名を書き込むのに waddstr を使っていますが、別の関数でもかまいません。 wattron を使って、デフォルトの色 (2 番) ではなく別の色 (3 番) で書き込みを行っているのに注意してください。 最初の行で wbkgd を使って 2 番をデフォルトの色として指定していますから、wattroff を呼ぶことでデフォルトの色に戻ります。

void draw_menubar(WINDOW *menubar)
{
    wbkgd(menubar,COLOR_PAIR(2));
    waddstr(menubar,"Menu1");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F1)");
    wattroff(menubar,COLOR_PAIR(3));
    wmove(menubar,0,20);
    waddstr(menubar,"Menu2");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F2)");
    wattroff(menubar,COLOR_PAIR(3));
}

次の関数は、F1 や F2 が押されたときにメニューを表示します。 メニューらしく見えるようにするために、新しいウィンドウはメニューバーと同じ白で作成します。 その後ろには青いウィンドウがあります。 この白いウィンドウは、後ろのウィンドウの以前の内容を上書きしないようにしないといけません。 メニューが閉じられたら元に戻す必要があるからです。。 メニューウィンドウを stdscr の子ウィンドウとして作成しないのは、そのためです。 例を見ると分かるように、item[0] というウィンドウは newwin 関数を使って作りますが、他の 8 つについては items[0] の子ウィンドウとして作成しています。 items[0] はメニューの外枠として使い、その他はメニュー内の選択項目として使います。 メニューの回りの枠の部分には上書きしないようにしています。 項目が選択されているように見せるためには、選択された項目を他の項目とは別の背景色にします。 後ろから 3 行目でやっているのがそれに相当します。 最初の項目の背景色は他とは違う色にしていますので、メニューがポップアップしたときには最初の項目が選択されていることになります。

WINDOW **draw_menu(int start_col)
{
    int i;
    WINDOW **items;
    items=(WINDOW **)malloc(9*sizeof(WINDOW *));

    items[0]=newwin(10,19,1,start_col);
    wbkgd(items[0],COLOR_PAIR(2));
    box(items[0],ACS_VLINE,ACS_HLINE);
    items[1]=subwin(items[0],1,17,2,start_col+1);
    items[2]=subwin(items[0],1,17,3,start_col+1);
    items[3]=subwin(items[0],1,17,4,start_col+1);
    items[4]=subwin(items[0],1,17,5,start_col+1);
    items[5]=subwin(items[0],1,17,6,start_col+1);
    items[6]=subwin(items[0],1,17,7,start_col+1);
    items[7]=subwin(items[0],1,17,8,start_col+1);
    items[8]=subwin(items[0],1,17,9,start_col+1);
    for (i=1;i<9;i++)
        wprintw(items[i],"Item%d",i);
    wbkgd(items[1],COLOR_PAIR(1));
    wrefresh(items[0]);
    return items;
}

次の関数は単に上の関数で作ったメニューウィンドウを破棄します。 まず items が指すウィンドウを全て delwin 関数で削除し、items ポインタ用に確保したメモリ領域を開放します。

void delete_menu(WINDOW **items,int count)
{
    int i;
    for (i=0;i<count;i++)
        delwin(items[i]);
    free(items);
}

scroll_menu 関数はメニュー間およびメニュー内部でスクロールするようにします。 キーボードから入力されたキーを getch を使って読み込みます。 上矢印キーか下矢印キーが押されたら、上もしくは下の項目が選択されます。 選択項目の背景色をその他の項目とは違った色にすれば良いです。 右矢印キーや左矢印キーが押されたら、現在開いているメニューを閉じて別のメニューを開きます。 エンターキーが押されたら選択されている項目を返却します。 ESC キーが押されたら項目を何も選択していない状態でメニューを閉じます。 これ以外のキー入力は無視します。 この関数では、getch を使うことでキーボードからのカーソルキーの入力を読み込んでいます。 これが可能なのは、最初の init_curses 関数で keypad(stdscr,TRUE) を呼んでいるからです。 また、ファンクションキーの値は char 型で保持出来る値よりも大きいため、getch の返却値を char 型の変数でなく int 型の変数に格納している点にも注意してください。

int scroll_menu(WINDOW **items,int count,int menu_start_col)
{
    int key;
    int selected=0;
    while (1) {
        key=getch();
        if (key==KEY_DOWN || key==KEY_UP) {
            wbkgd(items[selected+1],COLOR_PAIR(2));
            wnoutrefresh(items[selected+1]);
            if (key==KEY_DOWN) {
                selected=(selected+1) % count;
            } else {
                selected=(selected+count-1) % count;
            }
            wbkgd(items[selected+1],COLOR_PAIR(1));
            wnoutrefresh(items[selected+1]);
            doupdate();
        } else if (key==KEY_LEFT || key==KEY_RIGHT) {
            delete_menu(items,count+1);
            touchwin(stdscr);
            refresh();
            items=draw_menu(20-menu_start_col);
            return scroll_menu(items,8,20-menu_start_col);
        } else if (key==ESCAPE) {
            return -1;
        } else if (key==ENTER) {
            return selected;
        }
    }
}

最後はやはり main 関数です。 これまでに説明したすべての関数を使います。 また、getch を使ってキー押下を読み込み、F1 か F2 が押された場合には、draw_menu を使って適切なメニューウィンドウを作成します。 その後 scroll_menu を呼び出し、ユーザがメニューから選択できるようにします。 scroll_menu から戻ってきたらメニューウィンドウを破棄し、選択された項目をメッセージバーに表示します。

touchwin 関数についても触れておかない訳にはいきません。 メニューを閉じた後で touchwin を呼ばずにすぐに refresh を呼んでも、最後に開いていたメニューはスクリーン上に残ったままになります。 メニュー関数は stdscr を一切変更しないため、refresh が呼ばれても、ウィンドウが変更されていないと見なされしまい、stdscr に対して何も再表示しないからです。 touchwin は WINDOW 構造体中のフラグを立て、 全ての行で変更があったことを refresh に知らせます。 すると、実際にはウィンドウの内容が変わっていなくても、次に refresh が呼ばれたときにはウィンドウ全体が再表示されます。 メニューは、 stdscr に対して上書きするのではなく、新しいウィンドウとして作成してありますから、メニューを閉じれば stdscr の情報がそのまま残っています。

int main()
{
    int key;
    WINDOW *menubar,*messagebar;

    init_curses();

    bkgd(COLOR_PAIR(1));
    menubar=subwin(stdscr,1,80,0,0);
    messagebar=subwin(stdscr,1,79,23,1);
    draw_menubar(menubar);
    move(2,1);
    printw("Press F1 or F2 to open the menus. ");
    printw("ESC quits.");
    refresh();

    do {
        int selected_item;
        WINDOW **menu_items;
        key=getch();
        werase(messagebar);
        wrefresh(messagebar);
        if (key==KEY_F(1)) {
            menu_items=draw_menu(0);
            selected_item=scroll_menu(menu_items,8,0);
            delete_menu(menu_items,9);
            if (selected_item<0)
                wprintw(messagebar,"You haven't selected any item.");
            else
                wprintw(messagebar,
                  "You have selected menu item %d.",selected_item+1);
            touchwin(stdscr);
            refresh();
        } else if (key==KEY_F(2)) {
            menu_items=draw_menu(20);
            selected_item=scroll_menu(menu_items,8,20);
            delete_menu(menu_items,9);
            if (selected_item<0)
                wprintw(messagebar,"You haven't selected any item.");
            else
                wprintw(messagebar,
                  "You have selected menu item %d.",selected_item+1);
            touchwin(stdscr);
            refresh();
        }
    } while (key!=ESCAPE);

    delwin(menubar);
    delwin(messagebar);
    endwin();
    return 0;
}

コードを example.c というファイルにコピーして、説明の部分を取り除いたら、以下のようにしてコンパイルできます。

gcc -Wall example.c -o example -lcurses

コンパイルができたらプログラムを動かしてみてください。 コードは、参照に載せてある場所からダウンロードすることも可能です。

 

まとめ

この記事では ncurses の基礎を説明してきました。 あなたのプログラムにかっこいいインターフェースを持たせるには十分な内容でしょう。 しかし、このライブラリでできることは上で説明した内容にとどまりません。 たびたび言ってきたように、man ページを見ることでいろいろなことが発見できます。 この記事に載っていることが、単なる入門にすぎないのだということが分かると思います。

 

参照


Webpages maintained by the LinuxFocus Editor team
© Reha K. Gerçeker, FDL
LinuxFocus.org
翻訳履歴:
tr --> -- : Reha K. Gerçeker <gerceker/at/itu.edu.tr>
tr --> en: Reha K. Gerçeker <gerceker/at/itu.edu.tr>
en --> jp: 須藤 賢一 <deep_blue/at/users.sourceforge.jp>

2003-06-02, generated by lfparser version 2.36