No-Waiting Command for receiving input [duplicate] - c++

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C/C++: Capture characters from standard input without waiting for enter to be pressed
char ch;
think I want to get a character. I know two different ways.
1- using cin command in iostream header file.
cin >> ch;
It will wait for user to type something, then user MUST press enter to send input into ch variable.
2- getch() command in conio.h header file.
ch = _getch();
It will wait for user to type something, as soon as typing a character it will be saved in ch variable and user DOES NOT need to press enter. BTW this code will stop program and will wait for an input.
Now I want to write a command which does not need pressing enter and it does not stop program for pressing something. Just imagine I delay program for 1 seconds, if user presses something it will save it into ch variable, if not nothing, program will continue and it won't stop for pressing something. It is like a picker command, if there is something it will pick it up, if not it will continue.
Hope I'm clear. So how to do it?

On Windows, you can do this:
CreateFile using the filename "CONIN$", which will give you a Win32 handle to the console.
WaitForSingleObject, passing the console handle and a timeout.
If the wait succeeds, use ReadConsoleInput to determine what even happened.
If the wait fails, the timeout occurred.
If you're just polling in a loop performing some other action, then you can use PeekConsoleInput, which checks to see if any events are in the input queue, and always returns immediately.
On Unix, a similar approach will work. Just note that:
It's not necessary to open a file, since stdin is always file descriptor 0.
Use select or poll to test for activity on the input with a timeout.
Final difference: On Windows, mouse activity is captured the same way. On Unix, you'll usually be reading keyboard input from a tty, and mouse stuff is totally separate.

