termios 结构是在POSIX规范中定义的标准接口,它类似于系统V中的termio接口,通过设置termios类型的数据结构中的值和使用一小组函数调用,你就可以对终端接口进行控制。
可以被调整来影响终端的值按照不同的模式被分为如下几组:
1.输入模式
2.输出模式
3.控制模式
4.本地模式
5.特殊控制模式
termios结构类型包括若干个标志集和一个控制字符的数组,所有的Unix版本包含以下结构体:
struct termios
{
tcflag_tc_iflag; //输入模式标志
tcflag_tc_oflag; //输出模式标志
tcflag_tc_cflag; //控制模式标志
tcflag_tc_lflag; //本地模式标志
cc_t c_cc[NCCS]; //控制字符
speed_tc_isspeed; //输入波特率
speed_tc_ospedd; //输出波特率
}
(1)c_iflag:输入模式标志,控制终端输入方式,具体参数如下所示。
键 值 | 说 明 |
IGNBRK | 忽略BREAK键输入 |
BRKINT | 如果设置了IGNBRK,BREAK键的输入将被忽略,如果设置了BRKINT ,将产生SIGINT中断 |
IGNPAR | 忽略奇偶校验错误 |
PARMRK | 标识奇偶校验错误 |
INPCK | 允许输入奇偶校验 |
ISTRIP | 去除字符的第8个比特 |
INLCR | 将输入的NL(换行)转换成CR(回车) |
IGNCR | 忽略输入的回车 |
ICRNL | 将输入的回车转化成换行(如果IGNCR未设置的情况下) |
IUCLC | 将输入的大写字符转换成小写字符(非POSIX) |
IXON | 允许输入时对XON/XOFF流进行控制 |
IXANY | 输入任何字符将重启停止的输出 |
IXOFF | 允许输入时对XON/XOFF流进行控制 |
IMAXBEL | 当输入队列满的时候开始响铃,Linux在使用该参数而是认为该参数总是已经设置 |
(2)c_oflag:输出模式标志,控制终端输出方式,具体参数如下所示。
键 值 | 说 明 |
OPOST | 处理后输出 |
OLCUC | 将输入的小写字符转换成大写字符(非POSIX) |
ONLCR | 将输入的NL(换行)转换成CR(回车)及NL(换行) |
OCRNL | 将输入的CR(回车)转换成NL(换行) |
ONOCR | 第一行不输出回车符 |
ONLRET | 不输出回车符 |
OFILL | 发送填充字符以延迟终端输出 |
OFDEL | 以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符将是NUL(‘\0’)(非POSIX) |
NLDLY | 换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s) |
CRDLY | 回车延迟,取值范围为:CR0、CR1、CR2和 CR3 |
TABDLY | 水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3 |
BSDLY | 空格输出延迟,可以取BS0或BS1 |
VTDLY | 垂直制表符输出延迟,可以取VT0或VT1 |
FFDLY | 换页延迟,可以取FF0或FF1 |
(3)c_lflag:本地模式标志,控制终端编辑功能,具体参数如下所示。
键 值 | 说 明 |
ISIG | 当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号 |
ICANON | 使用标准输入模式 |
XCASE | 在ICANON和XCASE同时设置的情况下,终端只使用大写。如果只设置了XCASE,则输入字符将被转换为小写字符,除非字符使用了转义字符(非POSIX,且Linux不支持该参数) |
ECHO | 显示输入字符 |
ECHOE | 如果ICANON同时设置,ERASE将删除输入的字符,WERASE将删除输入的单词 |
ECHOK | 如果ICANON同时设置,KILL将删除当前行 |
ECHONL | 如果ICANON同时设置,即使ECHO没有设置依然显示换行符 |
ECHOPRT | 如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX) |
TOSTOP | 向后台输出发送SIGTTOU信号 |
(4)c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如下所示。
|
(5)c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。
只有在本地模式标志c_lflag中设置了IEXITEN时,POSIX没有定义的控制字符才能在Linux中使用。每个控制字符都对应一个按键组合(^C、^H等),但VMIN和VTIME这两个控制字符除外,它们不对应控制符。这两个控制字符只在原始模式下才有效。
宏 | 说 明 |
c_cc[VINTR] | 默认对应的控制符是^C,作用是清空输入和输出队列的数据并且向tty设备的前台进程组中的每一个程序发送一个SIGINT信号,对SIGINT信号没有定义处理程序的进程会马上退出。 |
c_cc[VQUIT] | 默认对应的控制符是^/,作用是清空输入和输出队列的数据并向tty设备的前台进程组中的每一个程序发送一个SIGQUIT信号,对SIGQUIT 信号没有定义处理程序的进程会马上退出。 |
c_cc[verase] | 默认对应的控制符是^H或^?,作用是在标准模式下,删除本行前一个字符,该字符在原始模式下没有作用。 |
c_cc[VKILL] | 默认对应的控制符是^U,在标准模式下,删除整行字符,该字符在原始模式下没有作用。 |
c_cc[VEOF] | 默认对应的控制符是^D,在标准模式下,使用read()返回0,标志一个文件结束。 |
c_cc[VSTOP] | 默认对应的控制字符是^S,作用是使用tty设备暂停输出直到接收到VSTART控制字符。或者,如果设备了IXANY,则等收到任何字符就开始输出。 |
c_cc[VSTART] | 默认对应的控制字符是^Q,作用是重新开始被暂停的tty设备的输出。 |
c_cc[VSUSP] | 默认对应的控制字符是^Z,使当前的前台进程接收到一个SIGTSTP信号。 |
c_cc[VEOL]和c_cc[VEOL2] | 在标准模式下,这两个下标在行的末尾加上一个换行符('/n'),标志一个行的结束,从而使用缓冲区中的数据被发送,并开始新的一行。POSIX中没有定义VEOL2。 |
c_cc[VREPRINT] | 默认对应的控制符是^R,在标准模式下,如果设置了本地模式标志ECHO,使用VERPRINT对应的控制符和换行符在本地显示,并且重新打印当前缓冲区中的字符。POSIX中没有定义VERPRINT。 |
c_cc[VWERASE] | 默认对应的控制字符是^W,在标准模式下,删除缓冲区末端的所有空格符,然后删除与之相邻的非空格符,从而起到在一行中删除前一个单词的效果。 POSIX中没有定义VWERASE。 |
c_cc[VLNEXT] | 默认对应的控制符是^V,作用是让下一个字符原封不动地进入缓冲区。如果要让^V字符进入缓冲区,需要按两下^V。POSIX中没有定义 VLNEXT。 |
与结构体相关的函数
(1) tcgetattr()
l 原型:int tcgetattr(int fd,structtermois & termios_p);
l 功能:取得终端介质(fd)初始值,并把其值赋给temios_p;函数可以从后台进程中调用;但是,终端属性可能被后来的前台进程所改变。
(2)tcsetattr()
l 原型:inttcsetattr(int fd,int actions,const struct termios*termios_p);
l 功能:设置与终端相关的参数 (除非需要底层支持却无法满足),使用termios_p 引用的termios 结构。optional_actions(tcsetattr函数的第二个参数)指定了什么时候改变会起作用:
TCSANOW:改变立即发生
TCSADRAIN:改变在所有写入fd 的输出都被传输后生效。这个函数应当用于修改影响输出的参数时使用。(当前输出完成时将值改变)
TCSAFLUSH :改变在所有写入fd 引用的对象的输出都被传输后生效,所有已接受但未读入的输入都在改变发生前丢弃(同TCSADRAIN,但会舍弃当前所有值)。
(3)tcsendbreak()
传送连续的0 值比特流,持续一段时间,如果终端使用异步串行数据传输的话。如果duration 是0,它至少传输 0.25 秒,不会超过 0.5 秒。如果 duration非零,它发送的时间长度由实现定义。
如果终端并非使用异步串行数据传输,tcsendbreak()什么都不做。
(4)tcdrain()
等待直到所有写入fd 引用的对象的输出都被传输。
(5)tcflush()
丢弃要写入引用的对象,但是尚未传输的数据,或者收到但是尚未读取的数据,取决于queue_selector 的值:
TCIFLUSH:刷新收到的数据但是不读
TCOFLUSH :刷新写入的数据但是不传送
TCIOFLUSH :同时刷新收到的数据但是不读,并且刷新写入的数据但是不传送
(6)tcflow()
挂起 fd 引用的对象上的数据传输或接收,取决于action 的值:
TCOOFF:挂起输出
TCOON :重新开始被挂起的输出
TCIOFF :发送一个STOP 字符,停止终端设备向系统传送数据
TCION :发送一个START 字符,使终端设备向系统传输数据
打开一个终端设备时的默认设置是输入和输出都没有挂起。
(7)波特率函数
被用来获取和设置termios 结构中,输入和输出波特率的值。新值不会马上生效,直到成功调用了tcsetattr() 函数。
设置速度为 B0 使得 modem"挂机"。与 B38400 相应的实际比特率可以用setserial(8) 调整。 输入和输出波特率被保存于 termios 结构中。
cfmakeraw 设置终端属性如下:
termios_p->c_iflag &=~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8;
l cfgetospeed() 返回termios_p 指向的termios 结构中存储的输出波特率
l cfsetospeed() 设置termios_p 指向的termios 结构中存储的输出波特率为speed。取值必须是以下常量之一:
B0 B50 B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 B4800 B9600 B19200 B38400 B57600 B115200 B230400
其中:零值 B0 用来中断连接。如果指定了 B0,不应当再假定存在连接。通常,这样将断开连接。CBAUDEX是一个掩码,指示高于POSIX.1 定义的速度的那一些(57600 及以上)。因此,B57600& CBAUDEX 为非零。
l cfgetispeed() 返回 termios 结构中存储的输入波特率。
l cfsetispeed() 设置 termios 结构中存储的输入波特率为 speed。如果输入波特率被设为0,实际输入波特率将等于输出波特率。
RETURNVALUE 返回值
cfgetispeed() 返回termios 结构中存储的输入波特率。
cfgetospeed()返回 termios 结构中存储的输出波特率。
其他函数返回: 0(成功)或-1(失败),并且为 errno 置值来指示错误。
注意:tcsetattr()返回成功,如果任何所要求的修改可以实现的话。因此,当进行多重修改时,应当在这个函数之后再次调用tcgetattr() 来检测是否所有修改都成功实现
实例:
1.关闭终端回显,键盘输入的字符不会在终端窗口显示:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
int main(void)
{
struct termios ts,ots;
char passbuf[1024];
tcgetattr(STDIN_FILENO,&ts); // STDIN_FILENO的值是1,表示标准输入的文件描述符
ots = ts;
ts.c_lflag &= ~ECHO; /* 关闭回终端回显功能*/
ts.c_lflag |= ECHONL;
tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts); /* 应用新终端设置 */
fgets(passbuf,1024,stdin); /* 输入字符不会在终端显示 */
printf("you input character = %s/n",passbuf);
tcsetattr(STDIN_FILENO,TCSANOW,&ots); /* 恢复旧的终端设备 */
}
2.读取每一个字符
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
char *menu[] = {
"a - add new record",
"d - delete record",
"q - quit",
NULL,
};
int getchoice(char *greet, char *choices[], FILE *in, FILE *out);
int main()
{
int choice = 0;
FILE *input;
FILE *output;
struct termios initial_settings,new_settings;
if (!isatty(fileno(stdout))) {
fprintf(stderr,"You arenot a terminal, OK.\n");
}
input =fopen("/dev/tty", "r");
output = fopen("/dev/tty","w");
if(!input || !output) {
fprintf(stderr, "Unableto open /dev/tty\n");
exit(1);
}
tcgetattr(fileno(input),&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &=~ICANON;
new_settings.c_lflag &=~ECHO;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
new_settings.c_lflag &=~ISIG;
if(tcsetattr(fileno(input),TCSANOW, &new_settings) != 0) {
fprintf(stderr,"couldnot set attributes\n");
}
do {
choice =getchoice("Please select an action", menu, input, output);
printf("You havechosen: %c\n", choice);
} while (choice != 'q');
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}
int getchoice(char *greet, char *choices[], FILE *in, FILE *out)
{
int chosen = 0;
int selected;
char **option;
do {
fprintf(out, "Choice:%s\n",greet);
option = choices;
while(*option) {
fprintf(out,"%s\n",*option);
option++;
}
do {
selected = fgetc(in);
} while (selected == '\n' ||selected == '\r');
option = choices;
while(*option) {
if(selected ==*option[0]) {
chosen = 1;
break;
}
option++;
}
if(!chosen) {
fprintf(out,"Incorrect choice, select again\n");
}
} while(!chosen);
return selected;
}