Ncurses и realtime (реализовано на C, unix)

Я пытаюсь реализовать игру, используя ncurses в C. Мне нужно показать текущее время (время должно обновляться каждую секунду), и мой цикл 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
}

Моя проблема в том, что время обновляется только тогда, когда я нажимаю клавишу. Это способ обновить время без необходимости нажатия клавиши (и реализовать это одновременно)?


person Silent Control    schedule 29.12.2012    source источник
comment
Проблема в блокировке ввода-вывода на стандартный ввод. Вам понадобится способ использовать неблокирующий ввод-вывод. (Я не знаю, реализует ли (n)curses какой-либо способ использования неблокирующего ввода-вывода. Думаю, да)   -  person wildplasser    schedule 29.12.2012
comment
(Экспериментальная) функция wgetch_events() удовлетворяет ваши потребности. (Примечание: я никогда не использовал его, я просто посмотрел его в ncurses.h)   -  person wildplasser    schedule 29.12.2012


Ответы (3)


Есть несколько функций, которые вы могли бы использовать:

  1. без задержки
  2. тайм-аут

int nodelay(WINDOW *win, bool bf);

Установите bf true, чтобы сделать getch() неблокирующим

недействительный тайм-аут (целая задержка);

Задержка указывается в миллисекундах, поэтому, если вы установите значение 1000, время ожидания getch истечет через секунду.

В обоих случаях getch вернет ERR, если нет ввода.

person parkydr    schedule 02.01.2013

Решение здесь состоит в том, чтобы ЛИБО использовать неблокирующий ввод-вывод или использовать потоки. Однако использование потоков создаст новую проблему, заключающуюся в том, что только один поток может использовать проклятия в любой момент времени, поэтому вам нужно будет использовать блокировки или что-то подобное, чтобы другие потоки не могли использовать проклятия в этот момент. время. Конечно, одним из решений для этого является наличие одного потока, ответственного за обновление содержимого экрана, а другие потоки просто отправляют сообщения этому потоку с текстом «Я хочу вывести экран в точке X, Y».

person Mats Petersson    schedule 29.12.2012

Это очень уродливый хак, который поддерживает только одну функцию 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;
}
person wildplasser    schedule 29.12.2012
comment
Это полно дыр. Во-первых, вы не сбрасываете будильник после получения символа, поэтому будильник может сработать во время обработки чего-то, и вы сразу же вернетесь к my_getc со стеком и всем, что установлено обратно на этот код — что-то плохое обязательно произойдет. если вы это сделаете. Затем у вас есть гонка между возвращением из fgetc и срабатыванием будильника. Конечно, он может работать большую часть времени, но оставьте его включенным на некоторое время, пропустите через него несколько персонажей, и он гарантированно упадет. - person Mats Petersson; 30.12.2012
comment
Это правильно. Это было задумано только как демонстрация. Обработчики также должны быть установлены без гонки через sigaction(). Проблема Secondaru заключается в том, что curses/termio, вероятно, использует 0,5-секундную задержку для синхронизации escape-строк. (чтобы отличить простой ESC от начала реальной последовательности). NB: я обновил, чтобы добавить только сигнал тревоги (0); - person wildplasser; 30.12.2012
comment
Поскольку OP уже использует ncurses, в ncurses есть много способов выполнить неблокирующее чтение ключей (у него также есть функция timeout()). Или вы можете просто использовать select() или poll() на стандартном вводе с тайм-аутом. - person nos; 30.12.2012