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