First we need a function to turn on and off nonblocking input:
void nonblock(const bool state){
struct termios ttystate;
//get the terminal state
tcgetattr(STDIN_FILENO, &ttystate);
if (state){
//turn off canonical mode
ttystate.c_lflag &= ~ICANON;
//minimum of number input read.
ttystate.c_cc[VMIN] = 1;
}
else{
//turn on canonical mode
ttystate.c_lflag |= ICANON;
}
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
Now we need a function to test and see if a key was pressed:
int keypress(void){
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
We are going to be checking for two things in parallel. Has the user pressed a key, or has time run out?
Here is a function to change a boolean value after a specified number of seconds:
void SleepForNumberOfSeconds(const int & numberofSeconds,bool & timesUp){
timespec delay = {numberofSeconds,0};
timespec delayrem;
nanosleep(&delay, &delayrem);
timesUp = true;
return;
}
Here is the main function you will be able to call:
void WaitForTimeoutOrInterrupt(int const& numberofSeconds){
bool timesUp = false;
std::thread t(SleepForNumberOfSeconds, numberofSeconds, std::ref(timesUp));
nonblock(1);
while (!timesUp && !keypress()){
}
if (t.joinable()){
t.detach();
}
nonblock(0);
return;
}
Here is the code to test out.
compiled with:
g++ -std=c++0x -o rand rand.cpp -lpthread
on:
gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
This is just one solution, and it may not work for you.
Consider looking into ncurses as well.

Related

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.

How can I use getline without blocking for input?

Is there any method to call getline() and, if there is no input given, not to block and waiting?
I have the following code:
while(true){
if(recv(sd, tBuffer, sizeof(tBuffer), MSG_PEEK | MSG_DONTWAIT) > 0) break;
getline(cin,send);
}
I want to wait for an input, but if I receive some data at sd socket, I want stop waiting for the data and exit the while. My code right now, just stucks on first iteration at getline(). I want to evaluate getline(), and if is no input available, go back at if.
Is this possible?
PS: I tried with cin.peek(), but that blocks for input too.
You should be able to do this by setting non-blocking mode on standard input, file descriptor 0:
int flags = fcntl(0, F_GETFL, 0);
fcntl(0, F_SETFL, flags | O_NONBLOCK);
Now, if there's no input available, the underlying read() system call will return 0, and std::cin will think that this is end of file, and set eof() on std::cin.
When you wish to read from standard input again, just clear() the stream's state.
The only complicating factor here is that this makes it difficult to detect a real end-of-file condition on std::cin. Not much a problem when standard input is an interactive terminal; but if standard input can be a file this is going to be an issue.
In that case, your only realistic option is to forego std::cin completely, put non-blocking mode on file descriptor 0, poll() or select() it, to determine when there's something to read, then read() it.
Although you could also use poll() or select() with std::cin, this is going to get complicated, because you will need to explicitly check if there's anything already buffered in std::cin's streambuf, because that would, obviously, preempt any kind of poll() or select() checking; but by attempting to read something from std::cin, you still run the risk of reading the buffered data, then attempting to read() from the underlying file descriptor that's now in non-blocking mode, this resulting in a fake end-of-file condition.
To summarize: you need invest some additional time reading and understanding how file streams, and stream buffers work; and how file descriptors actually work, and how non-blocking mode works; in order to figure out the correct logic you will need to use.
Oh, and if you insist on going the non-blocking route with std::cin, and getline(), you will have no easy way to determine if the string returned by getline() ends because getline() actually read a newline from standard input, or it reached a premature fake-end of file condition and not the entire line of input has actually been read.
So, with non-blocking mode, and std::cin, you'll be pretty much forced to use read(), instead of getline().
The istream::getsome() method can be used to perform non-blocking reads. You can use it to build a non-blocking equivalent of std::getline.
bool getline_async(std::istream& is, std::string& str, char delim = '\n') {
static std::string lineSoFar;
char inChar;
int charsRead = 0;
bool lineRead = false;
str = "";
do {
charsRead = is.readsome(&inChar, 1);
if (charsRead == 1) {
// if the delimiter is read then return the string so far
if (inChar == delim) {
str = lineSoFar;
lineSoFar = "";
lineRead = true;
} else { // otherwise add it to the string so far
lineSoFar.append(1, inChar);
}
}
} while (charsRead != 0 && !lineRead);
return lineRead;
}
This functions works identically to the original std::getline() function except it always returns instantly. I've got it on my gists because it comes in handy occasionally.
I used select() to retrieve the status of the stdin file descriptor. This worked on Ubuntu and on an embedded Linux board. If stdin still has not received an enter keystroke from the user, select will wait some time and report that stdin is not ready. stopReading can stop monitoring the stdin and continue on other stuff. You can edit it as needed. It may not work on special input tty's.
#include <sys/select.h>
static constexpr int STD_INPUT = 0;
static constexpr __suseconds_t WAIT_BETWEEN_SELECT_US = 250000L;
// Private variable in my class, but define as needed in your project
std::atomic<bool> stopReading;
...
std::string userInput = "";
while (false == stopReading)
{
struct timeval tv = { 0L, WAIT_BETWEEN_SELECT_US };
fd_set fds;
FD_ZERO(&fds);
FD_SET(STD_INPUT, &fds);
int ready = select(STD_INPUT + 1, &fds, NULL, NULL, &tv);
if (ready > 0)
{
std::getline(std::cin, userInput);
break;
}
}

weird behaviour when calling external script

My C++ program is just a very simple while loop in which I grab user command from the console (standard input, stdin) using the getline() blocking function. Every now and then I must call an external bash script for other purposes. The script is not directly related to what the user do, it just do some stuff on the filesystem but it has to print text lines in the console standard output (stdout) to inform the user about the outcome of its computations.
What I get is that as soon as the script starts and prints stuff to stdout, the getline() function behave has it were non-blocking (it is supposed to block until the user inputs some text). As a consequence, the while(1) starts spinning at full speed and the CPU usage skyrockets to a near 100%.
I narrowed down the problem to a single C++ source file which reproduces the problem in the same exact way, here it is:
#include<iostream>
#include<string>
#include<sstream>
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
int pid = fork(); // spawn
if(pid > 0)
{
// child thread
system("sleep 5; echo \"you're screwed up!!!\"");
}
else
{
// main thread
std::string input;
while(1)
{
std::cout << std::endl << "command:";
getline(std::cin, input);
}
}
}
In this particular case after 5 seconds the program starts spamming "\ncommand:" on stdout and the only way to stop it is sending a SIGKILL signal. Sometimes you have to press some keys on the keyboard before the program starts spamming text lines.
Let run this code for 10 seconds then press any key on the keyboard. Be sure to be ready to fire the SIGKILL signal to the process from another console. You can use the command killall -9 progname
Did you check if failbit or eof is set?
Try changing the following line of your code
if (pid > 0)
to
if (pid == 0)
fork() returns 0 to child and pid of child to parent. In your example, you are running system() in parent and exiting the parent. The child then becomes orphan process running in a while(1) loop which i guess is messing up with the stdin, stdout.
I have modified your program to run system() in child process.
The basic problem:
if(pid > 0)
{
// child thread
system("sleep 5; echo \"you're screwed up!!!\"");
}
this is the PARENT. ;) The child gets pid : 0.

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.