有没有一种方法可以在等待输入时让程序持续运行?

3
我们有一个任务,需要用C语言创建游戏。我们不能使用任何外部头文件,并且它应该在控制台上运行。
我认为加入动画效果会很酷。我们已经有了一个走路的角色和一切(打印矩阵、更新、清除屏幕、重新打印...),但是加入诸如流水之类的东西会更好。要做到这一点,我需要一种方法来在等待输入时保持程序运行。目前,由于用户被提示输入(并且他将不断被要求指示方向),因此动画将停止,所以它不起作用。
我想知道是否有一种方法可以在输入提示仍在进行时保持动画运行,也许设置输入函数的超时时间并在每一帧重新提示,直到用户开始输入。我们熟悉C语言,但不熟悉任何可以用来解决这个问题的模糊函数和头文件。
如何在等待输入时保持程序运行?

3
你可以使用 select() 来检查是否有用户输入,如果没有则执行其他操作。 - Ingo Leonhardt
3
这一切都依赖于平台,无法使用标准的C语言完成。然而,您的帖子在可以使用的具体细节方面非常模糊。 - Antti Haapala -- Слава Україні
1
多线程通常是实现这种功能最简单的方法。 - Barmar
1
@FelixG C11 线程。 - S.S. Anne
1
@FelixG 但是正如之前指出的那样,这不能使用标准C完成。因此,如果他被允许使用POSIX库,则线程可能比select/epoll更容易。 - Barmar
显示剩余8条评论
2个回答

3
您可以使用 select 监控文件或一组文件描述符,直到它们有输入可用。根据您程序的结构,您还可以使用异步输入,其中在给定文件描述符的 I/O 准备就绪时调用回调函数。
下面的粗略代码片段显示了必要的方法,以便在目标文件描述符上有输入可用时调用回调 action_handler ,该文件描述符发出 SIGIO。 这允许在到达输入时处理它(如果到达)。 使用套接字(假设为UDP),您将具有类似于以下内容的内容。
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
...
create and bind socket
...

/** Set sigaction to use sigaction callback and not handler */
act.sa_flags = SA_SIGINFO; // Enables sigaction instead of handler
act.sa_sigaction = action_handler; // Callback function

/** Set mask to ignore all signals except SIGIO during execution of handler */
sigfillset(&act.sa_mask); // Mask all
sigdelset(&act.sa_mask, SIGIO); // Clear SIGIO from mask

/** Set callback for SIGIO */
sigaction(SIGIO, &act, NULL)

/** Set socket io signals to async and make sure SIGIO is sent to current
* process when a TCP connection is made on the socket
* */
file_status = fcntl(socket_fd, F_GETFL); // Get current status
file_status |= O_ASYNC | O_NONBLOCK;
fcntl(socket_fd, F_SETFL, file_status); // Set modified status flags
fcntl(socket_fd, F_SETSIG, SIGIO); // Produce a SIGIO signal when i/o is possible 
fcntl(socket_fd, F_SETOWN, getpid()); // Make sure SIGIO signal is sent to current process

2
最简单的方法是将stdin更改为非阻塞模式。在Linux和其他系统上,可以使用fcntl()函数更改stdin上的F_SETFL选项来实现此操作。
这样,fgetc()(或任何其他读取)将不再阻塞并立即返回。如果没有任何可读内容(没有输入),它将返回错误。如果有内容,它将返回该字符。
以下是更改stdin为非阻塞模式所需的调用:
fcntl(0,F_SETFL,FNDELAY);
第一个参数是文件句柄。我们传入0,这是stdin。下一个参数是要更改的选项(在本例中为F_SETFL),第三个参数是要更改为的内容(FNDELAY是非阻塞模式)。
以下是一个简单的示例程序:
#include <stdio.h>
#include <fcntl.h>

void SetupIO(void);
void ShutDownIO(void);

int main(void)
{
    long count;
    char c;

    SetupIO();

    count=0;
    for(;;)
    {
        printf("Counting %d\r",count);
        count++;

        c=fgetc(stdin);
        if(c=='q')
            break;
    }

    ShutDownIO();

    return 0;
}

void SetupIO(void)
{
    fcntl(0, F_SETFL, FNDELAY);
}

void ShutDownIO(void)
{
    fcntl(0, F_SETFL, 0);
}

这很棒。它不再阻塞,但仍会回显所键入的字符,并且在从fgetc()获取任何输入之前,需要按回车键。
如果我们想让它更像游戏,我们需要告诉终端停止干扰我们的输入。我们需要将终端切换到RAW模式。
以下示例将终端切换为RAW模式并将stdin更改为非阻塞模式。
#include <termios.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

void SetupIO(void);
void ShutDownIO(void);

struct termios orig_termios;

int main(void)
{
    long count;
    char c;

    SetupIO();

    count=0;
    for(;;)
    {
        printf("Counting %d\r",count);
        count++;

        c=fgetc(stdin);
        if(c=='q')
            break;
    }

    ShutDownIO();

    return 0;
}

void SetupIO(void)
{
    struct termios new_termios;

    /* take two copies - one for now, one for later */
    tcgetattr(0, &orig_termios);
    memcpy(&new_termios, &orig_termios, sizeof(new_termios));

    /* register cleanup handler, and set the new terminal mode */
    atexit(ShutDownIO);

    cfmakeraw(&new_termios);

    new_termios.c_iflag|=INLCR; // CR=NL
    new_termios.c_lflag|=ISIG;  // We still want Ctrl-C

    tcsetattr(0, TCSANOW, &new_termios);

    /* Non-blocking */
    fcntl(0, F_SETFL, FNDELAY);
}

void ShutDownIO(void)
{
    tcsetattr(0, TCSANOW, &orig_termios);
    fcntl(0, F_SETFL, 0);
}

这样做的缺点是,如果您使用Ctrl-C或者程序崩溃,它会使您的终端处于混乱状态,很难恢复。您可以通过添加atexit()和/或信号处理来帮助解决这个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接