I'm trying to write a program in which a number starts from 0, but when you press any key, it gets incremented by 1. If nothing is pressed, it keeps on decreasing by 1 per second until it reaches 0. Every increment or decrement is displayed on the console window.
Problem with my approach is that nothing happens until I press a key (that is, it checks if anything is pressed with getch()
). How do I check that nothing is pressed? And of course, !getch()
doesn't work because for that to work, it'll still need to check for keypress which nullifies the purpose itself.
OS: Windows 10 Enterprise, IDE: Code::Blocks
void main()
{
int i, counter = 0;
for (i = 0; i < 1000; i++)
{
delay(1000);
// if a key is pressed, increment it
if (getch())
{
counter += 1;
printf("\n%d", counter);
}
while (counter >= 1)
{
if (getch())
{
break;
}
else
{
delay(1000);
counter--;
printf("\n%d", counter);
}
}
}
}
The following short program requires neither ncurses or threads. It does, however, require changing the terminal attributes - using tcsetattr()
. This will work on Linux and Unix-like systems, but not on Windows - which does not include the termios.h
header file. (Perhaps see this post if you are interested in that subject.)
#include <stdio.h>
#include <string.h>
#include <termios.h>
int main(int argc, char *argv[]) {
struct termios orig_attr, new_attr;
int c = '\0';
// or int n = atoi(argv[1]);
int n = 5;
tcgetattr(fileno(stdin), &orig_attr);
memcpy(&new_attr, &orig_attr, sizeof(new_attr));
new_attr.c_lflag &= ~(ICANON | ECHO);
new_attr.c_cc[VMIN] = 0;
// Wait up to 10 deciseconds (i.e. 1 second)
new_attr.c_cc[VTIME] = 10;
tcsetattr(fileno(stdin), TCSANOW, &new_attr);
printf("Starting with n = %d\n", n);
do {
c = getchar();
if (c != EOF) {
n++;
printf("Key pressed!\n");
printf("n++ => %d\n", n);
} else {
n--;
printf("n-- => %d\n", n);
if (n == 0) {
printf("Exiting ...\n");
break;
}
if (feof(stdin)) {
//puts("\t(clearing terminal error)");
clearerr(stdin);
}
}
} while (c != 'q');
tcsetattr(fileno(stdin), TCSANOW, &orig_attr);
return 0;
}
The vital points are that
new_attr.c_lflag &= ~(ICANON | ECHO);
takes the terminal out of canonical mode (and disables character 'echo'),
new_attr.c_cc[VMIN] = 0;
places it in polling (rather than 'blocking') mode, and
new_attr.c_cc[VTIME] = 10;
instructs the program to wait up till 10 deciseconds for input.
Update (2019-01-13)
clearerr(stdin)
to clear EOF
on stdin
(seems to be necessary on some platforms)This could be done with multithreading as already suggested, but there are other possibilities.
ncurses for example has the possibility to wait for input with a timeout.
An example for ncurses (written by Constantin):
initscr();
timeout(1000);
char c = getch();
endwin();
printf("Char: %c\n", c);
I think poll
could also be used on stdin
to check if input is available.
And to make your program more responsive you could lower your sleep or delay to for example 100ms and only decrement if ten iterations of sleep have passed without input. This will reduce the input lag.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With