C++ loop until keystroke - c++

If I want to loop until a keystroke there is a quite nice Windows solution:
while(!kbhit()){
//...
}
But this is neither an ISO-Function nor works on other Operating Systems except MS Win. I found other cross-plattform solutions but they are quite confusing and bloated - isn't there another easy way to manage this?

No, C++ standard doesn't define concepts like 'keyboard' and 'keystrokes', because not all systems have such things. Use a portable library, maybe ncurses should have something.

You can use the next version of kbhit() for *nix OSes:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
int kbhit(void)
{
struct termios oldt, newt;
int ch;
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF)
{
ungetc(ch, stdin);
return 1;
}
return 0;
}

Related

Serial IO with posix and write to shared memory

I got a problem with a posix signal, that I use for reading serial data. First here is a small example code how I initialize my serial port, my signal and a shared memory.
#include <unistd.h>
#include <termios.h>
#include <sys/signal.h>
#include <stdint.h>
#include <cassert>
#include <cstdio>
#include <fcntl.h>
#include <iostream>
#include <QSharedMemory>
int fd;
struct termios termAttr;
struct sigaction saio;
char buff[255];
QSharedMemory QShare("IDSTRINGOFTHISMEMORY");
void signal_handler_IO (int status)
{
std::cout << "Got Serial Data!" << std::endl;
int si = read(fd, &buff[0], 255);
if(true) // Works with false here, but then the shared memory ist not used!
{
char TWrite = 't';
QShare.lock();
char *to = (char*)QShare.data();
const char *from = reinterpret_cast<char*>(&TWrite);
memcpy(to, from, 1);
QShare.unlock();
}
}
int main()
{
// Create SharedMemory:
QShare.create(1);
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
return -1;
}
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGINT);
sigaction(SIGIO,&saio,NULL);
fcntl(fd, F_SETFL, FNDELAY|FASYNC);
fcntl(fd, F_SETOWN, getpid());
tcgetattr(fd,&termAttr);
cfsetospeed(&termAttr,B57600);
cfsetispeed(&termAttr,B57600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
termAttr.c_cc[VMIN] = 5;
tcsetattr(fd,TCSANOW,&termAttr);
while(true)
{
usleep(1000000);
}
return 0;
}
I had this code in use for a while and it did work quite well. Now for a different project I want to store the serial data in shared memory block, so I can read the current datapoint from multiple processes. In the signal_handler_IO I try to write the data to a shared memory that is created by sys/shm.h. The moment I started using shared memory the posix signal stopped working. Since I was unsure about the use of the shared memory I also tried to use the QSharedMemory class from qt, which created the same result.
Can you tell me, what the shared memory does to my posix signal?

Using ncurses library along with standard I/O in C++ [duplicate]

