何为终端:
http://www.PRogramgo.com/article/8062473246/
http://www.programgo.com/article/7784652169/;jsessionid=242CCD1156839513C97203D4E4623CB7
一个终端模拟器打开的窗口可以视为一个终端,tty1到tty7也是终端
终端也是一个设备文件,通常一个终端有自己的标准输入,标准输出,标准错误,它们都可以被重定向。
有一个特殊设备/dev/tty始终指向当前终端或当前的登录回话。通过对/dev/tty操作来避免重定向导致的问题
若想知道标准输出是否被重定向,只需要检查底层文件描述符是否关联到一个终端即可。
系统调用
#include <unisd.h>
int isatty(int fd):
如果打开的文件描述符fd连接到一个终端,则系统调用isatty返回1,否者返回0;。
终端驱动程序和通用接口:
有时,程序需要更加精细的终端控制能力,而不是仅通过简单的文件操作来完成对终端的一些控制。
linux提供了一组编程接口用来控制终端驱动程序的行为,从而使得更好地控制终端的输入和输出。
termios是在POSIX规范中定义的标准接口。通过设置termios类型的数据结构中的值和使用一小组
函数调用,你就可以对终端接口进行控制。
可以被调整用来影响终端的值按照不同的模式被分成如下几组:
输入模式
输出模式
控制模式
本地模式
特殊控制字符
最小termios结构的典型定义如下
#include <termios.h>
struct termios{
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
};
int tcgetattr(int fd, struct termios *termios_p);
把当前终端接口变量的值写入termios_p参数指向的结构。
int tcsetattr(int fd, int actions, const struct termios *termios_p);
把termios_p的值写入终端设置,此外程序有责任将终端状态恢复为改变之前的状态
action参数:
TCSANOW:立刻对值进行修改
TCSADRAIN:等当前的输出完成后再对值进行修改
TCSAFLUSH:等当前的输出完成后再对值进行修改,但丢弃还未从read调用返回的当前可用的任何输入
输入模式:输入模式控制输入数据(终端驱动程序从串行口或键盘接收到的字符)在被传递给程序之前的处理方式。
通过设置termios结构中的c_iflag成员的标志对它们进行控制。所有的标志都被定义为宏
一般默认设置就可以,无需改动。
如下:
BRKINT: 在当前输入行检测到一个终止状态时,产生一个中断
IGNBRK: 忽略输入行中的终止状态
ICRNL: 将接收到的回车符转换为新行符
IGNCR: 忽略接收到的回车符
INLCR: 将接收到的新行符转换为回车符
IGNPAR: 忽略奇偶校验错误的字符
INPCK: 对接收到的字符执行奇偶校验
PARMRK: 对奇偶校验出错做出标记
ISTRip: 将所有接收到的字符裁剪为7比特
IXOFF: 对输入启用软件流控
IXON: 对输出启用软件流控
输出模式:同样的通过设置c_oflag来控制,如果OPOST没有被设置,则其他所有标志都被忽略
OPOST 打开输出处理功能
ONLCR 将输出中的换行符转换为回车/换行符
OCRNL 将输出中的回车符转换为新行符
ONOCR 在第0列不输出回车符
ONLRET 不输出回车符
OFILL 发送填充字符以提供延时
OFDEL 用DEL而不是NULL字符作为填充字符
NLDLY 新行符延时选择
CRDLY 回车符延时选择
TABDLY 制表符延时选择
BSDLY 退格符延时选择
VTDLY 垂直制表符延时选择
FFDLY 换页符延时选择
控制模式:
控制模式控制终端的硬件特性。同样的设置c_cflag成员
控制模式主要用于串行线连接调制解调器的情况,虽然它也可以用来和终端进行“对话”。但与通过使用termios的控制模式来修改默认的线路行为相比,直接修改终端配置文件通常更加容易些。????
CLOCAL 忽略所有调制解调器状态的状态行
CREAD 启用字符接收器
CS5 发送或接收字符时使用5比特
CS6 发送或接收字符时使用6比特
CS7 发送或接收字符时使用7比特
CS8 发送或接收字符时使用8比特
CSTOPB 每个字符使用两个停止位而不是一个
HUPCL 关闭时挂断调制解调器,
PARENB 启用奇偶校验码的生成和检测功能
PARODD 使用奇校验而不是偶校验
本地模式:
本地模式控制终端的各种特性,通过设置c_lflag成员
ECHO ***启用输入字符的本地回显功能
ECHOE 接收到ERASE时执行退格、空格、退格的动作组合
ECHOK 接收到KILL字符时执行行删除操作
ECHONL 回显新行符
ICANON ***启用标准输入处理
IEXTEN 启用基于特定实现的函数
ISIG 启用信号
NOFLSH 禁止清空队列
TOSTOP 在试图进行写操作之前给后台进程发送一个信号
特殊控制字符:
特殊控制字符是一些字符组合,如Ctrl+C,当用户键入这样的组合键时,终端会采取一些特殊的处理方式。
c_cc数组成员将各种特殊控制字符映射到对应的支持函数。每个字符的位置(也就是数组下标)是由一个宏定义的,
但并不限制这些字符必须是控制字符。
根据本地模式中的ICANON标志,即是否将终端设置为标准模式或非标准模式c_cc数组有很大差别。
在标准模式下可用的数组下标
VEOF
VEOL
VERASE
VINTR
VKILL
VQUIT
VSUSP
VSTART
VSTOP
在非标准模式下可用的数组下标
VINTR
VMIN
VQUIT
VSUSP
VTIME
VSTART
VSTOP
在shell下使用stty -a可以查看当前的termios设置情况
终端速度:
speed_t cfgetispeed(const struct termios *);
speed_t cfgetospeed(const struct termios *);
int cfsetispeed(struct termios *, speed_t speed);
int cfsetospeed(struct termios *, speed_t speed);
这些函数都是作用于termios结构体的,所以需要调用tcsetattr才能生效
speed参数可以为B0, B1200, B2400, B9600, B19200, B38400
其他函数:
int tcdrain(int fd);//等待所有的输出,才返回
int tcflow(int fd, int flowtype);//暂停或重新开始输出
int tcflush(int fd, int in_out_selector);//清空输入、输出或两者都清空
终端的输出:
通过termios可以控制键盘的输入,但是确不能够控制输出到屏幕的内容。比如在特定的位置输出。
几乎所有的终端都是用escape转义序列控制(以escape字符开头的字符串)来控制光标的位置和终端的其他属性——比如黑体和闪烁等,但是他们在实现手段上没有统一的标准。terminfo软件包便解决了,终端之间的差异问题,使程序的移植性更好。
terminfo软件包包含一个由大量不同类型终端的功能标志和escape转义序列等信息构成的数据库,并为它们提供了一个统一的编程接口。一个使用这个软件包的程序能够随着数据库的扩展来适应未来的终端类型,对不同类型终端的支持不再需要由应用程序自身来提供。
每个终端类型对应一个terminfo文件。每个terminfo定义由3种类型的数据项组成。每个数据项被成为capname(可以简单的将一个数据项理解为这个终端的什么输入与什么动作或什么信息之间的映射关系),它们分别用于终端的一种功能标志,三种类型的功能标志为。
布尔功能标志:指出终端是否支持某个特定的功能 比如支持XON/XOFF流控 xon
数值功能标志:定义长度 比如 cols#80 lines#24
字符串功能标志:它有两种终端作用 ,用于访问终端功能的输出字符串和当用户按下特定按键时终端接收 到的输入字符串。比如删除到行尾 el=Esc,[,K 它在terminfo源文件中写为el=/E[K,还有很多内容不详细列举(page165)
使用terminfo功能标志:
使用terminfo的第一步是调用setupterm设置终端类型,这将为当前的终端类型初始化一个TERMINAL结构
#include <term.h>
int setupterm(char *term, int fd, int *errret);
term: 将终端类型设置为term指向的值,如果为NULL,就使用环境变量TERM的值
fd:为一个打开的文件描述符,它用于向终端写数据,比如 stdout
errret:如果他不是一个空指针,返回值保存在errret中
-1:terminfo数据库不存在
0:terminfo数据库中没有匹配的数据项
1:成功
返回值:其在成功时返回OK,失败时返回ERR。如果errret被设置为空指针,setupterm函数会在失败时输出一条诊断信息并导致程序直接退出。
第二步:通过调用合适的函数获得功能标志
int tigetflag(char *capname);//返回terminfo中的布尔功能标志,失败返回 -1
int tigetnum(char *capname);//数值功能标志. -2
char *tigetstr(char *capname);//字符串功能标志 (char *)-1
例:获得当前终端的大小
1. setupterm(NULL, fileno(stdout), (int *)0);
2. nrows = tigetnum("lines");
ncolumns = tigetnum("cols");
第三步 //如果时带参数的字符串功能字,设置参数
char *tparm(char *cap, long p1, long p2, ..., long p9);//最多可以设置9个参数
第四步 //将字符串发送到终端,使用以下函数可以正确处理终端完成一个操作所需要的延时
int putp(char *const str);//它将控制字符串发送到标准输出stdout OK | ERR
int tputs(char *const str, int affcnt, int (*putfunc)(int));//这是为了不能通过标准输出stdout访问终端的情况准备的
affcnt: 表明受这一变化影响的行数,一般为1
putfunc:用于输出字符串的函数,其参数和返回值类型必须与putchar函数相同
其实putp 相当于 tputs(string, 1, putchar);
例:将光标移动到指定位置
1. cursor = tigetstr("cup");
2. esc_sequence = tparm(cursor, 5, 30);//第5行,第30列
3. putp(esc_sequence):
新闻热点
疑难解答