Ncurses 后台等待退出 [英] Ncurses background waiting for exit

查看:94
本文介绍了Ncurses 后台等待退出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Consider following code. I would like the program to end after pressing eg. F10 . I don't want to change the behavior of the program, I would like to do in the background, waiting for the key press and then end. How to modify the program to achieve this?

#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();
}

推荐答案

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

The easiest way is to replace your sleep(5) with a call to select() with a 5 second timeout, for example:

#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 秒延迟将重新开始.解决此问题的最简单方法是根据计时器设置延迟,例如:

The drawback with this is that if you press a key other than F10, your loop will repeat immediately and the 5 second delay will restart. The easiest way to fix this is to make your delay contingent on a timer, for instance:

#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() 来解决这个问题,我将把它留给你作为练习.

which will do exactly what you're looking for. There's a small possibility SIGALRM might fire in between select() calls, causing an update to be skipped. You can take care of this by switching to pselect(), which I'll leave as an exercise for you.

这篇关于Ncurses 后台等待退出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