On Windows, I have the following code to look for input without interrupting the loop:
#include <conio.h>
#include <Windows.h>
#include <iostream>
int main()
{
while (true)
{
if (_kbhit())
{
if (_getch() == 'g')
{
std::cout << "You pressed G" << std::endl;
}
}
Sleep(500);
std::cout << "Running" << std::endl;
}
}
However, seeing that there is no conio.h, whats the simplest way of achieving this very same thing on Linux?
If your linux has no conio.h that supports kbhit() you can look here for Morgan Mattews's code to provide kbhit() functionality in a way compatible with any POSIX compliant system.
As the trick desactivate buffering at termios level, it should also solve the getchar() issue as demonstrated here.
The ncurses howto cited above can be helpful. Here is an example illustrating how ncurses could be used like the conio example:
#include <ncurses.h>
int
main()
{
initscr();
cbreak();
noecho();
scrollok(stdscr, TRUE);
nodelay(stdscr, TRUE);
while (true) {
if (getch() == 'g') {
printw("You pressed G\n");
}
napms(500);
printw("Running\n");
}
}
Note that with ncurses, the iostream header is not used. That is because mixing stdio with ncurses can have unexpected results.
ncurses, by the way, defines TRUE and FALSE. A correctly configured ncurses will use the same data-type for ncurses' bool as the C++ compiler used for configuring ncurses.
A compact solution based on Christophe's answer is
#include <sys/ioctl.h>
#include <termios.h>
bool kbhit()
{
termios term;
tcgetattr(0, &term);
termios term2 = term;
term2.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term2);
int byteswaiting;
ioctl(0, FIONREAD, &byteswaiting);
tcsetattr(0, TCSANOW, &term);
return byteswaiting > 0;
}
Unlike that answer, this won't leave the terminal in a weird state after the program has exited. However, it still leaves the characters sitting in the input buffer, so the key that was pressed will unwelcomely appear on the next prompt line.
A different solution which fixes this problem is
void enable_raw_mode()
{
termios term;
tcgetattr(0, &term);
term.c_lflag &= ~(ICANON | ECHO); // Disable echo as well
tcsetattr(0, TCSANOW, &term);
}
void disable_raw_mode()
{
termios term;
tcgetattr(0, &term);
term.c_lflag |= ICANON | ECHO;
tcsetattr(0, TCSANOW, &term);
}
bool kbhit()
{
int byteswaiting;
ioctl(0, FIONREAD, &byteswaiting);
return byteswaiting > 0;
}
Usage is as follows
enable_raw_mode();
// ...
if (kbhit()) ...
// ...
disable_raw_mode();
tcflush(0, TCIFLUSH); // Clear stdin to prevent characters appearing on prompt
Now any characters typed between execution of the first and last lines won't show up in the terminal. However, if you exit with Ctrl+C the terminal is left in a weird state. (Sigh)
While using ncurses is functionally equivalent to the Turbo C "conio.h" API, a more complete solution is to use a conio implementation, as can be found here.
You download and use it in your program for a very complete implementation of the conio interface, on Linux. (Or OSX.) Written by Ron Burkey.
If you are using Linux, I found this solution where you can create your own local library:
http://linux-sxs.org/programming/kbhit.html
kbhit.cpp
#include "kbhit.h"
#include <unistd.h> // read()
keyboard::keyboard(){
tcgetattr(0,&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &new_settings);
peek_character=-1;
}
keyboard::~keyboard(){
tcsetattr(0, TCSANOW, &initial_settings);
}
int keyboard::kbhit(){
unsigned char ch;
int nread;
if (peek_character != -1) return 1;
new_settings.c_cc[VMIN]=0;
tcsetattr(0, TCSANOW, &new_settings);
nread = read(0,&ch,1);
new_settings.c_cc[VMIN]=1;
tcsetattr(0, TCSANOW, &new_settings);
if (nread == 1){
peek_character = ch;
return 1;
}
return 0;
}
int keyboard::getch(){
char ch;
if (peek_character != -1){
ch = peek_character;
peek_character = -1;
}
else read(0,&ch,1);
return ch;
}
kbhit.h
#ifndef KBHIT_H
#define KBHIT_H
#include <termios.h>
class keyboard{
public:
keyboard();
~keyboard();
int kbhit();
int getch();
private:
struct termios initial_settings, new_settings;
int peek_character;
};
#endif
inside main.cpp I created an instance:
#include "kbhit.h"
int main(){
int key_nr;
char key;
keyboard keyb;
while(true){
if( keyb.kbhit() ){
key_nr = keyb.getch(); //return int
key = key_nr; // get ascii char
// do some stuff
}
}
return 0;
}

Using another header for conio.h

