I am trying to run:
rosrun myrobot keys.cpp
But I am getting this error:
/home/kannachan/drone/src/quantum_drone/scripts/Controls/Keys.cpp: line 6: syntax error near unexpected token `('
/home/kannachan/drone/src/quantum_drone/scripts/Controls/Keys.cpp: line 6: `int getch() {'
I checked the program that I stole on the internet (to get keyboard input):
#include <termios.h>
#include <ros/ros.h>
#include "std_msgs/Int32.h"
int getch() {
static struct termios oldt, newt;
tcgetattr( STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON);
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
int ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "keyboard");
ros::NodeHandle n;
ros::Publisher pub = n.advertise<std_msgs::Int32>("/key", 1000);
ros::Rate loop_rate(10);
while (ros::ok())
{
std_msgs::Int32 c;
c.data = getch();
pub.publish(c);
ros::spinOnce();
loop_rate.sleep();
}
}
I checked line 6 to see the syntax error, but from my experience in C++, there doesn't appear to be any syntax errors. The code was previously commented, but I removed it because it was causing issues with rosrun as well and now I have the following errors that I just do not understand what is going on.
I was being a moron: I think the reason why it couldn't work was that I was running the source file and not actual compiled program.
Solution:
rosrun myrobot key
Related
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;
}
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.
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;
}
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;
}
I want to have user press the space key to jump out of the while loop.
For example:
while ( some condition )
{
printf ("Press space bar to continue...");
}
Thanks!!
I think you mean the following as long as a spacebar followed by the enter key is acceptable given your comments above.
char input = 0;
while( input != ' ' )
{
printf("Press space bar to continue...\n");
scanf("%c",&input);
}
Or if you prefer, without hitting the enter key:
#include <stdio.h>
int main(int argc, char **argv)
{
char input = 0;
while( input != ' ' )
{
printf("Press space bar to continue...\n");
input = getch();
}
}
This worked on my msysgit bash shell. BUT, some people will insist that it work on Linux as well. Which is fine I guess, I love Linux, but I said the above solution worked on msysgit. The following works on my, let me be specific, Oracle VM for Ubuntu 10.10.
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char input = 0;
while( input != ' ' )
{
printf("Press space bar to continue...\n");
input = mygetch();
}
}
int mygetch(void)
{
struct termios oldt, newt;
int ch;
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;
}
The mygetch came from here.
Capturing keystrokes under most platforms requires you to access the console directly. Typically there are libraries available to help you with that. Low-level libraries are termcap (which is derived from terminal capabilities) libraries. There is a "portable" layer on top of termcap called curses. Linux uses the GNU version which is called ncurses and is actually available on a multitude of platforms.
Curses is extensively documented, you can start a tutorial using
$ man ncurses
Your problem requires steps to initialize the console and set up keystroke reader. There are several ways to achieve your desired effect.
I am posting a working example for you to play with. It shows some basic ideas in curses:
/* file: curses_ex1.c */
#include <stdio.h>
#include <curses.h>
const char rotary[4] = "/-\\|";
int main() {
WINDOW *w;
int i = 0;
w = initscr();
if ( w == NULL ) {
return -1; /* unable to initialize curses */
}
timeout(500); /* do not block */
mvprintw(0, 0, "Press space bar to break out of the loop.");
noecho();
for(i = 0; ; i++) { /* no condition so loops forever */
int c;
mvaddch(0, 42, rotary[i%4]); /* display rotator */
c = getch(); /* get a character */
if ( c == ' ')
break;
if ( c != ERR ) { /* not a space but another valid key */
mvprintw(1, 0, "You need to press a space for me to stop (you pressed `%c')", c);
}
}
endwin();
return 0;
}
To compile it:
cc -o curses_ex1 curses_ex1.c -lcurses
Make a variable x = 1, put that in the condition, when they press space bar, set x to 2.