底層終端控制

 控制終端
POSIX.1定義了一個查詢和操縱終端的標準接口termios,他是一個數據結構和一系列操作這些數據結構的函數,
    #include <termios.h>
    struct termios
    {
        tcflag_t c_iflag;       /* input mode flags */
        tcflag_t c_oflag;       /* output mode flags */
        tcflag_t c_cflag;       /* control mode flags */
        tcflag_t c_lflag;       /* local mode flags */
        cc_t c_line;            /* line discipline */
        cc_t c_cc[NCCS];        /* control characters */
        speed_t c_ispeed;       /* input speed */
        speed_t c_ospeed;       /* output speed */
    }
    c_iflag用來控制輸入處理選項的,他將影響到終端驅動程序把輸入發送給程序之前是否對其進行處理,以及處理的方式,同樣,c_oflag用來控制輸出的處理。c_cflag決定終端的硬件特性的控制標誌。c_lflag用來決定本地終端的特性,例如輸入時是否回顯等,c_cc包含了特殊字符序列的值,以及他們所代表的操作。c_line表示控制協議。
   
    c_iflag flag constants:
        IGNBRK 忽略輸入中的BREAK條件
        BRKINT 如果設置了IGNBRK,將在BREAK時產生SIGINT
        IGNPAR Ignore framing errors and parity errors.
        PARMRK If  IGNPAR is not set, prefix a character with a parity error or
                   framing error with \377 \0.  If neither  IGNPAR  nor  PARMRK  is
                   set,  read  a  character with a parity error or framing error as
                   \0.
        INPCK  Enable input parity checking.
        ISTRIP Strip off eighth bit.
        INLCR  將輸入的CR轉化成NL
        IGNCR  忽略輸入中的CR
        ICRNL  Translate carriage return to newline on input (unless  IGNCR  is
                  set).
        IUCLC  (not in POSIX) Map uppercase characters to lowercase on input.
        IXON   Enable XON/XOFF flow control on output.
        IXANY  (XSI)  Typing  any  character will restart stopped output.  (The
                  default is to allow just the START character to restart output.)
        IXOFF  Enable XON/XOFF flow control on input.
        IMAXBEL
                  (not  in  POSIX) Ring bell when input queue is full.  Linux does
                  not implement this bit, and acts as if it is always set.

    c_oflag flag constants defined in POSIX.1:
        OLCUC  (not  in POSIX) Map lowercase characters to uppercase on output.
        ONLCR  將輸出中的NL,轉化成CR—NL
        OCRNL  將輸出中的CR轉換成NL
        ONOCR  Don’t output CR at column 0.
        ONLRET 不輸出CR
        OFILL  Send fill characters for a delay,  rather  than  using  a  timed
            delay.
        OFDEL  (not  in  POSIX)  Fill character is ASCII DEL (0177).  If unset,
            fill character is ASCII NUL (’\0’).  (Not implemented on Linux.)
        NLDLY  Newline   delay  mask.   Values  are  NL0  and  NL1.   [requires
            _BSD_SOURCE or _SVID_SOURCE or _XOPEN_SOURCE]
        CRDLY  Carriage return delay mask.  Values are CR0, CR1, CR2,  or  CR3.
            requires _BSD_SOURCE or _SVID_SOURCE or _XOPEN_SOURCE]
        TABDLY Horizontal  tab  delay  mask.  Values are TAB0, TAB1, TAB2, TAB3
            (or XTABS).  A value of TAB3, that is, XTABS,  expands  tabs  to
            spaces   (with   tab  stops  every  eight  columns).   [requires
            _BSD_SOURCE or _SVID_SOURCE or _XOPEN_SOURCE]
        BSDLY  Backspace delay mask.  Values are BS0 or BS1.  (Has  never  been
            implemented.)    [requires   _BSD_SOURCE   or   _SVID_SOURCE  or
            _XOPEN_SOURCE]
        VTDLY  Vertical tab delay mask.  Values are VT0 or VT1.
        FFDLY  Form feed  delay  mask.   Values  are  FF0  or  FF1.   [requires
            _BSD_SOURCE or _SVID_SOURCE or _XOPEN_SOURCE]

    c_cflag flag constants:
        CBAUD  (not   in   POSIX)   Baud  speed  mask  (4+1  bits).   [requires
            _BSD_SOURCE or _SVID_SOURCE]
        CBAUDEX (not in POSIX) Extra baud speed mask (1 bit), included in CBAUD.
            [requires _BSD_SOURCE or _SVID_SOURCE]
            (POSIX  says that the baud speed is stored in the termios struc-
            ture  without   specifying   where   precisely,   and   provides
            cfgetispeed()  and cfsetispeed() for getting at it. Some systems
            use bits selected by CBAUD in c_cflag, other systems  use  sepa-
            rate fields, e.g.  sg_ispeed and sg_ospeed.)
        CSIZE  Character size mask.  Values are CS5, CS6, CS7, or CS8.
        CSTOPB Set two stop bits, rather than one.
        CREAD  Enable receiver.
        PARENB Enable  parity  generation  on  output  and  parity checking for
            input.
        PARODD Parity for input and output is odd.
        HUPCL  在最後處理結束時,關閉設備並關閉連接
        CLOCAL 忽略調制解調器控制線
        LOBLK  (not  in POSIX) Block output from a noncurrent shell layer.  For
            use by shl (shell layers).  (Not implemented on Linux.)
        CIBAUD (not in POSIX) Mask for input speeds. The values for the  CIBAUD
            bits are the same as the values for the CBAUD bits, shifted left
            IBSHIFT  bits.   [requires  _BSD_SOURCE  or  _SVID_SOURCE]  (Not
            implemented on Linux.)
        CRTSCTS (not   in   POSIX)   Enable  RTS/CTS  (hardware)  flow  control.
            [requires _BSD_SOURCE or _SVID_SOURCE]
           
    c_lflag flag constants:
        ISIG   When any of the  characters  INTR,  QUIT,  SUSP,  or  DSUSP  are
            received, generate the corresponding signal.
        ICANON 啓用規範模式
        XCASE  (not in POSIX; not supported under Linux) If ICANON is also set,
            terminal is uppercase only.  Input is  converted  to  lowercase,
            except for characters preceded by \.  On output, uppercase char-
            acters are preceded by \ and lowercase characters are  converted
            to uppercase.
        ECHO   輸入回顯
        ECHOE  If  ICANON is also set, the ERASE character erases the preceding
            input character, and WERASE erases the preceding word.
        ECHOK  If ICANON is also set, the KILL  character  erases  the  current
            line.
        ECHONL 在規範模式中,即使沒有設置ECHO也回顯NL
        ECHOCTL
            (not in POSIX) If ECHO is also set, ASCII control signals  other
            than  TAB,  NL, START, and STOP are echoed as ^X, where X is the
            character with ASCII code 0x40 greater than the control  signal.
            For  example,  character  0x08  (BS) is echoed as ^H.  [requires
            _BSD_SOURCE or _SVID_SOURCE]
        ECHOPRT
            (not in POSIX) If ICANON and IECHO are also set, characters  are
            printed  as  they  are  being  erased.  [requires _BSD_SOURCE or
            _SVID_SOURCE]
        ECHOKE (not in POSIX) If ICANON is also set, KILL is echoed by  erasing
            each  character  on the line, as specified by ECHOE and ECHOPRT.
            [requires _BSD_SOURCE or _SVID_SOURCE]
        DEFECHO (not in POSIX) Echo only when a process is reading.  (Not imple-
            mented on Linux.)
        FLUSHO (not  in  POSIX;  not  supported  under  Linux)  Output is being
            flushed.  This flag is toggled by typing the DISCARD  character.
            [requires _BSD_SOURCE or _SVID_SOURCE]
        NOFLSH Disable flushing the input and output queues when generating the
            SIGINT, SIGQUIT and SIGSUSP signals.
        TOSTOP Send the SIGTTOU signal to the process  group  of  a  background
            process which tries to write to its controlling terminal.
        PENDIN (not  in POSIX; not supported under Linux) All characters in the
            input queue are reprinted  when  the  next  character  is  read.
            (bash  handles  typeahead  this  way.)  [requires _BSD_SOURCE or
            _SVID_SOURCE]
        IEXTEN Enable implementation-defined input processing.  This  flag,  as
            well as ICANON must be enabled for  the special characters EOL2,
            LNEXT, REPRINT, WERASE to be interpreted, and for the IUCLC flag
            to be effective.
    The  c_cc  array  defines the special control characters.
        VINTR  (003, ETX, Ctrl-C, or also 0177, DEL, rubout) Interrupt  charac-
            ter.  Send  a  SIGINT  signal.  Recognized when ISIG is set, and
            then not passed as input.
        VQUIT  (034, FS, Ctrl-\) Quit character. Send SIGQUIT  signal.   Recog-
            nized when ISIG is set, and then not passed as input.
        VERASE (0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) Erase charac-
            ter. This erases the previous not-yet-erased character, but does
            not erase past EOF or beginning-of-line.  Recognized when ICANON
            is set, and then not passed as input.
        VKILL  (025, NAK, Ctrl-U, or Ctrl-X, or also @)  Kill  character.  This
            erases  the input since the last EOF or beginning-of-line.  Rec-
            ognized when ICANON is set, and then not passed as input.
        VEOF   (004, EOT, Ctrl-D) End-of-file character.  More precisely:  this
            character  causes the pending tty buffer to be sent to the wait-
            ing user program without waiting for end-of-line.  If it is  the
            first  character  of  the  line,  the read() in the user program
            returns 0, which signifies end-of-file.  Recognized when  ICANON
            is set, and then not passed as input.
        VMIN   Minimum number of characters for non-canonical read.
        VEOL   (0,  NUL)  Additional  end-of-line  character.   Recognized when
            ICANON is set.
        VTIME  Timeout in deciseconds for non-canonical read.
        VEOL2  (not in POSIX; 0, NUL) Yet another end-of-line character.   Rec-
            gnized when ICANON is set.
        VSWTCH (not in POSIX; not supported under Linux; 0, NUL) Switch charac-
            ter. (Used by shl only.)
        VSTART (021, DC1, Ctrl-Q) Start character. Restarts output  stopped  by
            the  Stop  character.  Recognized when IXON is set, and then not
            passed as input.
        VSTOP  (023, DC3, Ctrl-S) Stop character. Stop output until Start char-
            acter  typed.   Recognized when IXON is set, and then not passed
            as input.
        VSUSP  (032, SUB, Ctrl-Z) Suspend character. Send SIGTSTP signal.  Rec-
            ognized when ISIG is set, and then not passed as input.
        VDSUSP (not  in  POSIX;  not  supported  under  Linux; 031, EM, Ctrl-Y)
            Delayed suspend character: send SIGTSTP signal when the  charac-
            ter  is  read  by  the user program.  Recognized when IEXTEN and
            ISIG are set, and the system supports job control, and then  not
            passed as input.
        VLNEXT (not  in  POSIX; 026, SYN, Ctrl-V) Literal next. Quotes the next
            input character, depriving it of  a  possible  special  meaning.
            Recognized when IEXTEN is set, and then not passed as input.
        VWERASE
            (not  in  POSIX;  027, ETB, Ctrl-W) Word erase.  Recognized when
            ICANON and IEXTEN are set, and then not passed as input.
        VREPRINT
            (not in POSIX; 022,  DC2,  Ctrl-R)  Reprint  unread  characters.
            Recognized  when  ICANON and IEXTEN are set, and then not passed
            as input.
        VDISCARD
            (not in POSIX; not supported under Linux; 017, SI, Ctrl-O)  Tog-
            gle: start/stop discarding pending output.  Recognized when IEX-
            TEN is set, and then not passed as input.
        VSTATUS
            (not in POSIX; not supported under Linux; status  request:  024,
            DC4, Ctrl-T).

        #include <termios.h>
        #include <unistd.h>
        int     tcgetattr   (int fd, struct termios *termios_p);
        int     tcsetattr   (int fd, int optional_actions, const struct termios *termios_p);

        int     tcsendbreak (int fd, int duration);
        int     tcdrain     (int fd);
        int     tcflush     (int fd, int queue_selector);
        int     tcflow      (int fd, int action);
        void    cfmakeraw   (struct termios *termios_p);

        speed_t cfgetispeed (const struct termios *termios_p);
        speed_t cfgetospeed (const struct termios *termios_p);
        int     cfsetispeed (struct termios *termios_p, speed_t speed);
        int     cfsetospeed (struct termios *termios_p, speed_t speed);