I would like to write a C++ programme on Ubuntu,
which reacts immediately to the input without pressing enter.
(-> I cannot use the header #include <conio.h> due to the reason that I am working on an UNIX system)
For instance:
I press on my keyboard the key "a", but instead of showing "a" in the terminal,
the programme should show "p".
For the last two days, I tried to do this with the header #include <ncurses.h>.
Unfortunately, it doesn't work.
Therefore, I would like to ask for your request.
With conio.h it would be like this:
#include <iostream>
#include <conio.h>
#include <string>
using namespace std;
int main(void)
{
char c;
c = getch();
while(true)
{
if(c=='a')
{
putch('p');
}
else
{
putch(c);
}
c = getch();
}
cin.sync();
cin.get();
}
Can you please simply post the working source code with #include <ncurses.h> instead of #include <conio.h>?
Thank you so much in advance!!!
With best regards
quark42
Thank you Paulo1205!!!!
Here is my final code without conio.h:
#include <iostream>
#include <string>
#include <unistd.h>
#include <termios.h>
#include <ncurses.h>
using namespace std;
int my_getch(void){
struct termios oldattr, newattr;
unsigned char ch;
int retcode;
tcgetattr(STDIN_FILENO, &oldattr);
newattr=oldattr;
newattr.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
retcode=read(STDIN_FILENO, &ch, 1);
tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
return retcode<=0? EOF: (int)ch;
}
int main(void)
{
char c;
c = my_getch();
while(true)
{
if(c=='a')
{
putchar('p'); fflush(stdout);
}
else
{
putchar(c); fflush(stdout);
}
c = my_getch();
}
cin.sync();
cin.get();
}
If all you want is a quick replacement for the old ConIO getch(), the following code is enough.
int my_getch(void){
struct termios oldattr, newattr;
unsigned char ch;
int retcode;
tcgetattr(STDIN_FILENO, &oldattr);
newattr=oldattr;
newattr.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
retcode=read(STDIN_FILENO, &ch, 1);
tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
return retcode<=0? EOF: (int)ch;
}
However, note that old DOS ConIO is a stripped-down version of UNIX Curses package, which provides everything you need for text terminal screen operations.
EDIT: Curses is most certainly the way to go, anyway. If you ever want to deal with arrow or function keys, without bothering with knowing the escape sequeneces associated with them for each type of terminal, you'd rather learn Curses and its own version of getch().
Also, if you think you'll ever need support for characters out of ASCII range with UTF-8 or any other multibyte representation, you're better-off using ncursesw library's function get_wch() and its sisters.

Using kbhit() and getch() on Linux

On Windows, I have the following code to look for input without interrupting the loop:
#include <conio.h>
#include <Windows.h>
#include <iostream>
int main()
{
while (true)
{
if (_kbhit())
{
if (_getch() == 'g')
{
std::cout << "You pressed G" << std::endl;
}
}
Sleep(500);
std::cout << "Running" << std::endl;
}
}
However, seeing that there is no conio.h, whats the simplest way of achieving this very same thing on Linux?
If your linux has no conio.h that supports kbhit() you can look here for Morgan Mattews's code to provide kbhit() functionality in a way compatible with any POSIX compliant system.
As the trick desactivate buffering at termios level, it should also solve the getchar() issue as demonstrated here.
The ncurses howto cited above can be helpful. Here is an example illustrating how ncurses could be used like the conio example:
#include <ncurses.h>
int
main()
{
initscr();
cbreak();
noecho();
scrollok(stdscr, TRUE);
nodelay(stdscr, TRUE);
while (true) {
if (getch() == 'g') {
printw("You pressed G\n");
}
napms(500);
printw("Running\n");
}
}
Note that with ncurses, the iostream header is not used. That is because mixing stdio with ncurses can have unexpected results.
ncurses, by the way, defines TRUE and FALSE. A correctly configured ncurses will use the same data-type for ncurses' bool as the C++ compiler used for configuring ncurses.
A compact solution based on Christophe's answer is
#include <sys/ioctl.h>
#include <termios.h>
bool kbhit()
{
termios term;
tcgetattr(0, &term);
termios term2 = term;
term2.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term2);
int byteswaiting;
ioctl(0, FIONREAD, &byteswaiting);
tcsetattr(0, TCSANOW, &term);
return byteswaiting > 0;
}
Unlike that answer, this won't leave the terminal in a weird state after the program has exited. However, it still leaves the characters sitting in the input buffer, so the key that was pressed will unwelcomely appear on the next prompt line.
A different solution which fixes this problem is
void enable_raw_mode()
{
termios term;
tcgetattr(0, &term);
term.c_lflag &= ~(ICANON | ECHO); // Disable echo as well
tcsetattr(0, TCSANOW, &term);
}
void disable_raw_mode()
{
termios term;
tcgetattr(0, &term);
term.c_lflag |= ICANON | ECHO;
tcsetattr(0, TCSANOW, &term);
}
bool kbhit()
{
int byteswaiting;
ioctl(0, FIONREAD, &byteswaiting);
return byteswaiting > 0;
}
Usage is as follows
enable_raw_mode();
// ...
if (kbhit()) ...
// ...
disable_raw_mode();
tcflush(0, TCIFLUSH); // Clear stdin to prevent characters appearing on prompt
Now any characters typed between execution of the first and last lines won't show up in the terminal. However, if you exit with Ctrl+C the terminal is left in a weird state. (Sigh)
While using ncurses is functionally equivalent to the Turbo C "conio.h" API, a more complete solution is to use a conio implementation, as can be found here.
You download and use it in your program for a very complete implementation of the conio interface, on Linux. (Or OSX.) Written by Ron Burkey.
If you are using Linux, I found this solution where you can create your own local library:
http://linux-sxs.org/programming/kbhit.html
kbhit.cpp
#include "kbhit.h"
#include <unistd.h> // read()
keyboard::keyboard(){
tcgetattr(0,&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &new_settings);
peek_character=-1;
}
keyboard::~keyboard(){
tcsetattr(0, TCSANOW, &initial_settings);
}
int keyboard::kbhit(){
unsigned char ch;
int nread;
if (peek_character != -1) return 1;
new_settings.c_cc[VMIN]=0;
tcsetattr(0, TCSANOW, &new_settings);
nread = read(0,&ch,1);
new_settings.c_cc[VMIN]=1;
tcsetattr(0, TCSANOW, &new_settings);
if (nread == 1){
peek_character = ch;
return 1;
}
return 0;
}
int keyboard::getch(){
char ch;
if (peek_character != -1){
ch = peek_character;
peek_character = -1;
}
else read(0,&ch,1);
return ch;
}
kbhit.h
#ifndef KBHIT_H
#define KBHIT_H
#include <termios.h>
class keyboard{
public:
keyboard();
~keyboard();
int kbhit();
int getch();
private:
struct termios initial_settings, new_settings;
int peek_character;
};
#endif
inside main.cpp I created an instance:
#include "kbhit.h"
int main(){
int key_nr;
char key;
keyboard keyb;
while(true){
if( keyb.kbhit() ){
key_nr = keyb.getch(); //return int
key = key_nr; // get ascii char
// do some stuff
}
}
return 0;
}

