Non-Blocking i/o in c? (windows) - c

I'm trying to get a non-blocking I/O on a Windows terminal application (windows only, sorry!).
What if I want to have a short input time in wich the user can press a button, but if he doesn't the input stops and the program continues?
For example:
A timer that counts from 1 to whatever that stops when the user presses a certain key:
I should have a while loop, but if I do a getch or a getchar function it will stop the program, right?
I know I could use kbhit(); , but for the "program" I'm trying to make I need to know the input, not just IF THERE IS input!
Are there any simple functions that would allow me to read like the last key in the keyboard buffer?

From the documentation for _kbhit():
The _kbhit function checks the console for a recent keystroke. If the function returns a nonzero value, a keystroke is waiting in the buffer. The program can then call _getch or _getche to get the keystroke.
So, in your loop:
while (true) {
// ...
if (_kbhit()) {
char c = _getch();
// act on character c in whatever way you want
}
}
So, you can still use _getch(), but limit its use to only after _kbhit() says there is something waiting. That way it won't block.

Here is how to make a non blocking call to stdin in windows by using the right API :
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
void ErrorExit(LPSTR);
void KeyEventProc(KEY_EVENT_RECORD ker);
// Global variables are here for example, avoid that.
DWORD fdwSaveOldMode;
HANDLE hStdin;
void printToCoordinates(int x, int y, char* text)
{
printf("\033[%d;%dH%s", y, x, text);
}
int main()
{
printf("\033[H\033[J");
int i = 0;
char* s = "*";
DWORD fdwMode, cNumRead;
INPUT_RECORD irInBuf[128];
DWORD bufferSize = 0;
hStdin = GetStdHandle(STD_INPUT_HANDLE);
// Just a check to ensure everything is fine at this state
if (hStdin==INVALID_HANDLE_VALUE){
printf("Invalid handle value.\n");
exit(EXIT_FAILURE);
}
// Just a check to ensure everything is fine at this state
if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
ErrorExit("GetConsoleMode");
// Those constants are documented on Microsoft doc
// ENABLE_PROCESSED_INPUT allows you to use CTRL+C
// (so it's not catched by ReadConsoleInput here)
fdwMode = ENABLE_WINDOW_INPUT | ENABLE_PROCESSED_INPUT;
if (! SetConsoleMode(hStdin, fdwMode) )
ErrorExit("SetConsoleMode");
while (i < 60) {
// The goal of this program is to print a line of stars
printToCoordinates(i, 5, s);
i++;
GetNumberOfConsoleInputEvents(hStdin, &bufferSize);
// ReadConsoleInput block if the buffer is empty
if (bufferSize > 0) {
if (! ReadConsoleInput(
hStdin, // input buffer handle
irInBuf, // buffer to read into
128, // size of read buffer
&cNumRead) ) // number of records read
ErrorExit("ReadConsoleInput");
// This code is not rock solid, you should iterate over
// irInBuf to get what you want, the last event may not contain what you expect
// Once again you'll find an event constant list on Microsoft documentation
if (irInBuf[cNumRead-1].EventType == KEY_EVENT) {
KeyEventProc(irInBuf[cNumRead-1].Event.KeyEvent);
Sleep(2000);
}
}
Sleep(100);
}
// Setting the console back to normal
SetConsoleMode(hStdin, fdwSaveOldMode);
CloseHandle(hStdin);
printf("\nFIN\n");
return 0;
}
void ErrorExit (LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
// Restore input mode on exit.
SetConsoleMode(hStdin, fdwSaveOldMode);
ExitProcess(0);
}
void KeyEventProc(KEY_EVENT_RECORD ker)
{
printf("Key event: \"%c\" ", ker.uChar.AsciiChar);
if(ker.bKeyDown)
printf("key pressed\n");
else printf("key released\n");
}
Please notice this work in brand new Terminal application but not in CMD (due to termcaps used in the code) but it will compile and you can run it anyway.

