zoukankan      html  css  js  c++  java
  • Non-blocking user input in loop without ncurses.

    The title sounds a bit awkward, let my briefly explain what is it all about.

    In my program, I want to wait for user input, but at the same time, I want my other operations keep continue processing. That I define it as non-blocking user input. I want my program to wait for user input in the loop, if receives user input process it and continue wait for next user input, else continue processing other circular operations.

    Usually, people will think about using ncurses library. By using ncurses, you can easily perform non-blocking user input using timeout(0) and getch(). Refers to the ncurses matrix for references.

    What if I don’t want to uses ncurses? Its there any alternatives? Sometimes I just wanna add a function which need non-blocking in a large program, I don’t want to use ncurses because it makes my print line acts differently, or some other reason.

    I have figure out a way, thanks to programmers at ##c@irc.freenode.net. It might look messy and complected, but it works the way I wanted. I uses functions of termios and select, lets look at the functions one by one.

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    int kbhit()
    {
        struct timeval tv;
        fd_set fds;
        tv.tv_sec = 0;
        tv.tv_usec = 0;
        FD_ZERO(&fds);
        FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
        select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
        return FD_ISSET(STDIN_FILENO, &fds);
    }

    This function perform non-blocking checking on the standard input (stdin) without timeout 0, tv.tv_sec and tv.tv_usec both set to zero. select usually uses in the case where there have multiple I/O need to process, or check at the same time. But in this case, I only interested in standard input, therefore only one FD_SET(STDIN_FILENO, &fds) is trigger. For select parameters details, please check out the manual. Seems we are only interested in input, so we place out fd set at second parameter of select(), the 3rd is for output and 4th is for exception.

    Important part, after select if user input is trigger, FD_ISSET will return non zero value, else return 0. So, we now can use it like this

    1
    2
    3
    4
    5
    while(!kbhit())
    {
          //do certain operation..
    }
    //user hits enter.

    Due to the canonical mode of your terminal, you need to hit enter to confirm your user input. Canonical mode means it always wait for enter to confirms the user input. If that is not your case, bellow is another function to cater that.

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    void nonblock(int state)
    {
        struct termios ttystate;
     
        //get the terminal state
        tcgetattr(STDIN_FILENO, &ttystate);
     
        if (state==NB_ENABLE)
        {
            //turn off canonical mode
            ttystate.c_lflag &= ~ICANON;
            //minimum of number input read.
            ttystate.c_cc[VMIN] = 1;
        }
        else if (state==NB_DISABLE)
        {
            //turn on canonical mode
            ttystate.c_lflag |= ICANON;
        }
        //set the terminal attributes.
        tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
     
    }

    The function name might be misleading, what the function actually does is turn off the canonical mode for stdin. First, get the states of stdin of term. Next turn off the canonical by set 0 to the bits. The line of ttystate.c_cc[VMIN] is set the minimum number of user input to accept. If you set it as 2, the select will wait until 2 character is given, then it will capture as input. At last set the term state. The function actually does allow you to turn on and off the canonical mode.

    Okay, Let see how it apply to work

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    int main()
    {
        char c;
        int i=0;
     
        nonblock(NB_ENABLE);
        while(!i)
        {
            usleep(1);
            i=kbhit();
            if (i!=0)
            {
                c=fgetc(stdin);
                if (c=='q')
                    i=1;
                else
                    i=0;
            }
     
            fprintf(stderr,"%d ",i);
        }
        printf(" you hit %c. ",c);
        nonblock(NB_DISABLE);
     
        return 0;
    }

    Press ‘q’ will lead the program to quit, else you will see the print of ’0′ overwhelming the screen. Observed that I am using usleep(1) inside the loop, without putting usleep, the programs is more responsive, but it uses high CPU resources. On the other hand, putting usleep(1) reduces the CPU resources and also decreases responsiveness of the program. Depend on your needs, you may choose to put it in or not.

    I have make the comparison between this application and a simple ncurses program, this program seems to use lesser memory. Couldn’t measure for CPU resources, as both ps and top shows 0.0 %.

    Curses sample shows as bellow:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include<stdio.h>
    #include<curses.h>
    #include<unistd.h>
     
    int main ()
    {
        int i=0;
     
        initscr();     //in ncurses
        timeout(0);
        while(!i)
        {
            usleep(1);
            i=getch();
            printw("%d ",i);
            if(i>0)
                i=1;
            else
                i=0;
        }
        endwin();
        printf(" hitkb end ");
        return 0;
    }

    Download the source code:
    Non-blocking kbhit sample source code.

    ————————————————————————————————————————————————————————————————————————————— 无他,唯手熟尔。。。
  • 相关阅读:
    WindowsDocker初始化之Hyper-V
    vue cli安装失败,nodejs缺少模块,npm安装报错-之万能重装法则
    企业架构演进
    原生JS实现后端文件流导出Excel(附Node后端代码)
    Git和TortoiseGit安装配置
    SQL Server 单用户多用户模式切换
    Microsoft Visual Studio 2019一些个人初始设置
    腾讯云CentOS 7.6 64位之docker的镜像和容器练习
    腾讯云CentOS 7.6 64位安装docker
    常见的javascript跨站
  • 原文地址:https://www.cnblogs.com/ZXNblog/p/4029926.html
Copyright © 2011-2022 走看看