Trying to read keyboard input without blocking (Windows, C++) - c++

I'm trying to write a Windows console application (in C++ compiled using g++) that will execute a series of instructions in a loop until finished OR until ctrl-z (or some other keystroke) is pressed. The code I'm currently using to catch it isn't working (otherwise I wouldn't be asking, right?):
if(kbhit() && getc(stdin) == 26)
//The code to execute when ctrl-z is pressed
If I press a key, it is echoed and the application waits until I press Enter to continue on at all. With the value 26, it doesn't execute the intended code. If I use something like 65 for the value to catch, it will reroute execution if I press A then Enter afterward.
Is there a way to passively check for input, throwing it out if it's not what I'm looking for or properly reacting when it is what I'm looking for? ..and without having to press Enter afterward?

Try ReadConsoleInput to avoid cooked mode, and GetNumberOfConsoleInputEvents to avoid blocking.

If G++ supports conio.h then you could do something like this:
#include <conio.h>
#include <stdio.h>
void main()
{
for (;;)
{
if (kbhit())
{
char c = getch();
if (c == 0) {
c = getch(); // get extended code
} else {
if (c == 'a') // handle normal codes
break;
}
}
}
}
This link may explain things a little more for you.

Related

Why doesn't my code work in visual studio?

this is my code
#include <conio.h>
#include <iostream>
using namespace std;
int main() {
bool a = false;
char b='p';
int c=0;
while (a != true) {
if (_kbhit()) {
b = _getch();
}
if (b=='w') {
c++;
cout << c << " ";
}
else if (b == 'c') {
cout << "hello";
}
}
system("pause");
return 0;
}
The problem is where when I press 'w' I want it to print out the value of c and it should be repeating until i press another input for _kbhit() right? because now it add 1 to c then prints c and when i press w again samething. What's wrong with my visual studio I'm using community 2017 I've tried to uninstall it and install it again but same problem occurs.
The problem you're running into seems to be a result of a recently added bug in _getch()/_kbhit.
For an extended key (e.g., a cursor key) it's documented that _getch() returns either a 0x0 or 0xe0 followed by the scan code for the key that was actually pressed. What's not documented is that if the user presses a non-extended key, _kbhit will still return true twice in succession, and calls to _getch() will return the key code the first time, and 0x0 the second time.
In your code, when the user presses 'w' or 'c', _kbhit will return true not just once (as you'd expect) but twice. The first time you call it, it'll return the scan code of the key, and the second it'll return a 0 byte.
What's happening in your code is that you're reading the scan code, printing something appropriately, then _kbhit is returning true again, so you read the '\0' byte, set b to '\0', and then (since you don't have any code to do anything when b is 0) you (repeatedly) do nothing until the next time the user presses a key.
Reference
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/getch-getwch?view=vs-2017
It seems like the program behaves the way you want it to. If I press 'w', it goes into an infinite loop of increasing the value of c and printing it. Pressing any other key stops printing and pressing 'c' goes on the same infinite loop and prints hello. Doesn't seem to be any problems as far as the posted code is concerned. Also, I would like to say the same as #Someprogrammerdude, if it compiles, but doesn't behave the way you want it to, it's an issue with the code, not the IDE and/or compiler.
Hypothetical answer: Your computer might always be thinking that a key is pressed, thus kbhit() always returns true. This maybe caused by a bad mouse/keyboard/controller driver and/or configuration. The code is fine, your PC is not.

Program hangs after handling SIGWINCH

Just for fun I'm trying to write a library that does everything ncurses does, using iostreams and sending escape sequences directly to the terminal.
I'm trying to handle SIGWINCH to tell the library when the terminal is resized. The program responds normally until I resize the terminal, then it stops responding to input, even CTRL-C (although I'm not handling SIGINT, and have the terminal in "raw" mode using termios).
Here's some code snippets I've copied out of my code to show how I've set up the signal handler.
void handle_sigwinch(int sig)
{
if(sig == SIGWINCH)
{
// set a flag here
}
}
void setup_signals()
{
struct sigaction new_sig_action;
new_sig_action.sa_handler = handle_sigwinch;
sigemptyset (&new_sig_action.sa_mask);
new_sig_action.sa_flags = 0;
sigaction (SIGWINCH, NULL, &old_sig_action_);
if (old_sig_action_.sa_handler != SIG_IGN)
{
sigaction (SIGWINCH, &new_sig_action, NULL);
}
}
int main()
{
setup_signals();
int ch;
// exit if ctrl-c is pressed
while((ch == cin.get()) != 3)
{
if(ch > 0)
cout << (char)ch;
}
}
I've tailored my code according to the example provided at https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html#Sigaction-Function-Example for setting up the signal handler.
Is there something I've failed to do after handling SIGWINCH that is causing my program to stop working?
Edit: I left out the code where I set up the terminal using cfmakeraw and tcsetattr, and prior to this I sent an escape sequence for putting xterm into the alternate screenbuffer mode.
Thanks to nos's comment, I found through the debugger that the program was running normally, but cin.get() wasn't receiving valid input anymore. So I changed my google search from "program hangs after signal handler" to "input stream broken after signal handler" and found this answer on StackOverflow, which allowed me to realize that the input stream was in an error state after the signal handler was called.
I had placed a check before the input to ignore a character value of -1 (I must have been thinking of the Arduino library read statement when I did that, where -1 is an indicator that no input is available). So the program was basically ignoring errors on the input stream. (I edited my question's code to reflect that omission).
I placed a cin.clear() statement immediately before the read in the loop, and now the program works as expected.

Reading ESC on Linux during unbuffered input in C

I wrote a getch function for program. I couldn't use curses as it breaks the terminal for iostream I already use. The code:
inline std::string getch() {
char inp[4];
system("stty raw");
inp[0] = std::cin.get();
if(inp[0] == 27 && (inp[1] = std::cin.get()) != std::char_traits<char>::eof()) {
std::cin>>inp[2];
inp[3] = '\0';
}
else {
inp[1] = '\0';
}
system("stty cooked echo");
return std::string(inp);
}
I know it would be better to use termios.h instead of system calls. Everything works fine except if ESC key. I'm trying to capture arrows which are string of for example "\1B[A". When I detect ESC as first character I also read the second two to get full key code.
The problem is that it shouldn't occur when I press ESC as it's code is 1B alone. The cin.get() should return EOF when the buffer is empty during read, but it simply stops.
Is there a way to read ESC key on linux without using curses? Why my solution isn't working?
Thanks
After many hours of searching I found the solution. I had to use read function from unistd.h
It fills an array of given size, with characters from the input. When a key is pressed the buffer is filled with all read characters (works also on multiple keys). So an ESC has simply {27,0,0,...,0} and arrow {27,'[','A',0,0,...,0}.
I've rewritten my function using termios.h and put in library, so anyone can benefit.
Here is the code:
readkey on github

C++ - Making an event loop

Does anyone know how to make an event loop in c++ without a library? It doesn't have to be cross-platform, I'm on a Mac. Basically, I want the program to run and do nothing until the user presses the up arrow key, then the program will output "You pressed up" or something. All i can think of is having an infinite while or for loop and get input with cin, but I don't think cin can detect arrow keys and I believe it pauses the program until it reaches a '\n';
I would want it to look like this:
void RUN()
{
while(true)
{
// poll events and do something if needed
}
}
int main()
{
RUN();
}
I'm kinda sure it's possible without threads, and I've heard that this can be accomplished with fd_set or something, but I'm not sure how.
Any help would be really appreciated.
EDIT:
The program has to run in the background when there aren't any events. For example, Microsoft Word doesn't stop until the user presses a button, it keeps running. I want something like that, but command-line not GUI.
Since you're talking keyboard input, and not looking for a Mac look and feel, what you want is the UNIX way of doing it. And that is,
1) set the terminal in either raw or cbrk mode (I forget which).
2) now use read() to read single characters at a time.
3) temporarily echo the character read (as an int) so you can find what the up arrow key gives you.
As for the more general event loop question, where the only input device is the keyboard, you sit in a loop, and whenever a key is typed (in raw mode?) you call a routine with the value of the key typed. If you had more input devices, you would need multiple threads each could listen to a different device, putting what they find on a queues (with appropriate locking). The main loop would then check the queue and call a routine appropriately everytime something appears in it.
You can use ncurses and enable cbreak to get the raw input stream.
I've used a while loop with signal handlers. Like this incomplete snippet.
void getSomething()
{
std::cout << "Enter new step size: "; std::cout.flush();
std::cin >> globalVariable;
std::getchar(); // consume enter key.
}
void printCommands()
{
std::cout << "1: do something\n"
<< "q: quit\n"
<< "h: help\n"
<< std::endl;
}
void getCommand()
{
// Output prompt
std::cout << "Enter command ('h' for help): "; std::cout.flush();
// Set terminal to raw mode
int ret = system("stty raw");
// Wait for single character
char input = std::getchar();
// Reset terminal to normal "cooked" mode
ret = system("stty cooked");
std::cout << std::endl;
if (input == 'h') printCommands();
else if (input == '1') getSomething();
else if (input == 'q') {
g_next = true;
g_quit = true;
}
}
void
signalHandler(int signo)
{
if (signo == SIGINT) {
g_next = true;
} else if (signo == SIGQUIT) {
getCommand();
}
}
int main(int argc, char* argv[])
{
signal(SIGINT, signalHandler);
signal(SIGUSR1, signalHandler);
signal(SIGQUIT, signalHandler);
do {
// Stuff
} while (!g_quit);
exit(0);
}
The question has been updated to say "The program has to run in the background ... but command-line not GUI."
All traditional; *NIX shells that can put a program into the background also disconnect the program's standard input from the terminal, so AFAIK, this has become impossible.
This does not need to be Mac specific. The Mac supports *NIX mechanisms for reading characters from a keyboard.
AFAICT all the program is doing is waiting for a character, so it might as well block.
Normally the terminal device, tty (teletype!), is interpreting characters typed on the keyboard before your program can read them from standard input. Specifically the tty device normally buffers an entire line of text, and intercepts the rubout character (and a few others like CTRL+w) to edit the line of text. This pre-processing of characters is called a 'line discipline'
You need to set the tty device driver to stop doing that! Then you can get all of the characters the user types.
You change the device using ioctl or termios on the file descriptor.
Search for e.g. "ioctl tty line discipline raw" to understand the details, and find program examples.
You can set the terminal to 'raw' using the command line program stty.
Please read the stty man page because setting it back can be slightly tricky (NB: if you make a mistake it is often easier to kill the terminal, than try to fix it, because there is not echoing of anything you type)
It is possible that the up-arrow is not a single char, so it will require some byte-at-a-time decoding to avoid blocking at the wrong point in the input stream, i.e. if some input sequences are one character, and others two, or three characters, the decoding needs to happen at each byte to decide if there is a pending byte, or one too many read's might get issued, which would cause the program to block.

Getting input from console without cin?

I'm trying to make a little console program that will basically be console pong. So right now I have this:
int main()
{
while(1)
{
clearScreen();
restThread(100);
}
return 0;
}
The only input I need to poll is if the user has pressed the A or D key since the screen was cleared. I will also need to know when the key is released. I'm also trying to do this cross platform.
so really all I need is like an if(keyWasDown('a')) {} sort of function.
Thanks
Maybe you want kbhit (non-blocking) or getch (blocking), both from <conio.h>. There's also getchar, from <stdio.h> or <cstdio>.
If you want the program to wait for a keyboard press, getch or getchar by themselves will do.
If you don't want the program to wait for a keyboard press, kbhit combined with either getch or getchar will suffice.
However, as GMan said, these methods are not really cross platform (if you never intend to try this on different platforms, that's moot, really). For console games, you might be interested looking into ncurses.
#include <stdio.h>
#include <conio.h>
int main()
{
while(1)
{
clearScreen();
if(kbhit())
{
int const ch = getch();
switch(ch)
{
case 0x61: printf("A was pressed!\n"); break;
case 0x64: printf("D was pressed!\n"); break;
}
}
restThread(100);
}
return 0;
}