Ncurses and realtime (implemented in C, unix)
Asked Answered
V

3

6

I am trying to implement a game using ncurses in C. I have to show the current time (the time must update each second) and my while loop looks like this

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
}

My problem is that the time will refresh only when I press a key. Is it a way to make the time refresh without the need of pressing a key (and to implement this in the same while)?

Veron answered 29/12, 2012 at 15:2 Comment(2)
The problem is in the blocking I/O on stdin. You'll need a way to use nonbocking I/O. (I don't know if (n)curses implements some way of using nonblocking I/O. I guess it does)Silvertongued
The (experimental) function wgetch_events() appears to fill your needs. (Note: I never used it, I just looked it up in ncurses.h)Silvertongued
P
5

There are a couple of functions you could use:

  1. nodelay
  2. timeout

int nodelay(WINDOW *win, bool bf);

Set bf true to make getch() non-blocking

void timeout(int delay);

Delay is in milliseconds, so if you set it to 1000, the getch will timeout after a second.

In both cases getch will return ERR if there is no input.

Philippe answered 2/1, 2013 at 22:1 Comment(0)
C
1

The solution here is EITHER to use non-blocking IO or to use threads. However, using threads will give you a new problem, which is that only one thread can use curses at any given time, so you will need to use locks or some such to prevent the other thread(s) from using curses at that point in time. Of course, one solution for that is to have one thread responsible for updating the screen content, and other threads simply send messages to that thread with "I want to put on the screen at X, Y"

Chronicles answered 29/12, 2012 at 15:32 Comment(0)
S
0

This is a very ugly hack which supports just a single stdio function: fgetc(). Others could be added, btw. It works by setting a timer, an if the alarm goes off before a single character is read, a -2 value is returned instead (remember : -1 means EOF)

It won't work with any of the other curses's wgetXXX(), functions, which may call fgetc() (etc) directly. YMMV.

But, in the general case, I think you should investigate 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;
}
Silvertongued answered 29/12, 2012 at 18:59 Comment(3)
This is full of holes. First of all, you don't reset the alarm after receiving a character, so the alarm may go off whilst processing something, and you jump straight back to my_getc, with the stack and everything set back to that code - bad stuff will definitely happen if you do that. Then you have a race between returning from fgetc and the alarm going off. Sure, it may work most of the time, but leave it running for a while, run some characters through it, and it will fall over, guaranteed.Chronicles
That is correct. It was only intended as a demonstration. The handlers should also be installed race free, via sigaction(). A secondaru problem is with curses / termio probably using a .5 second delay for the timing of escape-strings. (to distinguish a bare ESC from the start of an actual sequence). NB: I updated to only add the alarm(0);Silvertongued
Since the OP already uses ncurses, there's many ways in ncurses to do a non-blocking read of the keys (It also has a timeout() function). Or you could just use select() or poll() on stdin, with at timeout.Tidewater

© 2022 - 2024 — McMap. All rights reserved.