设置终端模式为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模式的定义如下:
-
Noncanonical模式。正如我们这一节开始所述,这个模式会关闭一些输入字符的处理。它不会关闭信号处理,所以用户始终可以键入任何一个终端信号产生函数。需要注意的是,调用者应该获取这些信号,否则信号可能会终止终端程序导致终端可能处于一种cbreak模式。
做为一个通用的规则,当我们写一个改变终端模式的程序的时候,我们应该捕获尽可能多的信号,这样我们可以在程序终止的时候重新设置终端。
-
关闭回显。
-
一次输入一个字节。为了实现这个,我们设置MIN为1,TIME为0。这样和前面表中列出的case B一样了。read不会返回,直到至少有一个字节可用。
我们对raw模式的定义如下:
- Noncanonical模式。我们也会关闭对信号生成字符(ISIG)以及扩展输入字符(IEXTEN)的处理。另外,我们通过关闭BRKINT来不让BREAK字符产生信号。
- 关闭回显。
- 我们禁止输入上面CR和NL之间的映射(ICRNL)、等值输入检查(INPCK)、以及输入的第8位的strip(ISTRIP),以及输出流控制(IXON)。
- 8位字符(CS8),和等值检查(PARENB)被禁止。
- 所有输出的处理(OPOST)被禁止。
- 每次输入一个字节(MIN=1,TIME=0)。
网友评论