【问题标题】:Is there a way to keep the program running while waiting for input?有没有办法让程序在等待输入时保持运行?
【发布时间】:2020-03-17 08:55:12
【问题描述】:

我们有一个任务是用 C 创建一个游戏。我们不能使用任何外部头文件,它应该在控制台上运行。

我认为制作动画会很酷。我们已经有了一个行走的角色和所有的东西(打印矩阵、更新、清屏、重印……),但如果有诸如流水之类的东西就好了。为此,我需要一种在等待输入时保持程序运行的方法。按照目前的情况,动画会在提示用户输入时停止(并且会不断地向他询问方向),所以它不起作用。

我想知道是否有办法在输入提示仍在进行时保持动画运行,也许输入功能会超时并重新提示每一帧,直到用户开始输入。我们熟悉 C,但不熟悉任何可用于解决此问题的晦涩函数和标头。

如何在等待输入时保持程序运行?

【问题讨论】:

  • 您可以使用select() 来检查是否有用户输入,如果没有则执行其他操作
  • 所有这些都依赖于平台,不能用标准 C 来完成。但是你的帖子在 what 你实际上可以使用的细节方面非常模糊。
  • 多线程通常是实现此类事情的最简单方法。
  • @FelixG C11 线程。
  • @JL2210 好点,没有考虑这些,因为我仍然主要使用 C99

标签: c linux animation input console


【解决方案1】:

最简单的方法就是将标准输入更改为非阻塞。这是通过使用fcntl() 函数更改标准输入上的F_SETFL 选项来完成的(在Linux 和其他系统上)。

这将使fgetc()(或任何其他读取)不再阻止立即返回。如果没有任何东西要读取(没有输入),它将返回一个错误。如果有东西,它会返回那个字符。

这是将标准输入更改为非阻塞所需的调用。

fcntl(0, F_SETFL, FNDELAY);

第一个参数是文件句柄。我们传入 0,即标准输入。下一个参数是我们要更改的选项(在本例中为 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);
}

这很棒。它不再阻塞,但仍会回显正在输入的字符,您需要按 Enter 键才能从 fgetc() 取回任何输入。

如果我们想让它更像游戏,我们需要告诉终端停止干扰我们的输入。我们需要将终端切换到 RAW 模式。

以下示例将终端转为 RAW 模式并将标准输入更改为非阻塞。

#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() 和/或信号处理来帮助解决此问题。

【讨论】:

    【解决方案2】:

    您可以使用select 来监视一个文件或一组文件描述符,直到它们有可用的输入。根据您程序的结构,您还可以使用异步输入,当 I/O 发出信号为给定文件描述符准备就绪时调用回调。

    下面粗略的 sn-p 显示了必要的方法,以允许在目标文件描述中输入可用时调用回调 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-04
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 2013-10-24
      • 2020-01-07
      • 2012-05-30
      • 1970-01-01
      相关资源
      最近更新 更多