CUIでいろいろと。

実験第二用に、ためしに作ったcurses*1のプログラム。0行目に入力欄があって、2行目以降に文字列が下に流れていくだけ。これをチャットに改造しようかと。
コンパイルは、

cc curses_test.c -lcurses

で。


参考にしたのはここら辺。http://publib16.boulder.ibm.com/pseries/Ja_JP/aixprggd/genprogc/mastertoc.htm#ToC_17
ここも使えそう。http://docs.hp.com/ja/B2355-90858/curses.5.html

#include <time.h>
#include <sys/select.h>
#include <curses.h>
#define BUFSIZE 1024
#define OUTPUT_LINE 2 // 表示領域の最初の行。( 0〜(LINES-1) )

void output_line( char * str ){
  int x, y;

  getyx( stdscr, y, x ); // 元の位置を覚えておく。

  move( OUTPUT_LINE, 0 );
  insertln( );
  addstr( "> " );
  addstr( str );

  move( y, x ); // 元の位置に戻す。
}

void reset_input_area( ){
  move( 0, 0 );
  deleteln( ); // 1度消して、
  insertln( ); // 新しく追加。
  addstr( "< " );
}

// キーボードからの入力。
void input_keybord( char * buf, int * buf_len, int * loop ){
  int c;

  c = getch( );

  if( c == '\n' ){ // 改行の場合。
    buf[ *buf_len ] = '\0';

    // 終了条件チェック。
    if(
       ( strcmp( buf, "." ) == 0 ) ||
       ( strcasecmp( buf, "q" ) == 0 ) ||
       ( strcasecmp( buf, "quit" ) == 0 ) ||
       ( strcasecmp( buf, "exit" ) == 0 )
       ){
      *loop = 0;
      return;
    }

    // 出力。
    output_line( buf );
    *buf_len = 0; // buf[]を空に。

    // 入力欄クリア。
    reset_input_area( );
  }else if( c == KEY_DOWN ){ // keypad をtrueにしないとこない。
    *loop = 0;
  }else if( c == KEY_BACKSPACE ){
    delch( );
    (*buf_len)--;
    if( *buf_len < 0 ) *buf_len = 0;
  }else if(
	   ( c == '' ) || // '^'+'D'じゃなくて、'C-d'。
	   ( c == '!' )
	   ){
    output_line( "!" );
    *loop = 0;
  }else if( c == '' ){
    reset_input_area( );
    buf[ *buf_len ] = '\0';
    addstr( buf );
  }else{ // その他の文字。
    if(0){ //test
      char buf[1024];
      sprintf( buf, "0x%02X", c );
      output_line( buf );
    }
    if( BUFSIZE - 1 <= *buf_len ) return; // bufがいっぱいだったらそのまま捨てる。
    if( c > 0xff ) return; // その他の特殊キーだったら捨てる。
    buf[ (*buf_len)++ ] = c;
  }
}

main(){
  char buf[ BUFSIZE ];
  int  buf_len = 0;
  time_t time_now;
  const int stdin_fd = 0; // = fileno( stdin )
  fd_set input_fds;
  struct timeval sleep;
  int r_select;
  int i;
  int loop;

  initscr( ); // cursesの初期化。
  cbreak( ); // いらないけどなんとなく。
  echo( ); // いらないけどなんとなく。
  keypad( stdscr, TRUE );

  // (0行目の次の)1行目に線を引く。
  for( i = 0; i < COLS; i++ ){
    mvaddch( 1, i, ACS_HLINE ); // 横線を引く。
  }

  reset_input_area( );
  refresh( );

  sleep.tv_sec = 0;
  sleep.tv_usec = 0;
  loop = 1;
  while( loop ){
    FD_ZERO( &input_fds );
    FD_SET( stdin_fd, &input_fds );

    r_select = select( 1, &input_fds, NULL, NULL, &sleep );

    if( r_select > 0 ){ // 入力がある。
      if( FD_ISSET( stdin_fd, &input_fds ) ){ // キーボードからの入力。
	input_keybord( buf, &buf_len, &loop );
      }
    }else if( r_select == 0 ){ // 時間切れ。
      time_now = time( NULL );
      output_line( ctime( &time_now ) );
      sleep.tv_sec = 1;
      sleep.tv_usec = 0;
    }

    refresh( );
  } // end while( loop )

  endwin( ); // cursesの終了。
}

xとyの順番が普通と逆なのがややこしいなぁ。y行目のx文字目って感じか。
BackSpaceがいやな感じ。うまいことやってくれないのかなぁ。nocbreak()とかやったら、echoされなくなるし。なんでよ。
最近、前一文字削除はC-hを使ってたので、BackSpaceのキーと動作が違うことに気づかんかった。
C-z と C-c は使える。C-h, C-j, C-m, C-iも。C-tはscreen、C-oはxkeymacsで乗っ取ってるのでわからん。
いい終了方法が思いつかなかった。C-dあたりかな、とも思うんだけど。

*1:ところでcursesって、どうよむの? あ、wikipediaにあるじゃん。カーシスだってさ。http://ja.wikipedia.org/wiki/Curses