I've got a cross-platform (windows and unix+xcb) terminal+graphics_window application and it mostly works ok, until you wait too long at the input prompt, where under heavy load the image may disappear. :(
I've got a mainloop (REPL) for the interpreter (postscript interpreter) which calls an event handler function each time around its loop. The event handler performs one iteration of what would normally be the window's message/event loop. But input is processed with normal C i/o and so the event handler never gets called when blocked in fgetc()
.
The graphics window is output-only. It has no buttons and only needs to respond to events like Raise, Map, Expose, etc.
How can I arrange to have the event handler called during input reading loops deeper in the call stack? This needs to be possible to implement with both POSIX and win32 APIs.
The options appear to be
- Non-blocking i/o
relatively simple in unix. looks like a pain in windows - Polling
- Input thread
pthreads? - Window thread
pthreads?
Are any of these likely to be less painful than the others?
If I could just stay on unix, then this would seem to do the whole trick:
#include <errno.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
void idleproc () { /* simulates calling the event handler
(ie. one slice of the window loop) */
//printf("idle\n");
putchar('.');
}
int idlefgetc (FILE *stream) {
int ret;
do {
ret = fgetc(stream);
idleproc();
} while(ret == EOF &&
(errno == EAGAIN || errno == EINTR));
return ret;
}
int setraw (FILE *stream) {
struct termios tbuf;
if (tcgetattr(fileno(stream), &tbuf) == -1)
return -1;
tbuf.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
tbuf.c_oflag &= ~OPOST;
tbuf.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tbuf.c_cflag &= ~(CSIZE | PARENB);
tbuf.c_cflag |= CS8;
if (tcsetattr(fileno(stream), TCSANOW, &tbuf) == -1)
return -1;
return 0;
}
int setnonblocking (FILE *stream) {
int flags;
if (setraw(stream) != 0)
return -1;
if (!((flags = fcntl(fileno(stream), F_GETFL)) & O_NONBLOCK)) {
flags |= O_NONBLOCK;
fcntl(fileno(stream), F_SETFL, flags);
}
return 0;
}
int main (int argc, char **argv) {
if (setnonblocking(stdin)) {
perror(argv[0]);
return 0;
}
printf("'%d'\n", idlefgetc(stdin));
system("stty sane");
return 0;
}
Under Windows you need to use the Console API. You can perform asynchronous, non-blocking, reading of characters with ReadFileEx. Another possibility is ReadConsoleInput, and poll continously for input, without blocking. With SetConsole you can decide, what events to capture.
For more details on asynchronous I/O under Windows, see here.