terminfoを使用すると出力場所とかをイイ感じにできるかもしれない.
terminfoとは
terminfoのデータ
clear
カーソル移動
terminfoとは
かつてのUNIXシステムではいろんな端末があって,それらの制御方法は標準化されていなかった.そこで,統一されたインタフェースでプログラムからの要求を受け取り,保持している各端末の制御方法に関するデータを参照して処理するパッケージとしてterminfoが登場した.terminfoはcursesと組み合わせて使用するが,両者の境界はなんだかあいまいらしい.
terminfoのデータ
私のシステムではterminfoのデータが入っているterminfoファイルは/usr/share/terminfo/に沢山あった.これらはterminfoファイル名の頭文字ごとにディレクトリに分けられ,それぞれのterminfoファイルはticというコマンドでコンパイルされたもの(バイナリファイル)らしい.terminfoファイル内の各データは以下の3種類に分けられる.
ブール型
該当する機能をサポートするか否か.
数値型
サイズを表す.
文字列型
端末機能にアクセスするためのキーとか,キーボード入力をどんな文字列に変換してプログラムに渡すかを定義.
端末を制御するコーディングの流れは大体以下のようになる.
setuptermで現在の端末に対応するTERMINAL構造体を取得
tigetflag,tigetnum,tigetstrで現在の端末の状態を取得
エスケープシーケンスを生成
putp,tputsでエスケープシーケンスを端末に送る
エスケープシーケンスの生成についてちょっと書いておく.まず,どんな文字列型のデータがあるかは,man 5 terminfoに一覧がある(やたら多い).ここにある「Capname」をキーにしてtigetstrを呼び出すとその機能に対応したエスケープシーケンスが得られる.で,そこで得られるエスケープシーケンスの中にはそのままでは使用できないものがある.「カーソルを何行目の何列目に移動しろ」などという命令に対応するエスケープシーケンスは,数が膨大になるのを防ぐために,「何行目」とか「何列目」というのをパラメータ化している.このような場合,tparmを使用して取得したエスケープシーケンスのパラメータに具体的な値をいれるというステップが入る.
clear
サンプルとしてclearコマンドを作成する.man clearによるとclearコマンド自体,tputをclear引数とともに呼んでいるらしい.
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <term.h>
int main(void)
{
char *clear_cmd;
/* setupterm�ϼ��Ԥ���ȥ��顼���Ǥ��ƽ�λ���뤬
����Ȥ� */
if(setupterm(NULL, fileno(stdout), NULL) == ERR){
exit(EXIT_FAILURE);
}
/* clear���뤿��Υ��������ץ���������� */
clear_cmd = tigetstr("clear");
if(clear_cmd == (char *)-1 ||
clear_cmd == (char *)0){
exit(EXIT_FAILURE);
}
/* �����������������ץ�������ü�������� */
tputs(clear_cmd, 1, putchar);
exit(EXIT_SUCCESS);
}
terminfoを使用するときは,コンパイル時にコンパイルオプションが必要である.私のシステムでは以下でコンパイルできた.
gcc -Wall -o my_clear my_clear.c -lncurses
-lncursesがないと怒られる.システムによっては-Iでncurses.hの場所を教える必要があるかもしれない(私のシステムでは/usr/include/ncurses.hがあった).コンパイル後実行するとclearコマンドと同様の結果が得られた.
カーソル移動
もう一つの例としてカーソル移動のサンプルを作成してみた.
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <term.h>
#include <curses.h>
#include <errno.h>
/* �Υ˥���⡼�ɤˤ��� */
int to_nocanon(void);
/* ���������ư���� */
int mv_cursor(void);
static struct termios save_term;
static struct termios temp_term;
int main(void)
{
char *cmd;
int result;
/* ���åȥ��å� */
if(setupterm(NULL, fileno(stdout), (int *)0) == ERR){
fprintf(stderr,"setupterm failure\n");
exit(EXIT_FAILURE);
}
/* clear���� */
errno = 0;
if((cmd = tigetstr("clear")) == NULL){
perror("tigetstr failure");
exit(EXIT_FAILURE);
}
if(putp(cmd) == ERR){
exit(EXIT_FAILURE);
}
/* �Υ˥���⡼�ɤˤ��� */
if(to_nocanon() == -1){
exit(EXIT_FAILURE);
}
/* ���Ϥ˱����ƥ��������ư���� */
while(1){
if((result = mv_cursor()) == -1){
tcsetattr(fileno(stdin), TCSANOW, &save_term);
exit(EXIT_FAILURE);
}else if(result == 1){
break;
}
}
/* ���Υ˥���⡼�ɤ��᤹ */
tcsetattr(fileno(stdin), TCSANOW, &save_term);
exit(EXIT_SUCCESS);
}
/* �Υ˥���⡼�ɤˤ��� */
int to_nocanon(void)
{
errno = 0;
if(tcgetattr(fileno(stdin), &save_term) == -1){
perror("tcgetattr failure");
exit(EXIT_FAILURE);
}else{
temp_term = save_term;
}
temp_term.c_iflag &= IGNCR;
temp_term.c_oflag &= ONLRET;
temp_term.c_lflag &= (~ISIG & ~ICANON & ~ECHO);
temp_term.c_cc[VMIN] = 1;
temp_term.c_cc[VTIME] = 5;
errno = 0;
if(tcsetattr(fileno(stdin), TCSANOW, &temp_term) == -1){
perror("tcsetattr failure");
return -1;
}
return 0;
}
/* ���������ư���� */
int mv_cursor(void)
{
char ch;
char *cmd;
ch = getchar();
switch(ch){
default:
return 0;
break;
case 'q':
return 1;
break;
case 'h':
/* ���������1��� */
cmd = tigetstr("cub1");
break;
case 'j':
/* ���������1�IJ��� */
cmd = tigetstr("cud1");
break;
case 'k':
/* ���������1�ľ�� */
cmd = tigetstr("cuu1");
break;
case 'l':
/* ���������1�ı��� */
cmd = tigetstr("cuf1");
break;
}
if(cmd == NULL){
fprintf(stderr, "cmd == NULL\n");
return -1;
}else if(putp(cmd) == ERR){
fprintf(stderr, "putp failure\n");
return -1;
}
return 0;
}
このサンプルはviみたくhjklでカーソルを移動する.文字は入力できない.あとqを入力すると終了する.