屬性控制
    tcgetattr用來查詢和fd相關聯的終端的參數,並將他們存儲到termios_p中,成功返回0,失敗返回-1。
    tcsetattr用來設置終端的屬性,其中optional_actions決定了改變什麼時候生效,他的值可以爲:
        TCSANOW     立即改變這些屬性值
        TCSADRAIN   改變發生在當所有fd上的輸出都已經被髮送到終端以後,當要改變輸出設置時
                使用此函數
        TCSAFLUSH   改變發生在當所有fd上的輸出都已經被髮送到終端以後,但任何掛起的輸入
                將會被丟棄。
速度控制
    上面列出的速度控制函數都是成對出現,用來獲取和設置相應的動作,其中要說明的是cfsetospeed的speed參數必須是下面列出的常數中的一個:
        B0(關閉連接)  B50
        B75     B110
        B134        B150
        B200        B300
        B600        B1200
        B1800       B2400
        B4800       B9600
        B19200      B38400
        B57600      B115200
        B230400
行控制
    tcdrain函數將一直保持等待,直到所有的輸出都已經寫入到文件描述符fd指向的文件爲止。
    爲了強迫輸入,輸出或者兩者同時刷新,便可以使用tcflush函數,queue_selector用來指定要刷新的數據,他的取值如下:
        TCIFLUSH  flushes data received but not read.
        TCOFLUSH  flushes data written but not transmitted.
        TCIOFLUSH flushes  both data received but not read, and data written but
               not transmitted.
    實際的流量控制,不管是處於開狀態還是關狀態,都由tcflow來控制,參數action用來控制函數的動作,他的取值如下:
        TCOOFF suspends output.
        TCOON  restarts suspended output.
        TCIOFF transmits a STOP character, which stops the terminal device from
            transmitting data to the system.
        TCION  transmits a START character, which starts the terminal  device
            transmitting data to the system.

