【问题标题】:Ncurses and realtime (implemented in C, unix)Ncurses 和实时(在 C、unix 中实现)
【发布时间】:2012-12-14 12:12:12
【问题描述】:

我正在尝试在 C 中使用 ncurses 实现游戏。我必须显示当前时间(时间必须每秒更新),而我的 while 循环看起来像这样

while(1)
{
    clk = time(NULL);
    cur_time = localtime(&clk);
    mvprintw(0,1,"%d %d %d",cur_time->tm_hour,cur_time->tm_min,cur_time->tm_sec);
    int key = getch()
    //other stuff
}

我的问题是只有当我按下一个键时时间才会刷新。这是一种无需按键即可刷新时间(并同时实现此功能)的方法吗?

【问题讨论】:

  • 问题出在标准输入上的阻塞 I/O。您需要一种使用非阻塞 I/O 的方法。 (我不知道 (n)curses 是否实现了某种使用非阻塞 I/O 的方式。我猜是的)
  • (实验性)函数 wgetch_events() 似乎可以满足您的需求。 (注:我没用过,只是在ncurses.h里查的)

标签: c ncurses time.h


【解决方案1】:

您可以使用几个函数:

  1. 节点层
  2. 超时

int nodelay(WINDOW *win, bool bf);

将 bf 设置为 true 以使 getch() 非阻塞

无效超时(int延迟);

延迟以毫秒为单位,因此如果将其设置为 1000,则 getch 将在一秒后超时。

在这两种情况下,如果没有输入,getch 将返回 ERR。

【讨论】:

    【解决方案2】:

    这里的解决方案是使用非阻塞 IO 或使用线程。然而,使用线程会给你带来一个新问题,那就是在任何给定时间只有一个线程可以使用curses,所以你需要使用锁或类似的东西来防止其他线程在那个时候使用curses时间。当然,一种解决方案是让一个线程负责更新屏幕内容,而其他线程只需向该线程发送“我想在 X,Y 时放在屏幕上”的消息

    【讨论】:

      【解决方案3】:

      这是一个非常丑陋的 hack,它只支持一个 stdio 函数:fgetc()。其他可以添加,顺便说一句。它通过设置一个计时器来工作,如果在读取单个字符之前警报响起,则返回 -2 值(记住:-1 表示 EOF)

      不会与任何其他 curses 的 wgetXXX() 函数一起工作,这些函数可能会直接调用 fgetc() (等)。 YMMV。

      但是,在一般情况下,我认为您应该调查 wgetch_events()。

      #include <stdio.h>
      #include <string.h>
      #include <unistd.h>
      #include <signal.h>
      #include <setjmp.h>
      
      sigjmp_buf the_jump;
      
      int my_fgetc(FILE *fp, unsigned timeout);
      void sig_handler(int signr);
      
      void sig_handler(int signr) {
              switch (signr) {
              case SIGALRM:
                    siglongjmp(the_jump,1);
                    break;
              default:
                    break;
              }
      }
      
      int my_fgetc(FILE *fp, unsigned timeout)
      {
      alarm( timeout);
      
      switch (sigsetjmp(the_jump, -1)) {
      case 0:
              alarm(0);
              return fgetc (fp);
      case 1:
              return -2;
      default:
              return -3;
              }
      }
      
      int main()
      {
      int rc;
      signal(SIGALRM, sig_handler);
      
      rc = setvbuf(stdin, NULL, _IONBF, 0);
      printf("setvbuf(_IONBF) = %d\n", rc);
      while (1) {
              rc = my_fgetc(stdin, 1);
              printf("my_fgetc(NULL) = %d\n", rc);
              }
      
      return 0;
      }
      

      【讨论】:

      • 这全是漏洞。首先,您在收到字符后不会重置警报,因此警报可能会在处理某些内容时响起,然后您直接跳回 my_getc,堆栈和所有内容都设置回该代码 - 肯定会发生坏事如果你这样做。然后你会在从 fgetc 返回和警报响起之间进行一场竞赛。当然,它可能在大多数情况下都可以工作,但让它运行一段时间,让一些角色通过它,它肯定会摔倒。
      • 没错。它只是为了演示。处理程序也应该通过 sigaction() 进行无竞争安装。第二个问题是 curses / termio 可能使用 0.5 秒的延迟来控制转义字符串的时间。 (将裸 ESC 与实际序列的开头区分开来)。注意:我更新为仅添加警报(0);
      • 由于 OP 已经使用 ncurses,ncurses 中有很多方法可以对键进行非阻塞读取(它还有一个 timeout() 函数)。或者你可以只在标准输入上使用 select() 或 poll(),超时。
      猜你喜欢
      • 2011-04-25
      • 2012-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-29
      • 2012-08-15
      相关资源
      最近更新 更多