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做修改之前一定要保存一份,程序结束后回复。

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

No comments:

Post a Comment