Linux识别按键

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相关源码

原文地址:https://www.cnblogs.com/delta1037/p/13500490.html