Ncurses does not detect keystroke - c++

I am trying to use ncurses to get non-blocking input.
#include <iostream>
#include <ncurses.h>
int main()
{
char ch;
nodelay(stdscr, TRUE);
while(1)
{
ch= getch();
if (ch == ERR) {
printf("here \n");
usleep(100000);
}
else {
printf("---------------\n");
}
}
}
However when I run this code, irrespective of what I press I always just get "here" printed.
Sample output:
Latitude-E6430:~$ ./try
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
here
dhere
ddhere
dhere
here
The d's and the spaces are not detected at all.
Can someone tell me why?
Thanks.

Finally I found the answer to the question.
I need to do initscr();
After that I am able to print out correctly (the formatting is not-as-expected though).
Correct code:
#include <iostream>
#include <ncurses.h>
int main()
{
char ch;
initscr();
nodelay(stdscr, TRUE);
while(1)
{
ch= getch();
if (ch == ' ') {
// printf("here \n");
usleep(100000);
}
else {
printf("---------------\n");
}
}
}

Related

C++: how to input text in console and keep it at the bottom

The description of the problems:
I code a program that prints out some information frequently.
I want to input some commands during the program running.
std::out will flush out my input.
For examples:
>>> ./my_program
[sensorA] initial...ok
[sensorB] initial...ok
ge //!< I want to input 'get' here but the next output break it
[motorA] self-check...ok
t //!< break it into two spice
Expected:
>>> ./my_program
[sensorA] initial...ok
[sensorB] initial...ok
[motorA] self-check...ok
get //!< always fixed here whenever I input
Thanks a lot !
Appreciation
Firstly, I show my Great appreciation to Sam Varshavchik
Primary Result I found
Sam gave me the hints to use Curses Library. I read the doc and now finish the basic function.
My method is to create to sub-windows(output_win and input_win). User input show in input_win whereas program information print on output_win.
Let me share my code:
#include <iostream>
#include <string>
#include <curses.h>
#include <thread>
#include <atomic>
#include <chrono>
#include <unistd.h>
using namespace std;
WINDOW* win;
WINDOW* output_win;
WINDOW* input_win;
int row = 0, col = 0;
std::atomic<bool> flag(false);
string buf;
void ninit()
{
win = initscr();
getmaxyx(win, row, col);
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
refresh();
}
void nprintf(string str)
{
touchwin(win);
str += '\n';
wprintw(output_win, str.c_str());
wrefresh(output_win);
}
void nprintf(const char* fmt, ...)
{
touchwin(win);
va_list ap;
va_start(ap, fmt);
vw_printw(output_win, fmt, ap);
va_end(ap);
wrefresh(output_win);
}
void nmonitor()
{
while(1)
{
char x = getch();
if(x != '\r')
{
touchwin(win);
buf += x;
waddch(input_win, x);
}
else
{
nprintf(buf);
touchwin(input_win);
flag = true;
wclear(input_win);
}
wrefresh(input_win);
}
}
string nget()
{
while(!flag)
usleep(100);
string cmd = buf;
flag = false;
buf = "";
return cmd;
}
////////////////////////////////
void print_thread()
{
while(1)
{
static int i = 0;
nprintf("no.%d\n", i++);
usleep(100000);
}
}
int main()
{
ninit();
fflush(stdin);
output_win = subwin(win, row - 1, col, 0, 0);
scrollok(output_win, true);
input_win = subwin(win, 1, col, row - 1, 0);
std::thread pthr(print_thread);
std::thread nthr(nmonitor);
string cmd;
while(1)
{
cmd = nget();
if(cmd == "quit")
break;
else
nprintf("[info] You input: %s\n", cmd.c_str());
}
getch();
endwin();
}
Environment Configure and Build
For Mac OSX:
brew install ncurses
For Ubuntu:
sudo apt-get install libcurses5-dev
To build:
g++ f04.cpp - f04 -lcurses # I try for 4 times so name it f04
Some bugs
Actually it has some bugs, here I found:
when you input backspace, it will not delete a char but show a special char;
after inputting enter, output_win sometimes show some strange words.
I am a beginner and may need help.
(Maybe I will solve them soon.)
May it can help others indeed.
You can try the following:
Before you print something, read from stdin everything the user
entered so far.
If there was something in stdin, print '\r' (so that the next output will overwrite the text entered by the user).
Print your output.
Print the text the user entered so far (but without '\n').
Please also see:
Rewinding std::cout to go back to the beginning of a line

NCurses getch always returns ERR (-1)

