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

  • 相关阅读:
    在C#中使用官方驱动操作MongoDB
    【C#设计模式-抽象工厂模式】
    【MongoDB-MongoVUE图像管理工具】
    【MongoDB-query查询条件】
    【MongoDB学习-安装流程】
    【MongoDB学习-在.NET中的简单操作】
    【MongoDB】2.可视化工具的安装和使用
    越狱Season 1-Episode 12:Odd Man Out
    越狱Season 1-Episode 11: And Then There Were 7-M
    越狱Season 1-Episode 10: Sleight of Hand
  • 原文地址:https://www.cnblogs.com/delta1037/p/13500490.html
Copyright © 2020-2023  润新知