控制终端
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);
}