Linux识别按键
top中的源代码,慢慢理解。。。
不需要按ENTER键,按下即可识别
代码片段
部分摘自top源代码,部分来源于网络博客
#include <sys/time.h>
#include <stdio.h>
#include <termios.h>
/* The original and new terminal attributes */
static struct termios Savedtty,
Rawtty;
// 设置终端相关属性
static void initKeyboard()
{
tcgetattr(0,&Savedtty);
Rawtty = Savedtty;
Rawtty.c_lflag &= ~ICANON; // 设置不以规范模式工作,读请求直接从队列读取字符,至少接到MIN字节或者两个字节之间超时值TIME到期时,read才返回
Rawtty.c_lflag &= ~ECHO; // 关闭输入字符回显到终端设备
// Rawtty.c_lflag &= ~ISIG; // 判断输入字符是否要产生终端信号的特殊字符
Rawtty.c_cc[VMIN] = 1; // 至少接到MIN字节
Rawtty.c_cc[VTIME] = 0; // 两个字节之间超时值TIME
tcsetattr(0, TCSANOW, &Rawtty);
}
// 恢复终端属性
static void closeKeyboard()
{
tcsetattr(0, TCSANOW, &Savedtty);
}
// 读取字符
static int chin (int ech, char *buf, unsigned cnt)
{
int rc;
fflush(stdout);
if (!ech)
rc = read(STDIN_FILENO, buf, cnt);
else {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
rc = read(STDIN_FILENO, buf, cnt);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
}
// may be the beginning of a lengthy escape sequence
tcflush(STDIN_FILENO, TCIFLUSH);
return rc; // note: we do NOT produce a vaid 'string'
}
// 判断读取字符的有效性
static char kbhit(){
long file_flags;
int rc;
char c;
fd_set fs;
FD_ZERO(&fs);
FD_SET(STDIN_FILENO, &fs);
file_flags = fcntl(STDIN_FILENO, F_GETFL);
if(file_flags==-1) file_flags=0;
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
// check 1st, in case tv zeroed (by sig handler) before it got set
rc = chin(0, &c, 1);
if (rc <= 0) {
// EOF is pretty much a "can't happen" except for a kernel bug.
// We should quickly die via SIGHUP, and thus not spin here.
// if (rc == 0) end_pgm(0); /* EOF from terminal */
fcntl(STDIN_FILENO, F_SETFL, file_flags);
select(1, &fs, NULL, NULL, NULL);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
}
if (chin(0, &c, 1) > 0) {
fcntl(STDIN_FILENO, F_SETFL, file_flags);
return c;
} else {
fcntl(STDIN_FILENO, F_SETFL, file_flags);
}
kbhit();
}
## use
initKeyboard();
while(true){
char input = kbhit();
if( input == 'q'){
// do something
break;
}
}
closeKeyboard();
相关知识
终端相关
#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]; // 控制字符
};
// 获取终端的属性
tcgetattr(int fd, struct termios* tty_struct);
// 设置终端属性
tcsetattr(int fd, int opt, const struct termios* tty_struct);
tcsetattr的opt参数指定新设置的终端属性什么时候起作用
- TCSANOW:更改立即发生
- TCSADRAIN:发送了所有的输出后更改才发生
- TCSAFLUSH:发送了所有的输出后更改才发生。更进一步,更改发生时未读入的所有输入都会被丢弃
标志详细见APUE
select函数
允许进程指示内核等待多个事件中的任何一个发生,并且只有一个或者多个事件或者超时之后才唤醒它
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
// return:返回就绪的描述符的数目,超时返回0,出错返回-1
- 若timeout为空指针,则永远等下去
- 若timeout中秒数和微妙数设置为0,检查描述符之后立即返回
- 若timeout中秒数和微妙数设置不为0,在有描述符就绪时返回,但是不超过timeout指定的时间
fcntl函数
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
//fcntl()针对(文件)描述符提供控制.参数fd是被参数cmd操作(如下面的描述)的描述符。针对cmd的值,fcntl能够接受第三个参数(arg)
fcntl函数功能:
- 复制一个现有的描述符(cmd=F_DUPFD)
- 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)
- 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL)
- .获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
- 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)
参考文献
【1】top相关源码