I've just started to work with ROS and I'm trying to write a node that publish keys in a topic.
I have created a node on a Linux Ubuntu 16.04.4 using ncurses.
This is my code:
#include <curses.h>
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
int ch;
nodelay(stdscr, TRUE);
ros::init(argc, argv, "keyboard_driver");
ros::NodeHandle n;
ros::Publisher key_pub = n.advertise<std_msgs::String>("keys", 1);
ros::Rate loop_rate(100);
while (ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
if ((ch = getch()) != ERR)
{
ss << ch;
std::cout << ch;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
key_pub.publish(msg);
}
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
I'm using ncurses to avoid terminal buffer.
The topic appears, but I don't get anything if, in another terminal, I run this command:
rostopic echo /keys
Debugging it I have found that getch() always return -1.
How can I do to make it work?
UPDATE
I have tried this small program, and it doesn't print anything:
#include <iostream>
#include <curses.h>
int main(int argc, char **argv)
{
int ch;
cbreak();
nodelay(stdscr, TRUE);
for(;;)
{
if ((ch = getch()) != ERR)
{
std::cout << ch;
}
}
return 0;
}
You've set nodelay so getch will return immediately with ERR if there's no data ready from the terminal. That's why getch is returning -1 (ERR). You haven't set cbreak or raw to disable terminal buffering, so you're still getting that -- no data will come from the terminal until Enter is hit.
So add a call to cbreak() at the start (just before or after the call to nodelay()) and it should work as you expect.
To use getch() you have to do the following:
#include <iostream>
#include <curses.h>
#include <signal.h>
#include <stdlib.h>
void quit(int sig)
{
endwin();
exit(0);
}
int main(int argc, char **argv)
{
int ch;
signal(SIGINT,quit);
initscr();
cbreak();
nodelay(stdscr, TRUE);
for(;;)
{
if ((ch = getch()) != ERR)
{
std::cout << ch;
}
}
return 0;
}
I forget to add initscr(); call at the beginning and endwin(); at the end of the program.
More info about how to use ncurses library here.

read() from command line returns EISDIR error

I'm trying to read arrow keys character from the terminal and use the to send String type of messages through ROS. For doing so I'm using termios read() function.
Firstly, I have done a small test that works fine under C, compiled using gcc. This is the code:
#include <unistd.h>
#include <stdlib.h>
#include <termios.h> // for keyboard input
#include <string.h>
#include <stdio.h>
bool quit_requested = false;
void processKeyboardInput (char c)
{
switch (c)
{
case 68:// Left arrow pressed
{
puts("Left pressed");
break;
}
case 67://Right arrow pressed
{
puts("Right pressed");
break;
}
case 65://Up arrow pressed
{
puts("Up pressed");
break;
}
case 66://Down arrow pressed
{
puts("Down pressed");
break;
}
case 'q'://Quit arrow pressed
{
quit_requested = true;
puts("q pressed, quitting...");
}
default:
{
break;
}
}
}
int main(int argc, char const *argv[]) {
int key_file_descriptor;
struct termios raw;
struct termios original_terminal_state;
tcgetattr(key_file_descriptor, &original_terminal_state); // get terminal properties
memcpy(&raw, &original_terminal_state, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);//local modes, enable canonical mode and echo
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;//special characters
raw.c_cc[VEOF] = 2;
tcsetattr(key_file_descriptor, TCSANOW, &raw);
puts("Reading from keyboard");
puts("---------------------------");
puts("Press the arrow keys to move and q to quit");
char c;
while (!quit_requested)
{
if (read(key_file_descriptor, &c, 1) < 0)
{
perror("read char failed():");
exit(-1);
}
processKeyboardInput(c);
}
tcsetattr(key_file_descriptor, TCSANOW, &original_terminal_state);
puts("Exit");
return 0;
}
Once done this, I have added ROS stuff to send the keys using a String message:
#include <unistd.h>
#include <termios.h> // for keyboard input
#include <cstring>
#include <stdio.h>
#include "ros/ros.h"
#include "std_msgs/String.h"
bool quit_requested = false;
std::string processKeyboardInput (char c)
{
switch (c)
{
case 68:// Left arrow pressed
{
puts("Left pressed");
return ("left");
}
case 67://Right arrow pressed
{
puts("Right pressed");
return ("right");
}
case 65://Up arrow pressed
{
puts("Up pressed");
return ("forward");
}
case 66://Down arrow pressed
{
puts("Down pressed");
return ("backward");
}
case 'q'://Quit arrow pressed
{
quit_requested = true;
puts("q pressed, quitting...");
}
default:
{
return ("");
}
}
}
int main(int argc, char **argv) {
std::string cmd;
int key_file_descriptor;
char c;
int error;
struct termios raw;
struct termios original_terminal_state;
//std::string name = "key_teleop";
/**** INIT ROS STUFFS *****/
ros::init(argc, argv, "key_teleop");
std_msgs::String msg;
ros::NodeHandle n;
ros::Publisher key_pub = n.advertise<std_msgs::String>("key_teleop", 1000);
ros::Rate loop_rate(1);
/***************************/
tcgetattr(key_file_descriptor, &original_terminal_state); // get terminal properties
memcpy(&raw, &original_terminal_state, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);//local modes, enable canonical mode and echo
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;//special characters
raw.c_cc[VEOF] = 2;
tcsetattr(key_file_descriptor, TCSANOW, &raw);
puts("Reading from keyboard");
puts("---------------------------");
puts("Press the arrow keys to move and q to quit");
while (ros::ok() & (!quit_requested))
{
//if (read(key_file_descriptor, &c, 1) < 0)
error = read(key_file_descriptor, &c, 1);
if ( error < 0)//Process the error
{//TODO I get errors, not ROSify code doesn't, C / C++ code mixture issue??
if (error == EAGAIN)
puts ("EAGAIN");
else if (error == EBADF)
puts ("EBADF");
else if (error == EFAULT)
puts ("EFAULT");
else if (error == EINTR)
puts ("EINTR");
else if (error == EINVAL)
puts ("EINVAL");
else if (error == EIO)
puts ("EIO");
else if (error = EISDIR)
puts ("EISDIR");
perror("read char failed():");
exit(-1);
}
cmd = processKeyboardInput(c);
if (!cmd.empty())
{
msg.data = cmd;
key_pub.publish(msg);
ros::spinOnce();
//loop_rate.sleep();
}
}
tcsetattr(key_file_descriptor, TCSANOW, &original_terminal_state);
puts("Exit");
return 0;
}
Once created the CMakeLists.txt and package.xml, I have compiled using catkin_make. Test it out and now doesn't work, most of the times I receive a error return at read(), it enter into EISDIR case and prints it. Any idea why now I'm receiving this error while the previous c example is working fine??
Thanks in advance!
Following the reccomendation of #Jonathan, I have added the flags -Wall -Werror -Wextra to the CMake compiling flags and discovered the next message thrown by the compiler:
error: ‘key_file_descriptor’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
I have found this nice article and changed the int key_file_descriptorI was using for STDIN_FILENO. So I have replaced it all over the code and now is working! I have deleted also the comparison typo. Thanks guys!

Wait for input for a certain time

Is there any function that can wait for input until a certain time is reached? I'm making kind of Snake game.
My platform is Windows.
For terminal based games you should take a look at ncurses.
int ch;
nodelay(stdscr, TRUE);
for (;;) {
if ((ch = getch()) == ERR) {
/* user hasn't responded
...
*/
}
else {
/* user has pressed a key ch
...
*/
}
}
Edit:
See also Is ncurses available for windows?
I found a solution using kbhit() function of conio.h as follows :-
int waitSecond =10; /// number of second to wait for user input.
while(1)
{
if(kbhit())
{
char c=getch();
break;
}
sleep(1000); sleep for 1 sec ;
--waitSecond;
if(waitSecond==0) // wait complete.
break;
}
Try with bioskey(), this is an example for that:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <bios.h>
#include <ctype.h>
#define F1_Key 0x3b00
#define F2_Key 0x3c00
int handle_keyevents(){
int key = bioskey(0);
if (isalnum(key & 0xFF)){
printf("'%c' key pressed\n", key);
return 0;
}
switch(key){
case F1_Key:
printf("F1 Key Pressed");
break;
case F2_Key:
printf("F2 Key Pressed");
break;
default:
printf("%#02x\n", key);
break;
}
printf("\n");
return 0;
}
void main(){
int key;
printf("Press F10 key to Quit\n");
while(1){
key = bioskey(1);
if(key > 0){
if(handle_keyevents() < 0)
break;
}
}
}
Based on #birubisht answer I made a function which is a bit cleaner and uses NON-deprecated versions of kbhit() and getch() - ISO C++'s _kbhit() and _getch().
Function takes: number of seconds to wait for user input
Function returns: _ when user does not put any char, otherwise it returns the inputed char.
/**
* Gets: number of seconds to wait for user input
* Returns: '_' if there was no input, otherwise returns the char inputed
**/
char waitForCharInput( int seconds ){
char c = '_'; //default return
while( seconds != 0 ) {
if( _kbhit() ) { //if there is a key in keyboard buffer
c = _getch(); //get the char
break; //we got char! No need to wait anymore...
}
Sleep(1000); //one second sleep
--seconds; //countdown a second
}
return c;
}

Create a function to check for key press in Unix using ncurses

I have been looking for an equivalent to kbhit() and I have read several forums on this subject, and the majority seem to suggest using ncurses.
How should I go about checking if a key is pressed in C++ using ncurses?
The function getch() provided by ncurses reads a character from the window.
I would like to write a function that only checks if there is a key press and then I want to do getch().
You can use the nodelay() function to turn getch() into a non-blocking call, which returns ERR if no key-press is available. If a key-press is available, it is pulled from the input queue, but you can push it back onto the queue if you like with ungetch().
#include <ncurses.h>
#include <unistd.h> /* only for sleep() */
int kbhit(void)
{
int ch = getch();
if (ch != ERR) {
ungetch(ch);
return 1;
} else {
return 0;
}
}
int main(void)
{
initscr();
cbreak();
noecho();
nodelay(stdscr, TRUE);
scrollok(stdscr, TRUE);
while (1) {
if (kbhit()) {
printw("Key pressed! It was: %d\n", getch());
refresh();
} else {
printw("No key pressed yet...\n");
refresh();
sleep(1);
}
}
}