進程控制
        #include <unistd.h>
        pid_t tcgetpgrp(int fd);
        int   tcsetpgrp(int fd, pid_t pgrp);
    函數tcgetpgrp返回fd終端上前臺運行的進程組pgrp_id的進程組標識號
    如果有足夠的權限,使用tcsetpgrp可以改變進程組的標識號


使用terminfo
    對於每種可能的終端類型,terminfo維護了其終端能力和特性的一個列表,被稱爲capname,capname可以分爲:布爾型,數值型,字符串型。其中,布爾型只能表示是否支持某一特定屬性,數值型用來定義和大小有關的能力,字符串型定義訪問某種特性的轉義序列或者定義當用戶按下任意鍵的動作。
        常用的終端能力(詳細的可查詢terminfo(5))
    capname     type        描述
    am      b (bool)    具有自動頁邊空白
    bce     b       使用背景色清除
    km      b       有META鍵
    ul      b       下劃線字符加粗
    cols        i (int)     當前終端列數
    lines       i       當前終端行數
    colors      i       支持的顏色數
    clear       s (strring) 清屏並將光標移動到初始位置
    cl      s       清除行內容
    ed      s       清除到屏尾的內容
    smcup       s       進入光標尋址模式
    cup     s       移動光標
    rmcup       s       退出光標尋址模式
    bel     s       蜂鳴
    flash       s       閃屏
    kf[0-63]    s       功能鍵F[0-63]
   
    爲了使用terminfo必須包含curses.h和term.h

