Ncurses 后台等待退出 [英] Ncurses background waiting for exit
问题描述
考虑以下代码.我希望程序在按下后结束.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屋!