Wednesday, January 11, 2012

[L] curses

curses里面封装了一下对terminal 还有cursor的用法,从我之前一篇blog也可以看出如果不封装是多么令人肾虚。

需要搞明白一点的是,前面是对terminal进行操作,这个是对X window进行操作。所以从本质上来讲是不一样的。但是用起来感觉没什么大不同。。

1. initscr(), endwin()
  init之后,整个屏幕被刷掉,生成一个新屏幕。在这里只能用curses里面的printw,scanw之类的。因为这是它自己的屏幕。之后endwin()退出。

2. move(x,y)
  封装得够简单了。

3. attron, attroff
  对输出属性进行设置,试一下就知道了。
  至于属性设置参考这里:
  http://www.mkssoftware.com/docs/man3/curs_attr.3.asp

4. refresh()
  把整个屏幕刷新一遍。比如initscr之后,如果不refresh,那么屏幕原来有的还在,很混乱。
  本来应该是所有动作之后都应该refresh一下,但是有些试了下不要也行。个人猜测是覆盖的原因。如果有覆盖的话要refresh().
  还有一点要注意的就是,必须有change 才会刷新。如果没有change还是不变的。如果没有change硬要刷新的话也行,用touchwin(WINDOW).

5. cbreak(), echo()
  前者相当于之前的non-canonical,就是不是一行输入了。变成一个个输入,包括换行。
  后者指定是否回显。

实践代码如下,因为都是终端运行即可见了,所以样例也不用贴了。


代码一:

*注意一点:中间getchar()的时候,不论输入什么也不会回显的。这是因为被流弄走了,但是不会保留在window上。而getstr(), getch()则会留在屏幕上,如果不把echo()关掉的话。


/*
 * =====================================================================================
 *
 *       Filename:  screen1.c
 *
 *    Description:  Uses of curses.h
 *
 *        Version:  1.0
 *        Created:  Wednesday, January 11, 2012 02:55:22 HKT
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Hongchao Deng (fengjingchao), fengjingchao@gmail.com
 *        Company:  Sun Yat-sen University
 *
 * =====================================================================================
 */


#include <errno.h>
#include <math.h>  
#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <curses.h>


#define PW_LEN 256
#define NAME_LEN 256


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  main
 *  Description:  main function
 * =====================================================================================
 */
int
main ( int argc, char *argv[] )
{
   /* Parameters */
   char *scan_ptr;
   char witch_one[] = "First Witch  ";
   char witch_two[] = "Second Witch ";


   /* TODO */
   initscr();


    
   move(5,15);
   attron(A_BOLD);
   printw("%s", "Macintosh");
   attroff(A_BOLD);
   refresh();
   getchar();
   
   move(8,15);
   attron(A_STANDOUT);
   printw("Thunder and lighting");
   attroff(A_STANDOUT);
   refresh();
   getchar();


   move(10,10);
   printw("TEST PRINT");
   move(11,23);
   printw("TEST PRINT");
   refresh();
   getchar();


   attron(A_DIM);
   scan_ptr = witch_one + strlen(witch_one) - 1;
   while(scan_ptr != witch_one){
      move(10,10);
      insch(*--scan_ptr);
   }
   attroff(A_DIM);
   refresh();
   getchar();


   move(LINES -1, COLS -1);
   printw("%s",witch_two);
   refresh();
   getchar();


   clear(); 


   char name[NAME_LEN];
   char password[PW_LEN];


   char *real_password = "123456";


   move(5,10);
   printw("Please login");


   move(7,10);
   printw("User name: ");
   getstr(name);


   move(8,10);
   printw("Password: ");
//   refresh();
   
   cbreak();
   noecho();


   memset(password,0,sizeof(password));
   
   int i=0;
   for(;i<PW_LEN;i++){
      password[i] = getch();
      if( password[i] == '\n') break;
      move(8,20+i);
      addch('*');
//      refresh();
   }
   
   echo();
   nocbreak();


   endwin();
return EXIT_SUCCESS;
} /* ----------  end of function main  ---------- */




××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
代码二:
展示多个window的情况





/*
 * =====================================================================================
 *
 *       Filename:  multiw.c
 *
 *    Description:  Managing Multiple Windows
 *
 *        Version:  1.0
 *        Created:  Wednesday, January 11, 2012 07:42:44 HKT
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Hongchao Deng (fengjingchao), fengjingchao@gmail.com
 *        Company:  Sun Yat-sen University
 *
 * =====================================================================================
 */




#include <errno.h>
#include <math.h>  
#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <curses.h>


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  main
 *  Description:  main function
 * =====================================================================================
 */
int
main ( int argc, char *argv[] )
{
   WINDOW *new_window_ptr;
   WINDOW *popup_window_ptr;


   int x_loop;
   int y_loop;


   char letter = 'a';
   
   initscr();
   
   move(5,5);
   printw("Testing multiple windows");
   refresh();
   sleep(1);


   for ( y_loop = 0;y_loop < LINES -1 ;++y_loop ) {
      for (x_loop = 0; x_loop < COLS -1; ++x_loop) {
         mvaddch(y_loop,x_loop,letter);
         letter++;
         if(letter >'z') letter = 'a';
      }
   }
   refresh();
   getchar();


   new_window_ptr = newwin(10,20,5,5);
   mvwprintw(new_window_ptr,2,5,"Testing multiple windows");
   mvwprintw(new_window_ptr,5,10,"*********************************************************");
   wrefresh(new_window_ptr);
   getchar();


   letter = '0';
   for ( y_loop = 0;y_loop < LINES -1 ;++y_loop ) {
      for (x_loop = 0; x_loop < COLS -1; ++x_loop) {
         mvaddch(y_loop,x_loop,letter);
         letter++;
         if(letter >'9') letter = '0';
      }
   }
  
   refresh();
   getchar();


   wrefresh(new_window_ptr);
   getchar();


   touchwin(new_window_ptr);
   wrefresh(new_window_ptr);
   getchar();


   endwin();


return EXIT_SUCCESS;
} /* ----------  end of function main  ---------- */