使用步驟:
    1,初始化terminfo數據結構
    2,檢索capname 
    3,修改capname
    4,將修改後的capname輸出到終端
    5,其他
    6,重複2-5

1,初始化
    #include <curses.h>
    #include <term.h>
    int setupterm(char *term, int fildes, int *errret);
    term用來指定終端類型,如果爲NULL,則會讀取環境變量TERM的值,否則,就會使用term所指定的終端類型。所有的輸出都將會發送到由fildes指示的文件或設備中,如果errret不是NULL,1表示成功,0表示在terminfo沒有找到term所指示的終端,-1表示terminfo數據庫沒有找到。返回OK表示調用成功,ERR表示失敗。

2,檢索
    int tigetflag(char *capname);
    int tigetnum(char *capname);
    char *tigetstr(char *capname);
    他們分別用來檢索布爾型,數值型,字符串型
    tigetflag支持返回TRUE,不支持返回FALSE,如果檢索的不是布爾型,就返回-1;
    tigetnum不支持capname返回-1,如果不是數值型,返回-2。
    tigetstr返回包含capname的轉義序列,如果不支持capname返回(char *)NULL, 如果不是字符串型返回(char *)-1;

3,修改
    在發送控制字符串之前,你必須線構造他,這就是tparm的工作。
    char *tparm(char *str, ...);
    成功時返回一個被恰當格式化過的字符串,失敗則返回NULL。