Related

How to remove characters(^\), ctrl + \(SIGQUIT), from STDIN in C [duplicate]

Good day,
I'm writing my own shell in C for my school which has to resemble bash as closely as possible.
I have to handle signals such as Ctrl-\ and Ctrl-C as bash does; for this reason I'm allowed to use signal function. It works fine, but the thing is whenever a Ctrl-C signal is caught (starting from the second catch), a ^C is printed.
On the net, I've found a workaround suggesting printing "\b \b\b \b\nminishell$ " whenever a Ctrl-C is caught, which will devour the two symbols. The thing is, since at the very first time ^C is not printed, the print devours two symbols of my prompting, making it just minishell instead of minishell$ , with the cursor incorrectly displayed.
Now I've come up with another workaround for this workaround which is to declare a static boolean to not print the baskspaces at the very first call. This doesn't help in case of Ctrl-\ though; Ctrl-\ proceeds to move my cursor to right when I attempt to write the two whitespaces that must replace the ^\.
I don't like these workarounds and would like to know whether there is a way to instruct the terminal not to output this stuff? I'm allowed to use tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs, tcsetattr, tcgetattr, have read their man pages but nothing seems to be helpful.
When you type a key on a terminal, two things happen
the character is echoed (displayed) on this terminal
the character is sent (over the line) to the attached program
Both these actions can be controlled via termios/tcsetattr(): a different character(s) can be sent or echoed, some can be suppressed, etc. (some/most of these actions take place in the terminal-driver , but this is not relevant here)
Demonstration: using tcsetattr() to control the echoing of the terminal:
#include <stdio.h>
#include <stdlib.h>
#define _SVID_SOURCE 1
#include <termios.h>
#include <unistd.h>
#include <signal.h>
struct termios termios_save;
void reset_the_terminal(void)
{
tcsetattr(0, 0, &termios_save );
}
sig_atomic_t the_flag = 0;
void handle_the_stuff(int num)
{
char buff[4];
buff[0] = '[';
buff[2] = '0' + num%10;
num /= 10;
buff[1] = '0' + num%10;
buff[3] = ']';
write(0, buff, sizeof buff);
the_flag = 1;
}
int main (void)
{
int rc;
int ch;
struct termios termios_new;
rc = tcgetattr(0, &termios_save );
if (rc) {perror("tcgetattr"); exit(1); }
rc = atexit(reset_the_terminal);
if (rc) {perror("atexit"); exit(1); }
termios_new = termios_save;
termios_new.c_lflag &= ~ECHOCTL;
rc = tcsetattr(0, 0, &termios_new );
if (rc) {perror("tcsetattr"); exit(1); }
signal(SIGINT, handle_the_stuff);
printf("(pseudoshell)Start typing:\n" );
while(1) {
ch = getc(stdin);
if (the_flag) {
printf("Saw the signal, last character was %02x\n", (unsigned) ch);
break;
}
}
exit (0);
}
The way to set the console such a SW may intercept all typed chars is to set the terminal in RAW MODE. The problems this way may present are that all keys that aren't in the ASCII 0-255 space, such as è, ì, à will be received from the console as a bytes sequence and all the function and control keys included cursors and backspace will not accomplish any action, some code such as CR, LF and some ANSI sequence may accomplish actions when are read from the input channel and rewritten on the output channel.
To set the terminal in raw mode you have to use the function cfmakeraw followed by the function tcsetattr.
The code below implements a simple but not very good implemented terminal, anyway I think this code is a good point to start. In any case, the code flow and the error control must be at least better arranged.
The code writes all sequence of ASCII char that enter into the console when a key is typed. All chars that have value smaller then 32 or greater then 126 will be written as [HEX-CODE]
I.E. hitting Esc on the console will be written [1B], the code of Ctrl+C will be written as [03], F1 will be [1B]OP, F11 will be [1B][23~, Enter will be [0D].
If you will hit Ctrl+X [18] will be written and the program stops, but this behaviour is under SW control as you can see in the code.
Here the code:
#include <stdio.h> // Standard input/output definitions
#include <string.h> // String function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitions (struct termios)
#include <sys/ioctl.h> // Used for TCGETS2, which is required for custom baud rates
#include <sys/select.h> // might be used to manage select
int setAttr(int ch, int resetToOld);
#define IN 0
#define OUT 1
typedef struct TermCap
{
int fd;
struct termios oldTermios;
struct termios newTermios;
// fd_set fds; // might be used to manage select
} TermCap;
TermCap m_termCap[2];
int main()
{
int i,ex=0;
char msg;
char buff[20];
m_termCap[IN].fd=STDIN_FILENO;
m_termCap[OUT].fd=STDOUT_FILENO;
// Gets STDIN config and set raw config
setAttr(IN,0);
// Gets STDOUT config and set raw config
setAttr(OUT,0);
// Console loop ... the console terminates when ^X is intercepted.
do {
do {
i=read(m_termCap[IN].fd,&msg,1);
if (i>0){
if (msg<32 || msg>126) {
sprintf(buff,"[%02X]",(unsigned char)msg);
write(m_termCap[OUT].fd,buff,4);
if (msg==24)
ex=1;
}else{
write(m_termCap[OUT].fd,&msg,i);
}
}
usleep(10000); // a minimal delay of 10 millisec
} while(i>0 && !ex);
} while(!ex);
// Reset console to initial state.
setAttr(IN,1);
setAttr(OUT,1);
printf("\r\n\nThe end!");
return 0;
}
int setAttr(int ch, int resetToOld)
{
int retVal=0;
int i;
if (!resetToOld) {
// Read old term config
i=tcgetattr(m_termCap[ch].fd, &m_termCap[ch].oldTermios);
if (i==-1) {
return 1;
}
}
m_termCap[ch].newTermios = m_termCap[ch].oldTermios;
if (!resetToOld) {
// Terminal in raw mode
cfmakeraw(&m_termCap[ch].newTermios);
}
i=tcsetattr(m_termCap[ch].fd, TCSANOW, &m_termCap[ch].newTermios);
if (i==-1) {
retVal = 2;
}
return retVal;
}
Wouldn't this work?
void signalHandler(int signo){
if(signo==SIGINT){
printf("\b\b \b\b");
fflush(NULL);
printf("\nHello World\n");
}
}
In my shell it seems to work fine. The first printf and fflush is what you have to implement in your handler. The printf after that is just a way for me to show you that you can, then, do whatever you want after the ^C not appearing.
Why does this make it not appear? In the first printf I erase the characters by using backspaces and spaces. As stdout is buffered by default and I didn't want to use a newline character, I flushed the buffer manually.

Forcing a terminal not to print Ctrl hotkeys when signals are caught

Good day,
I'm writing my own shell in C for my school which has to resemble bash as closely as possible.
I have to handle signals such as Ctrl-\ and Ctrl-C as bash does; for this reason I'm allowed to use signal function. It works fine, but the thing is whenever a Ctrl-C signal is caught (starting from the second catch), a ^C is printed.
On the net, I've found a workaround suggesting printing "\b \b\b \b\nminishell$ " whenever a Ctrl-C is caught, which will devour the two symbols. The thing is, since at the very first time ^C is not printed, the print devours two symbols of my prompting, making it just minishell instead of minishell$ , with the cursor incorrectly displayed.
Now I've come up with another workaround for this workaround which is to declare a static boolean to not print the baskspaces at the very first call. This doesn't help in case of Ctrl-\ though; Ctrl-\ proceeds to move my cursor to right when I attempt to write the two whitespaces that must replace the ^\.
I don't like these workarounds and would like to know whether there is a way to instruct the terminal not to output this stuff? I'm allowed to use tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs, tcsetattr, tcgetattr, have read their man pages but nothing seems to be helpful.
When you type a key on a terminal, two things happen
the character is echoed (displayed) on this terminal
the character is sent (over the line) to the attached program
Both these actions can be controlled via termios/tcsetattr(): a different character(s) can be sent or echoed, some can be suppressed, etc. (some/most of these actions take place in the terminal-driver , but this is not relevant here)
Demonstration: using tcsetattr() to control the echoing of the terminal:
#include <stdio.h>
#include <stdlib.h>
#define _SVID_SOURCE 1
#include <termios.h>
#include <unistd.h>
#include <signal.h>
struct termios termios_save;
void reset_the_terminal(void)
{
tcsetattr(0, 0, &termios_save );
}
sig_atomic_t the_flag = 0;
void handle_the_stuff(int num)
{
char buff[4];
buff[0] = '[';
buff[2] = '0' + num%10;
num /= 10;
buff[1] = '0' + num%10;
buff[3] = ']';
write(0, buff, sizeof buff);
the_flag = 1;
}
int main (void)
{
int rc;
int ch;
struct termios termios_new;
rc = tcgetattr(0, &termios_save );
if (rc) {perror("tcgetattr"); exit(1); }
rc = atexit(reset_the_terminal);
if (rc) {perror("atexit"); exit(1); }
termios_new = termios_save;
termios_new.c_lflag &= ~ECHOCTL;
rc = tcsetattr(0, 0, &termios_new );
if (rc) {perror("tcsetattr"); exit(1); }
signal(SIGINT, handle_the_stuff);
printf("(pseudoshell)Start typing:\n" );
while(1) {
ch = getc(stdin);
if (the_flag) {
printf("Saw the signal, last character was %02x\n", (unsigned) ch);
break;
}
}
exit (0);
}
The way to set the console such a SW may intercept all typed chars is to set the terminal in RAW MODE. The problems this way may present are that all keys that aren't in the ASCII 0-255 space, such as è, ì, à will be received from the console as a bytes sequence and all the function and control keys included cursors and backspace will not accomplish any action, some code such as CR, LF and some ANSI sequence may accomplish actions when are read from the input channel and rewritten on the output channel.
To set the terminal in raw mode you have to use the function cfmakeraw followed by the function tcsetattr.
The code below implements a simple but not very good implemented terminal, anyway I think this code is a good point to start. In any case, the code flow and the error control must be at least better arranged.
The code writes all sequence of ASCII char that enter into the console when a key is typed. All chars that have value smaller then 32 or greater then 126 will be written as [HEX-CODE]
I.E. hitting Esc on the console will be written [1B], the code of Ctrl+C will be written as [03], F1 will be [1B]OP, F11 will be [1B][23~, Enter will be [0D].
If you will hit Ctrl+X [18] will be written and the program stops, but this behaviour is under SW control as you can see in the code.
Here the code:
#include <stdio.h> // Standard input/output definitions
#include <string.h> // String function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitions (struct termios)
#include <sys/ioctl.h> // Used for TCGETS2, which is required for custom baud rates
#include <sys/select.h> // might be used to manage select
int setAttr(int ch, int resetToOld);
#define IN 0
#define OUT 1
typedef struct TermCap
{
int fd;
struct termios oldTermios;
struct termios newTermios;
// fd_set fds; // might be used to manage select
} TermCap;
TermCap m_termCap[2];
int main()
{
int i,ex=0;
char msg;
char buff[20];
m_termCap[IN].fd=STDIN_FILENO;
m_termCap[OUT].fd=STDOUT_FILENO;
// Gets STDIN config and set raw config
setAttr(IN,0);
// Gets STDOUT config and set raw config
setAttr(OUT,0);
// Console loop ... the console terminates when ^X is intercepted.
do {
do {
i=read(m_termCap[IN].fd,&msg,1);
if (i>0){
if (msg<32 || msg>126) {
sprintf(buff,"[%02X]",(unsigned char)msg);
write(m_termCap[OUT].fd,buff,4);
if (msg==24)
ex=1;
}else{
write(m_termCap[OUT].fd,&msg,i);
}
}
usleep(10000); // a minimal delay of 10 millisec
} while(i>0 && !ex);
} while(!ex);
// Reset console to initial state.
setAttr(IN,1);
setAttr(OUT,1);
printf("\r\n\nThe end!");
return 0;
}
int setAttr(int ch, int resetToOld)
{
int retVal=0;
int i;
if (!resetToOld) {
// Read old term config
i=tcgetattr(m_termCap[ch].fd, &m_termCap[ch].oldTermios);
if (i==-1) {
return 1;
}
}
m_termCap[ch].newTermios = m_termCap[ch].oldTermios;
if (!resetToOld) {
// Terminal in raw mode
cfmakeraw(&m_termCap[ch].newTermios);
}
i=tcsetattr(m_termCap[ch].fd, TCSANOW, &m_termCap[ch].newTermios);
if (i==-1) {
retVal = 2;
}
return retVal;
}
Wouldn't this work?
void signalHandler(int signo){
if(signo==SIGINT){
printf("\b\b \b\b");
fflush(NULL);
printf("\nHello World\n");
}
}
In my shell it seems to work fine. The first printf and fflush is what you have to implement in your handler. The printf after that is just a way for me to show you that you can, then, do whatever you want after the ^C not appearing.
Why does this make it not appear? In the first printf I erase the characters by using backspaces and spaces. As stdout is buffered by default and I didn't want to use a newline character, I flushed the buffer manually.

C - How to use halfdelay(i) in the ncurses library

I am trying to create a snake game in C using the ncurses library. I would like my program to detect user input at a constant tick speed. If there is no input after a certain amount of time I would like my program to continue along (ie. update game).
This is what I wrote to test out the halfdelay(i) function:
#include <ncurses.h>
int main(void)
{
initscr();
halfdelay(5);
int user_input;
do
{
user_input = getch();
if (user_input != -1)
{
clear();
printw("Key pressed: %d\n", user_input);
}
else
{
//printf("Timeout.\n");
printw("Timeout.\n");
}
} while (user_input != ESC);
endwin();
return 0;
}
EDIT #1:
I would like to see
Timeout.
Timeout.
Timeout.
Code used:
#include <ncurses.h>
int main(void)
{
initscr();
halfdelay(5);
int in;
do
{
timeout(1);
in = getch();
if (in != -1)
{
clear();
printw("Key pressed: %d\n", in);
}
else
{
//printf("Timeout.\n");
printw("Timeout.\n");
}
refresh();
} while (in != 27);
endwin();
return 0;
}
Some history first.
Traditionally, the ESC key value has been used as the prefix for escape sequences, which denote special sequences of characters that can be interpreted as a non-graphic character (e.g., an arrow key, a function key, etc.)
Due to (n)curses focus on portability (and telecommunication), out of the box it supports this notion of escape sequences, and as such pressing the ESC key can have some side effects. Notably, when keypad is enabled, there is an inbuilt delay as the program waits to decide if the user simply pressed ESC or if it needs to wait for some more information to complete the escape sequence. This timing can be adjusted via the ESCDELAY environment variable, or the set_escdelay function.
All this is important as you work forward, as given its a game you may want to enable the functionality of the keypad eventually, which will create some extra steps when using the ESC key.
And because of all this, there is no ESC or KEY_ESC macro for for the escape key. Instead, its raw code is 27 (or the octal 033).
Your use of the halfdelay function seems perfectly fine to me, just know that the argument is in tenths of a second, so 5 is half a second. Tenths of a second may not achieve the desired effect in a game, so consider using the timeout function instead, which allows for higher precision.
A simple, working example:
#include <ncurses.h>
int main(void) {
initscr();
halfdelay(5);
int user_input;
while ((user_input = getch()) != 27) {
clear();
if (user_input == ERR)
printw("Timeout.");
else
printw("Key pressed: %d\n", user_input);
refresh();
}
endwin();
}
Your updated program "works", it just doesn't clear the screen properly. Note that you probably don't want to mix calls to printw and printf as it creates a strange mess of the screen.
Also, you should use halfdelay or timeout, but not both. Remember that timeout takes its argument in milliseconds, which is 1/1000 of a second, and it sets the blocking delay for a window (timeout for stdscr, wtimeout for specific windows). It's not a "sleep" style function.
Use one delay function at a time, move clear to outside the if statement, and use printw. This is functionally the same program to the one posted above, just with a do ... while loop.
#include <ncurses.h>
int main(void) {
initscr();
/*
noecho();
scrollok(stdscr, TRUE);
*/
timeout(500);
int in;
do {
in = getch();
clear();
if (in != ERR)
printw("Key pressed: %d\n", in);
else
printw("Timeout.\n");
refresh();
} while (in != 27);
endwin();
}
If you are expecting the display of this program to be something along the lines of
Timeout.
Timeout.
Key pressed: 65
Timeout.
...
like a more traditional terminal, then you simply want to remove clear all together, and un-comment noecho and scrollok.

Recognizing special keys without taking over the screen

In a program that uses the curses or ncurses library, the getch() function can recognize either an ordinary character (like 'x') or the escape sequence sent by the arrow and function keys. For example, typing the up arrow key might send the sequence (Escape, '[', 'A'), but getch() will return the int value KEY_UP. It even uses timing information to distinguish between an Escape character sent by itself and an Escape character that's part of a sequence.
To use getch() successfully, you first have to call initscr(), which clears the screen so that curses can control what's displayed.
Is there a convenient way to recognize special keys without taking over the screen? Ideally I'd like to call getch() without first calling initscr(), and have it return the same value it would have returned if I had called initscr(), but experiment indicates that doesn't work.
Here's a non-working example of what I'm trying to do. The program prompts the user to enter Up, Down, Escape, Left, Right, and conforms that the correct keys were entered.
Running the program with -c invokes initscr() and allows it to work correctly. Running it without -c calls getch() without first calling initscr(); all the getch() calls fail without waiting for input, returning ERR (-1).
// gcc arrows.c -o arrows -lncurses
#include <term.h>
#include <stdio.h>
#include <curses.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int use_curses = 0;
if (argc == 2 && strcmp(argv[1], "-c") == 0) {
use_curses = 1;
}
else if (argc != 1) {
fprintf(stderr, "Usage: %s [-c]\n", argv[0]);
exit(EXIT_FAILURE);
}
WINDOW *win;
if (use_curses) {
win = initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
mvaddstr(0, 0, "Press UP, DOWN, Escape, LEFT, RIGHT");
}
else {
puts("Press UP, DOWN, Escape, LEFT, RIGHT");
}
int keys[5];
for (int i = 0; i < 5; i ++) {
keys[i] = getch();
}
int ok;
if (keys[0] == KEY_UP &&
keys[1] == KEY_DOWN &&
keys[2] == '\x1b' &&
keys[3] == KEY_LEFT &&
keys[4] == KEY_RIGHT)
{
ok = 1;
if (use_curses) {
mvaddstr(2, 0, "OK");
}
else {
puts("OK");
}
}
else {
ok = 0;
char message[100];
sprintf(message,
"Incorrect input: (%d, %d, %d, %d, %d)",
keys[0], keys[1], keys[2], keys[3], keys[4]);
if (use_curses) {
mvaddstr(2, 0, message);
}
else {
puts(message);
}
}
if (use_curses) {
mvaddstr(4, 0, "Press any key to quit ");
(void)getch();
endwin();
}
exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
The output without the -c option (on Ubuntu 17.04 x86_64) is:
Press UP, DOWN, Escape, LEFT, RIGHT
Incorrect input: (-1, -1, -1, -1, -1)
UPDATE: A solution using something other than curses would be fine. It would have to use terminfo, directly or indirectly, to know what input sequences to look for. (It seems to me that recognizing special key inputs should be straightforward and not intimately tied to taking over the entire screen.)
The filter function, called before initscr, tells curses to use a single line (rather than the whole screen). There is an example of its use (named "filter") in ncurses-examples, which consists of a command-prompt.
getch updates at least part of the screen, e.g., when it echoes the input. That update can affect either just a line (using filter), or the whole screen. If you're clever, you can turn off echo, and (since filter suppresses the screen erasure), pretend that it updates nothing at all. For example, you could redirect the output to /dev/null by initializing curses with newterm.
Echoing input is only part of the story: curses does initialize the screen, and the associated refresh done by getch would make that hard to avoid in a portable way. There's an ncurses-specific option in the sample program which works around unwanted switching between normal/alternate screens, which could occur during screen-initialization.
Here's a screenshot of "filter", demonstrating that it does not take over the whole screen. The inverse video is due to ncurses initializing colors (white-on-black), but as the help-message indicates, that's configurable. Without making a movie, it's hard to demonstrate that it handles special keys, but reading the source code should be enough.

Having C wait for a finite amount of time for a user input before moving on with the script

I currently have a small program I am writing that calls for a piece of code that will wait x seconds to see if the user inputs anything, and if no input is given moves on to conduct some more code. Such a thing would also be contained within a for loop such that once the further code has been executed, it waits the finite time period for input again. If input is given via keypress, the code then proceeds to execute further but differing code.
Initially I attempted to tackle this using a while loop, <time.h>'s clock() and scanf() but to no avail.
My latest attempt differs some what, but still does not yield the desired result.
My latest code is as follows:
int c;
char check[10];
for(c=0;c<1000;++c){
//do something
int end=time(0)+(1);
while(time(0)<end){
fgets(check,10,stdin);
if(*check=='\n'){
break;
}
}
//do the other stuff
if(*check!='\n'){
break;
}
}
//even more stuff
I have declared all relevant header files at the top of my script but am now stumped as to how to proceed and I am using Windows. Any help would be appreciated.
Many thanks
You have to use select with a timeout and then perform the IO.
A starting point could be:
#include <sys/select.h>
#include <stdio.h>
int main(void) {
char buf[100];
fd_set rfds;
int r;
FD_ZERO(&rfds);
FD_SET(0, &rfds);
struct timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
r = select(1, &rfds, NULL, NULL, &timeout);
if(r == 0)
printf("timeout!\n");
else {
fgets(buf, 100, stdin);
printf("==> %s\n", buf);
}
}
There is no suitable solution for this in standard C. time.h isn't useful for this task.
Operative systems have various non-standard API libraries that provide this kind of functionality, usually in form of an event that triggers a callback function.
Since you don't mention which system you are using, it won't be possible to give a good answer.
EDIT
On Windows you can use the Windows console API, through windows.h. Here is an example for how to check if a key is pressed, without locking up the program in a busy-wait:
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD fdwMode = ENABLE_WINDOW_INPUT;
(void)SetConsoleMode(hstdin, fdwMode);
(void)FlushConsoleInputBuffer(hstdin);
...
bool is_key_pressed (void)
{
if(WaitForSingleObject(hstdin, 0) != WAIT_OBJECT_0)
{
return false; // no events flagged
}
DWORD cNumRead;
INPUT_RECORD irInBuf[128];
if (!ReadConsoleInput(hstdin, // input buffer handle
irInBuf, // buffer to read into
128, // size of read buffer
&cNumRead)) // number of records read
{
return false;
}
for (DWORD i = 0; i<cNumRead; i++)
{
if(irInBuf[i].EventType == KEY_EVENT)
{
return true;
}
}
return false;
}
Well, in your case, because you are waiting on users input, select() or poll() will be a good choise.

Resources