Tuesday, January 10, 2012

[L] termios, curses, Terminal Control


好吧,终于到 terminal 了。那真叫一个纠结,最后还是看完了。。
附上实践:

/*
 * =====================================================================================
 *
 *       Filename:  menu.c
 *
 *    Description:  Reading Each Character, 
 *                    Without processing some signal characters even like C-c
 *                  Using Terminal Control
 *
 *        Version:  2.0
 *        Created:  Wednesday, December 28, 2011 11:27:45 HKT
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Hongchao Deng (fengjingchao), fengjingchao@gmail.com
 *        Company:  Sun Yat-sen University
 *
 * =====================================================================================
 */




#include <errno.h>
#include <math.h>  
#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <term.h>
#include <ncurses.h>


char *menu[] = {
   "a - add new record",
   "d - delete record" ,
   "q - quit",
   NULL
};


FILE *output_stream = NULL;


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  char_to_terminal
 *  Description: use output_stream to which the program writes the output.
 *                Otherwise in the getchoice() function we can use putp();
 * =====================================================================================
 */
int
char_to_terminal ( int char_to_write )
{
   if (output_stream) putc(char_to_write, output_stream);
   return 0;
} /* -----  end of function char_to_terminal  ----- */




/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  getchoice
 *  Description:  get the choice from prompt. 
 *                it will simply discard any invalid input including chars like ^C
 * =====================================================================================
 */
int
getchoice ( FILE *input, FILE *output)
{
   int choice;
   int chosen = 0;
   int screenrow = 4, screencol = 10;
   char **option;
   char *cursor, *clear;


   output_stream = output;


   setupterm(0,1,0);
   cursor = tigetstr("cup");
   clear = tigetstr("clear");


   tputs(clear, 1, char_to_terminal );


   while(!chosen){
      tputs(tparm(cursor,screenrow++,screencol), 1, char_to_terminal);
      fprintf(output,"Please select an option:\n");
      for(option = menu; *option != NULL; option++, screenrow++){
         tputs(tparm(cursor,screenrow++,screencol), 1, char_to_terminal);
         fprintf(output,"%s\n", *option);
      }


      for(choice = '\n'; choice == '\n';choice = fgetc(input));
      for(option = menu; *option != NULL; option++){
         if(choice == *option[0]){
            chosen = 1; break;
         }
      }
      if(!chosen) {
         tputs(tparm(cursor,screenrow++,screencol), 1, char_to_terminal);
         fprintf(output, "Incorrect choice: '%c'\n",choice);
      }
   }


   tputs(clear, 1, char_to_terminal );
   return choice;
} /* -----  end of function getchoice  ----- */



/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  main
 *  Description:  main function
 * =====================================================================================
 */
int
main ( int argc, char *argv[] )
{


   int choice = 0;


   FILE *input;                  /* input-file pointer */
   input = fopen( "/dev/tty", "r" );
   
   FILE *output;
   output       = fopen( "/dev/tty", "w");


   if ( input == NULL || output == NULL ) {
      fprintf ( stderr, "couldn't open file '%s'; %s\n",
        "/dev/tty", strerror(errno) );
      exit (EXIT_FAILURE);
   }
   
   struct termios initialSetting, newSetting;


   tcgetattr(fileno(stdin),&initialSetting);
   newSetting = initialSetting;
   newSetting.c_lflag   &= ~ISIG;
   newSetting.c_lflag   &= ~ECHO;
   newSetting.c_cc[VMIN] = 1;
   newSetting.c_cc[VTIME]= 0;
   newSetting.c_lflag   &= ~ICANON;


   if(tcsetattr(fileno(stdin), TCSANOW , &newSetting) != 0)
      fprintf(stderr, "Could not set terminal attributes!");
   
   for(choice = 0; choice != 'q';){
      choice = getchoice(input, output);
      printf("You have chosen %c\n",choice);
      sleep(1);
   }
   
   tcsetattr(fileno(stdin), TCSANOW , &initialSetting); 


   if( fclose(input) == EOF || fclose(output) == EOF ) {/* close input file   */
      fprintf ( stderr, "couldn't close file '%s'; %s\n",
        "/dev/tty", strerror(errno) );
      exit (EXIT_FAILURE);
   }


return EXIT_SUCCESS;
} /* ----------  end of function main  ---------- */


输出可以当场试,都已经是 terminal 了。。


总结一下:
功能:
1. 能调整光标的行列。
2.    能使得输入输出不显示,操作输入输出怎么读写。

经验:
1.    setupterm就直接 setupterm(0,1,0), 不知道第二个参数 (flie descriptor) 干嘛用的。
2.    逃过stdout , 用‘/dev/tty’ .. 但是注意对terminal输出控制的时候不能用putp()了,要用tputs().
3.    其实操作terminal都是一堆字符输过去的,而且不同类型的terminal还有不同的控制方法,所以才有 curses这个库。
4.    父进程会直接将stdin这些copy到子进程里面,就是说如果不回复过来的话,那么terminal就变成那样了。。所以对std 三剑客in,out,err做修改之前一定要保存一份,程序结束后回复。

我也不太懂。。下次弄个牛逼一点的再写份好点的总结。这次算记录。。