4,輸出到終端
    int tputs(const char *str, int affcnt, int (*putc)(int));
    int putp(const char *str);
    putp是將str輸出到標準輸出上,他等價與tputs(str,1,putchar)
    tputs將str輸出到setupterm中指定的term和fildes,str必須是一個terminfo字符串變量或者調用taprm或tigetstr的返回值,如果str是與行相關的能力,則affcnt便是將會影響的行數,putc是一個指向putchar風格(一次輸出一個字符)的指針。

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

int main(void)
{
 int i;
 char *buf;

 if (ERR == setupterm(NULL, fileno(stdin), NULL))
 {
  perror("failed on setupterm\n");
  exit(EXIT_FAILURE);
 }
 i = tigetflag("km");
 printf("km = %d\n", i);
 i = tigetnum("lines");
 printf("lines = %d\n", i);
 buf = tigetstr("flash");
 printf("bel = \\E%s\n", &buf[1]);
 
 return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>

int main(void)
{
 struct termios old, new;
 char passwd[32] = {0};
 
 if (-1 == tcgetattr(fileno(stdin), &old)) /*get ols attr.*/
 {
  perror("failed on tcgetattr\n");
  exit(EXIT_FAILURE);
 }
 
 new = old;
 new.c_lflag &= ~ECHO;  /*NO ECHO*/
 new.c_lflag |= ECHONL; /*echo nl*/
 
 if (-1 == tcsetattr(fileno(stdin), TCSANOW, &new)) /*set new attr*/
 {
  perror("failed on tcsetattr\n");
  exit(EXIT_FAILURE);
 }
 
 if ((-1 == tcgetattr(fileno(stdin), &new))) /*check set*/
 {
  perror("failure set\n");
  exit(EXIT_FAILURE);
 }
 if (new.c_lflag & ECHO)
 {
  puts("error on ECHO");
  exit(EXIT_FAILURE);
 }
 if (!new.c_lflag & ECHONL)
 {
  puts("error on ECHONL");
  exit(EXIT_FAILURE); 
 }
 
 puts("Input passwd:");
 fgets(passwd, 31, stdin);
 puts(passwd);
 
 tcsetattr(fileno(stdin), TCSANOW, &old); /*reset to old*/
 
 return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <termios.h>

void err(const char * str);
static void sig_catch(int sig);
int main(void)
{
 int fd = fileno(stdin);
 char c;
 struct termios old, new;
 
 if (signal(SIGINT, sig_catch) == SIG_ERR
  || signal(SIGQUIT, sig_catch) == SIG_ERR
  || signal(SIGTERM, sig_catch) == SIG_ERR)
  err("faile on signal\n");
 
 if (-1 == tcgetattr(fd, &old))
  err("failed on tcgetattr\n");
 new = old;
 new.c_lflag &= ~(ECHO | ICANON | ISIG);
 new.c_iflag &= ~(BRKINT | ICRNL);
 new.c_oflag &= ~OPOST;
 new.c_cc[VTIME] = 0;
 new.c_cc[VMIN]  = 1;
 if (-1 == tcsetattr(fd, TCSAFLUSH, &new))
  err("failed on tcsetattr");
 
 puts("Input keys:");
 while (1)
 {
  if (1 != read(fd, &c, 1))
   break;
  if (0161 == c)
   break;
  printf("%o\n",c);
 }
 
 tcsetattr(fd, TCSANOW, &old);
 return 0;
}

static void sig_catch(int sig)
{
 printf("Catch : %d\n",sig);
}

void err(const char * str)
{
 perror(str);
 exit(EXIT_FAILURE);
}

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

void moveto(int y, int x);
void clrscr(void);
void err(const char * str);
int main(void)
{
 int i;
 
 if (-1 == setupterm(NULL, fileno(stdout), NULL))
  err("setupinfo");
 printf("test...\n");
 getchar();
 clrscr();
 moveto(3, 3);
 i = tigetflag("km");
 printf("km = %d\n", i);
 printf("lines : %d cols : %d\n",
  tigetnum("lines"), tigetnum("cols"));
 moveto(10, 10);
 printf("X");
 moveto(1, 1);
 printf("x");

 return 0;
}

void moveto(int y, int x)
{
 char * buf = tigetstr("cup");
 putp(tparm(buf, y, x));
}

void clrscr(void)
{
 char *buf = tigetstr("clear");
 putp(buf);
}

void err(const char *str)
{
 printf("Error on %s\n", str);
 exit(EXIT_FAILURE);
}


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章