【问题标题】:Ncurses background waiting for exitNcurses 后台等待退出
【发布时间】:2015-04-09 11:44:55
【问题描述】:

考虑以下代码。我希望程序在按下例如后结束。 F10 。我不想改变程序的行为,我想在后台做,等待按键然后结束。如何修改程序来实现这一点?

#include <ncurses.h>
#include <unistd.h>

int main () {
      int parent_x, parent_y;
      int score_size =10;
      int counter =0 ;
      initscr();
      noecho();
      curs_set(FALSE);
      getmaxyx(stdscr, parent_y, parent_x);
      WINDOW *field = newwin(parent_y - score_size, parent_x, 0, 0);
      WINDOW *score = newwin(score_size, parent_x, parent_y - score_size, 0);
      while(true) {
          mvwprintw(field, 0, counter, "Field");
          mvwprintw(score, 0, counter, "Score");
          wrefresh(field);
          wrefresh(score);
          sleep(5);
          wclear(score);
          wclear(field);
          counter++;
      }

      delwin(field);
      delwin(score);
      endwin();
}

【问题讨论】:

    标签: c++ c ncurses


    【解决方案1】:

    也许你可以在 while 循环内的某处读取输入

    keypad(field, TRUE);
    int loop = 1;
    while(loop) {
      ...
      int c = wgetch(field);
      switch(c) {
        case KEY_F(10):
          loop = 0;
          break;
        default:
          break;
      }
      ...
    }
    

    【讨论】:

      【解决方案2】:

      此链接:https://www.mkssoftware.com/docs/man3/curs_inopts.3.asp 讨论 cbreak() 和 halfdelay()(在其他 ncurses 命令中)

      在程序开始附近调用 cbreak()。 然后在寻找击键时(不会永远阻塞)使用 halfdelay()。

      在这两个函数之间,代码应该能够检查某个击键并立即做出响应,而不需要像 getch() 这样的程序块。

      【讨论】:

      • 其实问题是关于 ncurses 的。该来源实际上记录了(没有注意到事实)旧版本的 ncurses。
      【解决方案3】:

      结合两者(都不完整),要识别 F10 ,您必须调用keypad,而要进行单字符处理,您需要类似cbreak,甚至raw .这是一个完整的例子:

      #include <ncurses.h>
      #include <stdlib.h>
      
      int main (void) {
            int parent_x, parent_y;
            int score_size =10;
            int counter =0 ;
            bool loop = TRUE;
            WINDOW *field;
            WINDOW *score;
      
            initscr();
            cbreak();
            noecho();
            curs_set(FALSE);
            getmaxyx(stdscr, parent_y, parent_x);
            field = newwin(parent_y - score_size, parent_x, 0, 0);
            score = newwin(score_size, parent_x, parent_y - score_size, 0);
            keypad(field, TRUE);
            halfdelay(1);
            while(loop) {
                int c = wgetch(field);
                switch(c) {
                case KEY_F(10):
                    loop = FALSE;
                    continue;
                default:
                    break;
                }
                mvwprintw(field, 0, counter, "Field");
                mvwprintw(score, 0, counter, "Score");
                wrefresh(field);
                wrefresh(score);
                napms(5000);     /* don't use sleep(5) */
                wclear(score);
                wclear(field);
                counter++;
      
            }
      
            delwin(field);
            delwin(score);
            endwin();
            return EXIT_SUCCESS;
      }
      

      【讨论】:

        【解决方案4】:

        最简单的方法是将您的 sleep(5) 替换为对 select() 的调用,超时 5 秒,例如:

        #include <stdlib.h>
        #include <ncurses.h>
        #include <unistd.h>
        #include <sys/select.h>
        #include <sys/time.h>
        #include <sys/types.h>
        
        int main(void) {
            int parent_x, parent_y;
            int score_size = 10;
            int counter = 0;
        
            WINDOW * mainwin = initscr();
            noecho();
            crmode();
            keypad(mainwin, TRUE);
            wrefresh(mainwin);
        
            curs_set(FALSE);
            getmaxyx(stdscr, parent_y, parent_x);
            WINDOW *field = newwin(parent_y - score_size, parent_x, 0, 0);
            WINDOW *score = newwin(score_size, parent_x, parent_y - score_size, 0);
        
            while ( true ) {
                mvwprintw(field, 0, counter, "Field");
                mvwprintw(score, 0, counter, "Score");
                wrefresh(field);
                wrefresh(score);
        
                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(STDIN_FILENO, &fds);
        
                struct timeval tv;
                tv.tv_sec = 5;
                tv.tv_usec = 0;
        
                int status = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
                if ( status == -1 ) {
                    perror("error calling select()");
                    exit(EXIT_FAILURE);
                }
                else if ( status == 1 ) {
                    if ( wgetch(mainwin) == KEY_F(10) ) {
                        break;
                    }
                }
        
                wclear(score);
                wclear(field);
                counter++;
            }
        
            delwin(field);
            delwin(score);
            endwin();
        }
        

        这样做的缺点是,如果您按下 F10 以外的键,您的循环将立即重复,并且 5 秒的延迟将重新开始。解决此问题的最简单方法是使您的延迟取决于计时器,例如:

        #include <stdio.h>
        #include <stdlib.h>
        #include <signal.h>
        #include <errno.h>
        #include <ncurses.h>
        #include <unistd.h>
        #include <sys/select.h>
        #include <sys/time.h>
        #include <sys/types.h>
        
        
        /*  Signal handler, doesn't need to do anything  */
        
        void handler(int signum)
        {
            (void) signum;      /*  Do nothing  */
        }
        
        int main(void)
        {
            int parent_x, parent_y;
            int score_size = 10;
            int counter = 0;
        
            /*  Register signal handler for SIGALRM  */
        
            struct sigaction sa;
            sa.sa_handler = handler;
            sa.sa_flags = 0;
            sigemptyset(&sa.sa_mask);
            if ( sigaction(SIGALRM, &sa, NULL) == -1 ) {
                perror("error calling sigaction()");
                exit(EXIT_FAILURE);
            }
        
            /*  Create timer  */
        
            struct itimerval itv;
            itv.it_interval.tv_sec = 5;
            itv.it_interval.tv_usec = 0;
            itv.it_value.tv_sec = 5;
            itv.it_value.tv_usec = 0;
            if ( setitimer(ITIMER_REAL, &itv, NULL) != 0 ) {
                perror("seeor calling setitimer()");
                exit(EXIT_FAILURE);
            }
        
            /*  Initialize curses  */
        
            WINDOW * mainwin = initscr();
            noecho();
            crmode();
            keypad(mainwin, TRUE);
            wrefresh(mainwin);
            curs_set(FALSE);
        
            /*  Create windows  */
        
            getmaxyx(stdscr, parent_y, parent_x);
            WINDOW *field = newwin(parent_y - score_size, parent_x, 0, 0);
            WINDOW *score = newwin(score_size, parent_x, parent_y - score_size, 0);
        
            while ( true ) {
                mvwprintw(field, 0, counter, "Field");
                mvwprintw(score, 0, counter, "Score");
                wrefresh(field);
                wrefresh(score);
        
                /*  Wait for available input  */
        
                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(STDIN_FILENO, &fds);
                int status = select(STDIN_FILENO + 1, &fds, NULL, NULL, NULL);
        
                if ( status == -1 ) {
        
                    /*  select() returned an error...  */
        
                    if ( errno == EINTR ) {
        
                        /*  Interrupted by SIGALRM, so update counter  */
        
                        wclear(score);
                        wclear(field);
                        counter++;
                    }
                    else {
        
                        /*  Other error, so quit  */
        
                        delwin(field);
                        delwin(score);
                        endwin();
                        perror("error calling select()");
                        exit(EXIT_FAILURE);
                    }
                }
                else if ( status == 1 ) {
        
                    /*  Input ready, so get and check for F10  */
        
                    if ( wgetch(mainwin) == KEY_F(10) ) {
                        break;
                    }
                }
            }
        
            /*  Clean up and exit  */
        
            delwin(field);
            delwin(score);
            endwin();
        
            return 0;
        }
        

        这将完全符合您的要求。 SIGALRM 可能会在 select() 调用之间触发,从而导致跳过更新。你可以通过切换到pselect() 来解决这个问题,我将把它留给你作为练习。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-24
          • 2012-09-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多