等待出口的Ncurses背景

时间:2015-04-09 11:44:55

标签: c++ c ncurses

考虑以下代码。我希望程序在按下例如后结束。 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();
}

4 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

组合两者(既不完整),要识别 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;
}

答案 3 :(得分:0)

最简单的方法是将您的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()来解决这个问题,我将把它作为练习留给你。