debian linux c++ how to make key stroke brake infinit loop

I want a infinit loop to break when I press "q" key on my keyboard.
Problems I did'nt realize: standard getchar waits for the user to make an
input and press enter, which halt the execution of the loop there.
I got around the "enter" problem, but the loop still halts and waits for the input.
here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <termios.h>
int getch(void); // Declare of new function
int main (void) { char x;
do
{
if (x = getch())
printf ("Got It! \n!);
else
{
delay(2000);
printf ("Not yet\n!);
}
}while x != 'q');
return 0;
}
int getch(void)
{
int ch;
struct termios oldt;
struct termios newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
You could read them from the device:
#define INPUT_QUEUE "/dev/input/event0"
#define EVENT_LEN 16
void readEventLine(FILE * in, char * data) { //read input key stream
int i;
for(i = 0; i <= 15; i++) { //each key press will trigger 16 characters of data, describing the event
data[i] = (char) fgetc(in);
}
}
int readKeyPress() {
FILE * input;
char data[EVENT_LEN];
input = fopen(INPUT_QUEUE, "r+");
readEventLine(input, data);
}
Just call something like this instead of your getch.
Adapted from http://www.cplusplus.com/forum/unices/8206/
I had to do following to make it work correctly, thanks for input!!
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
int getch(void); // Declare of new function
int main (void)
{
char x;
do
{
x = getch();
if (x != EOF)
{
printf ("\r%s\n", "Got something:");
printf ("it's %c!",x); // %c - for character %d - for ascii number
}else
{
delay(2000);
printf ("Not yet\n!);
}
}while x != 'q');
return 0;
}
int getch(void)
{
int ch;
struct termios oldt;
struct termios newt;
long oldf;
long newf;
tcgetattr(STDIN_FILENO, &oldt); /* Store old settings */
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO); /* Make one change to old settings in new settings */
tcsetattr(STDIN_FILENO, TCSANOW, &newt); /* Apply the changes immediatly */
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
newf = oldf | O_NONBLOCK;
fcntl(STDIN_FILENO, F_SETFL, newf);
ch = getchar();
fcntl(STDIN_FILENO, F_SETFL, oldf);
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); /* Reapply the old settings */
return ch;
}