美文网首页LinuxLinux学习之路
APUE读书笔记-18终端输入输出(14)

APUE读书笔记-18终端输入输出(14)

作者: QuietHeart | 来源:发表于2020-09-07 21:14 被阅读0次

    设置终端模式为cbreak或者raw

    #include "apue.h"
    #include <termios.h>
    #include <errno.h>
    
    static struct termios       save_termios;
    static int                  ttysavefd = -1;
    static enum { RESET, RAW, CBREAK } ttystate = RESET;
    
    int tty_cbreak(int fd) /* put terminal into a cbreak mode */
    {
        int              err;
        struct termios   buf;
    
        if (ttystate != RESET) {
            errno = EINVAL;
            return(-1);
        }
        if (tcgetattr(fd, &buf) < 0)
            return(-1);
        save_termios = buf; /* structure copy */
    
        /*
         * Echo off, canonical mode off.
         */
        buf.c_lflag &= ~(ECHO | ICANON);
    
        /*
         * Case B: 1 byte at a time, no timer.
         */
        buf.c_cc[VMIN] = 1;
        buf.c_cc[VTIME] = 0;
        if (tcsetattr(fd, TCSAFLUSH, &buf) < 0)
            return(-1);
    
        /*
         * Verify that the changes stuck. tcsetattr can return 0 on
         * partial success.
         */
        if (tcgetattr(fd, &buf) < 0) {
            err = errno;
            tcsetattr(fd, TCSAFLUSH, &save_termios);
            errno = err;
            return(-1);
        }
        if ((buf.c_lflag & (ECHO | ICANON)) || buf.c_cc[VMIN] != 1 ||
          buf.c_cc[VTIME] != 0) {
            /*
             * Only some of the changes were made. Restore the
             * original settings.
             */
            tcsetattr(fd, TCSAFLUSH, &save_termios);
            errno = EINVAL;
            return(-1);
        }
    
        ttystate = CBREAK;
        ttysavefd = fd;
        return(0);
    }
    
    int tty_raw(int fd)     /* put terminal into a raw mode */
    {
        int             err;
        struct termios  buf;
    
        if (ttystate != RESET) {
            errno = EINVAL;
            return(-1);
        }
        if (tcgetattr(fd, &buf) < 0)
            return(-1);
        save_termios = buf; /* structure copy */
    
        /*
         * Echo off, canonical mode off, extended input
         * processing off, signal chars off.
         */
        buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    
        /*
         * No SIGINT on BREAK, CR-to-NL off, input parity
         * check off, don't strip 8th bit on input, output
         * flow control off.
         */
        buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    
        /*
         * Clear size bits, parity checking off.
         */
        buf.c_cflag &= ~(CSIZE | PARENB);
    
        /*
         * Set 8 bits/char.
         */
        buf.c_cflag |= CS8;
    
        /*
         * Output processing off.
         */
        buf.c_oflag &= ~(OPOST);
    
        /*
         * Case B: 1 byte at a time, no timer.
         */
        buf.c_cc[VMIN] = 1;
        buf.c_cc[VTIME] = 0;
        if (tcsetattr(fd, TCSAFLUSH, &buf) < 0)
            return(-1);
    
        /*
         * Verify that the changes stuck. tcsetattr can return 0 on
         * partial success.
         */
        if (tcgetattr(fd, &buf) < 0) {
            err = errno;
            tcsetattr(fd, TCSAFLUSH, &save_termios);
            errno = err;
            return(-1);
        }
        if ((buf.c_lflag & (ECHO | ICANON | IEXTEN | ISIG)) ||
          (buf.c_iflag & (BRKINT | ICRNL | INPCK | ISTRIP | IXON)) ||
          (buf.c_cflag & (CSIZE | PARENB | CS8)) != CS8 ||
          (buf.c_oflag & OPOST) || buf.c_cc[VMIN] != 1 ||
          buf.c_cc[VTIME] != 0) {
            /*
             * Only some of the changes were made. Restore the
             * original settings.
             */
            tcsetattr(fd, TCSAFLUSH, &save_termios);
            errno = EINVAL;
            return(-1);
        }
    
        ttystate = RAW;
        ttysavefd = fd;
        return(0);
    }
    
    int tty_reset(int fd)      /* restore terminal's mode */
    {
        if (ttystate == RESET)
            return(0);
        if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)
            return(-1);
        ttystate = RESET;
        return(0);
    }
    void tty_atexit(void)        /* can be set up by atexit(tty_atexit) */
    {
        if (ttysavefd >= 0)
            tty_reset(ttysavefd);
    }
    
    struct termios * tty_termios(void)       /* let caller see original tty state */
    {
        return(&save_termios);
    }
    

    如果我们已经调用了tty_cbreak,我们需要在调用tty_raw之前调用tty_reset。调用tty_cbreak之后、再调用tty_raw的时候也是如此(即先调用tty_reset再tty_raw)。这样可以确保在出现错误的时候,终端不会处于不稳定的状态。

    还有两个函数:tty_atexit可以被建立成一个exit处理函数,保证在exit的时候终端的模式被重置,tty_termios返回一个指向原始的canonical模式的termios结构。

    我们对cbreak模式的定义如下:

    1. Noncanonical模式。正如我们这一节开始所述,这个模式会关闭一些输入字符的处理。它不会关闭信号处理,所以用户始终可以键入任何一个终端信号产生函数。需要注意的是,调用者应该获取这些信号,否则信号可能会终止终端程序导致终端可能处于一种cbreak模式。

      做为一个通用的规则,当我们写一个改变终端模式的程序的时候,我们应该捕获尽可能多的信号,这样我们可以在程序终止的时候重新设置终端。

    2. 关闭回显。

    3. 一次输入一个字节。为了实现这个,我们设置MIN为1,TIME为0。这样和前面表中列出的case B一样了。read不会返回,直到至少有一个字节可用。

    我们对raw模式的定义如下:

    1. Noncanonical模式。我们也会关闭对信号生成字符(ISIG)以及扩展输入字符(IEXTEN)的处理。另外,我们通过关闭BRKINT来不让BREAK字符产生信号。
    2. 关闭回显。
    3. 我们禁止输入上面CR和NL之间的映射(ICRNL)、等值输入检查(INPCK)、以及输入的第8位的strip(ISTRIP),以及输出流控制(IXON)。
    4. 8位字符(CS8),和等值检查(PARENB)被禁止。
    5. 所有输出的处理(OPOST)被禁止。
    6. 每次输入一个字节(MIN=1,TIME=0)。

    相关文章

      网友评论

        本文标题:APUE读书笔记-18终端输入输出(14)

        本文链接:https://www.haomeiwen.com/subject/oimnjktx.html