How to identify shift/ctrl keys were pressed in the linux world (without using GL and X11/XKBlib.h) in C++? - c++

I used to do in windows:
SHORT key1 = ::GetKeyState(VK_LSHIFT)& 0xF0;
SHORT key2 = ::GetKeyState(VK_RSHIFT)& 0xF0;
SHORT key3 = ::GetKeyState(VK_LCONTROL)& 0xF0;
SHORT key4 = ::GetKeyState(VK_RCONTROL)& 0xF0;
bShift = (key1 != 0) || (key2 != 0);
bCtrl = (key3 != 0) || (key4 != 0);
How to detect if the shift/ctrl keys were pressed in the linux world?
Without using GL(it can be pressed not in the graphic window..) or X11..
Is there something general to retrieve it directly form the OS?
Thanks,
Vladimir.

I would do this using Linux Input API. Take a look at Youarefunny's answer here to see how you can check current key state (pressed or released).
Such check may take noticeable amount of time especially if you need to call it very often. So once you determine the initial state you may monitor for changes by reading input events from device file, like this (I skipped error-checking for brevity):
#include <stdio.h>
#include <linux/input.h>
int main (int argc, char *argv[])
{
struct input_event ev;
FILE *kbd = fopen("/dev/input/by-id/usb-Dell_Dell_QuietKey_Keyboard-event-kbd", "r");
while (fread(&ev, sizeof(ev), 1, kbd) == 1)
{
if (ev.type == EV_KEY && (ev.code == KEY_LEFTSHIFT || ev.code == KEY_RIGHTSHIFT))
{
switch (ev.value)
{
case 0: printf("Shift released\n"); break;
case 1: printf("Shift pressed\n"); break;
case 2: printf("Shift repeated\n"); break;
default: break;
}
}
// similarly for KEY_LEFTCTRL, KEY_RIGHTCTRL, etc.
}
fclose(kbd);
return 0;
}
Note that reading /dev/input/* files will probably require root privileges (unless you run chmod before) since default access mode is 640 and the files belong to root.

Related

How to get real time key input in C++? (Linux) [duplicate]

how to detect a keyboard event in C without prompting the user in linux? That is the program running should terminate by pressing any key.
can anyone please help with this?
You have to modify terminal settings using termios. See Stevens & Rago 2nd Ed 'Advanced Programming in the UNIX Environment' it explains why tcsetattr() can return successfuly without having set all terminal characteristcs, and why you see what looks to be redundant calls to tcsetattr().
This is ANSI C in UNIX:
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
int checktty(struct termios *p, int term_fd)
{
struct termios ck;
return (
tcgetattr(term_fd, &ck) == 0 &&
(p->c_lflag == ck.c_lflag) &&
(p->c_cc[VMIN] == ck.c_cc[VMIN]) &&
(p->c_cc[VTIME] == ck.c_cc[VMIN])
);
}
int
keypress(int term_fd)
{
unsigned char ch;
int retval=read(term_fd, &ch, sizeof ch);
return retval;
}
int /* TCSAFLUSH acts like fflush for stdin */
flush_term(int term_fd, struct termios *p)
{
struct termios newterm;
errno=0;
tcgetattr(term_fd, p); /* get current stty settings*/
newterm = *p;
newterm.c_lflag &= ~(ECHO | ICANON);
newterm.c_cc[VMIN] = 0;
newterm.c_cc[VTIME] = 0;
return(
tcgetattr(term_fd, p) == 0 &&
tcsetattr(term_fd, TCSAFLUSH, &newterm) == 0 &&
checktty(&newterm, term_fd) != 0
);
}
void
term_error(void)
{
fprintf(stderr, "unable to set terminal characteristics\n");
perror("");
exit(1);
}
void
wait_and_exit(void)
{
struct timespec tsp={0,500}; /* sleep 500 usec (or likely more ) */
struct termios attr;
struct termios *p=&attr;
int keepon=0;
int term_fd=fileno(stdin);
fprintf(stdout, "press any key to continue:");
fflush(stdout);
if(!flush_term(term_fd, p) )
term_error();
for(keepon=1; keepon;)
{
nanosleep(&tsp, NULL);
switch(keypress(term_fd) )
{
case 0:
default:
break;
case -1:
fprintf(stdout, "Read error %s", strerror(errno));
exit(1);
break;
case 1: /* time to quit */
keepon=0;
fprintf(stdout, "\n");
break;
}
}
if( tcsetattr(term_fd, TCSADRAIN, p) == -1 &&
tcsetattr(term_fd, TCSADRAIN, p) == -1 )
term_error();
exit(0);
}
int main()
{
wait_and_exit();
return 0; /* never reached */
}
The nanosleep call is there to prevent the code from gobbling up system resources. You could call nice() and not use nanosleep(). All this does is sit and wait for a keystroke, then exit.
If you want to do that in a graphical application, you should use some libraries to do this.
Such a simple task can be easily done with whatever library (even low level ones like Xlib).
Just choose one and look for a tutorial that shows how to handle keyboard events.
no way with ANSI C. Look at ncurses lib.
Here’s code from /usr/src/bin/stty/key.c:
f_cbreak(struct info *ip)
{
if (ip->off)
f_sane(ip);
else {
ip->t.c_iflag |= BRKINT|IXON|IMAXBEL;
ip->t.c_oflag |= OPOST;
ip->t.c_lflag |= ISIG|IEXTEN;
ip->t.c_lflag &= ~ICANON;
ip->set = 1;
}
}
At a minimum, you have to get out of ICANON mode before your select(2) syscall or your FIONREAD ioctl will work.
I have an ancient, 20-year-old perl4 program that clears CBREAK and ECHO mode this way. It is doing curses stuff without resorting to the curses library:
sub BSD_cbreak {
local($on) = shift;
local(#sb);
local($sgttyb);
# global $sbttyb_t
$sgttyb_t = &sgttyb'typedef() unless defined $sgttyb_t;
# native BSD stuff by author (tsc)
ioctl(TTY,&TIOCGETP,$sgttyb)
|| die "Can't ioctl TIOCGETP: $!";
#sb = unpack($sgttyb_t,$sgttyb);
if ($on) {
$sb[&sgttyb'sg_flags] |= &CBREAK;
$sb[&sgttyb'sg_flags] &= ~&ECHO;
} else {
$sb[&sgttyb'sg_flags] &= ~&CBREAK;
$sb[&sgttyb'sg_flags] |= &ECHO;
}
$sgttyb = pack($sgttyb_t,#sb);
ioctl(TTY,&TIOCSETN,$sgttyb)
|| die "Can't ioctl TIOCSETN: $!";
}
sub SYSV_cbreak {
# SysV code contributed by Jeff Okamoto <okamoto#hpcc25.corp.hp.com>
local($on) = shift;
local($termio,#termio);
# global termio_t ???
$termio_t = &termio'typedef() unless defined $termio_t;
ioctl(TTY,&TCGETA,$termio)
|| die "Can't ioctl TCGETA: $!";
#termio = unpack($termio_t, $termio);
if ($on) {
$termio[&termio'c_lflag] &= ~(&ECHO | &ICANON);
$termio[&termio'c_cc + &VMIN] = 1;
$termio[&termio'c_cc + &VTIME] = 1;
} else {
$termio[&termio'c_lflag] |= (&ECHO | &ICANON);
# In HP-UX, it appears that turning ECHO and ICANON back on is
# sufficient to re-enable cooked mode. Therefore I'm not bothering
# to reset VMIN and VTIME (VEOF and VEOL above). This might be a
# problem on other SysV variants.
}
$termio = pack($termio_t, #termio);
ioctl(TTY, &TCSETA, $termio)
|| die "Can't ioctl TCSETA: $!";
}
sub POSIX_cbreak {
local($on) = shift;
local(#termios, $termios, $bitmask);
# "file statics" for package cbreak:
# $savebits, $save_vtime, $save_vmin, $is_on
$termios_t = &termios'typedef() unless defined $termios_t;
$termios = pack($termios_t, ()); # for Sun SysVr4, which dies w/o this
ioctl(TTY,&$GETIOCTL,$termios)
|| die "Can't ioctl GETIOCTL ($GETIOCTL): $!";
#termios = unpack($termios_t,$termios);
$bitmask = &ICANON | &IEXTEN | &ECHO;
if ($on && $cbreak'ison == 0) {
$cbreak'ison = 1;
$cbreak'savebits = $termios[&termios'c_lflag] & $bitmask;
$termios[&termios'c_lflag] &= ~$bitmask;
$cbreak'save_vtime = $termios[&termios'c_cc + &VTIME];
$termios[&termios'c_cc + &VTIME] = 0;
$cbreak'save_vmin = $termios[&termios'c_cc + &VMIN];
$termios[&termios'c_cc + &VMIN] = 1;
} elsif ( !$on && $cbreak'ison == 1 ) {
$cbreak'ison = 0;
$termios[&termios'c_lflag] |= $cbreak'savebits;
$termios[&termios'c_cc + &VTIME] = $cbreak'save_vtime;
$termios[&termios'c_cc + &VMIN] = $cbreak'save_vmin;
} else {
return 1;
}
$termios = pack($termios_t,#termios);
ioctl(TTY,&$SETIOCTL,$termios)
|| die "Can't ioctl SETIOCTL ($SETIOCTL): $!";
}
sub DUMB_cbreak {
local($on) = shift;
if ($on) {
system("stty cbreak -echo");
} else {
system("stty -cbreak echo");
}
}
And it elsewhere says that for POSIX,
($GETIOCTL, $SETIOCTL) = (TIOCGETA, TIOCSETA);
RE-translation back into the original C is left as an exercise for the reader, because I can't remember where the 20-years-ago-me snagged it from originally. :(
Once you're out of ICANON mode on the tty, now your select(2) syscall works properly again. When select's read mask returns that that descriptor is ready, then you do a FIONREAD ioctl to discover exactly how many bytes are waiting for you on that file descriptor. Having got that, you can do a read(2) syscall for just that many bytes, preferably on an O_NONBLOCK descriptor, although by now that should no longer be necessary.
Hm, here’s a foreboding note in /usr/src/usr.bin/vi/cl/README.signal:
Run in cbreak mode. There are two problems in this area. First, the
current curses implementations (both System V and Berkeley) don't give
you clean cbreak modes. For example, the IEXTEN bit is left on, turning
on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
the exception that flow control and signals are turned on, and curses
cbreak mode doesn't give you this.
We can either set raw mode and twiddle the tty, or cbreak mode and
twiddle the tty. I chose to use raw mode, on the grounds that raw
mode is better defined and I'm less likely to be surprised by a curses
implementation down the road. The twiddling consists of setting ISIG,
IXON/IXOFF, and disabling some of the interrupt characters (see the
comments in cl_init.c). This is all found in historic System V (SVID
3) and POSIX 1003.1-1992, so it should be fairly portable.
If you do a recursive grep for \b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\b on the non-kernel portions of /usr/src/, you should find stuff you can use. For example:
% grep -Pr '\b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\b' /usr/src/{{s,}bin,usr.{s,}bin,etc,gnu}
I would look at /usr/src/usr.bin/less/screen.c, down in the raw_mode() function. Riddled with ifdefs though it is in a quest for portability, that looks like the cleanest code for what you want to do. There’s also stuff lurking down in GNU.
OH MY, look in /usr/src/gnu/usr.bin/perl/h2pl/cbreak.pl! That must be my old code that I posted above. Interesting that it’s trickled out to every src system in the world. Scary, too, since it is twenty years out of date. Gosh, it's weird to see echoes of a younger self. Really hard to remember such particulars from 20 years ago.
I also see in /usr/src/lib/libcurses/term.h this line:
#define tcgetattr(fd, arg) ioctl(fd, TCGETA, arg)
in a bunch of ifdefs that are trying to infer termio or termios availability.
This should be enough to get you started.

Copy/Paste not working on a Linux based shell

After is searched a lot on the internet, am here in search of the solution for the below problem.
Thanks in advance for helping hands!
I have a Linux-based shell (not bash shell but similar) where am using a 3rd part code tinyrl (from clish/klish) to read the input from the shell.
Below is the function from tinyrl that reads the user input and returns it which is further used to display on the shell.
tinyrl_vt100_getchar(const tinyrl_vt100_t *this)
{
unsigned char c='\0';
int istream_fd = -1;
FILE *sfd = this->istream;
if (!sfd) return VT100_ERR;
istream_fd = fileno(sfd);
/* Just wait for the input if no timeout */
if (this->timeout <= 0) {
if ((c = getc(sfd)) == EOF) {
if (feof(sfd))
return VT100_EOF;
else
return VT100_ERR;
}
return c;
}
/* Set timeout for the select() */
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(istream_fd, &rfds);
struct timeval tv;
tv.tv_sec = this->timeout;
tv.tv_usec = 0;
int retval = -1;
while (((retval = select(istream_fd + 1, &rfds, NULL, NULL, &tv)) < 0) &&
(EAGAIN == errno));
/* Error or timeout */
if (retval < 0)
return VT100_ERR;
if (!retval)
return VT100_TIMEOUT;
if ((c = getc(sfd)) == EOF) {
if (feof(sfd))
return VT100_EOF;
else
return VT100_ERR;
}
return c;
}
Problem description
When an input is given by copy-pasting the content, only one character appears and after pressing any key (like an arrow, space, backspace, letter, etc) the remaining content appears on the shell.
Example:
If we have copied “abc def xyz” content, then when pasted on the shell it displays only “a” and when any key is pressed it displays remaining content.
The expectation is it should not be required to press any key to complete the paste operation.
The solution is I have already tried
Using read() in place of getc(). This actually resolves the copy-paste issue but it causes other problems that keystroke value like "[C, [D, etc" getting printed on the shell when the cursor is moved rapidly through the content we pasted on the shell.
So this solution is not useful.
Thanks!

Sampling keyboard input at a certain rate in C++

What I want to do is sampling the keyboard input at a certain rate (e.g. 10-20 Hz). I use a while loop that read from stdin (I use read because I want to read asynchronously, e.i. I don't want to press enter every time) and then I have a pause before a new cycle starts to keep the sampling frequency stable.
The user press left/right arrow to give a command (to a robot). If nothing is pressed, the output is 0.
The problem is that, during the pause, the stdin buffer is written (I suppose), and so the read will return an old input. The final result is that the output is delayed. So if I press left the output immediately change to 1, but when I release it takes some seconds to return to 0. I want to remove this delay.
My aim is to sample just the more recent key pressed, in order to synchronize user input and output command without delays. Is there a way? Thank you in advance.
This is the method I'm using:
void key_reader::keyLoop()
{
char c;
bool dirty = false;
int read_flag;
// get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
//FD_ZERO(&set); /* clear the set */
//FD_SET(kfd, &set); /* add our file descriptor to the set */
//timeout.tv_sec = 0;
//timeout.tv_usec = 10000;
if (fcntl(kfd, F_SETFL, O_NONBLOCK) == -1)
{
perror("fcntl:"); // an error accured
exit(-1);
}
puts("Reading from keyboard");
puts("---------------------------");
puts("Use arrow keys to move the turtle.");
ros::Rate r(10);
while (ros::ok())
{
read_flag = read(kfd, &c, 1);
switch (read_flag)
{
case -1:
// case -1: is empty and errono
// set EAGAIN
if (errno == EAGAIN)
{
//no input yet
direction = 0;
break;
}
else
{
perror("read:");
exit(2);
}
// case 0 means all bytes are read and EOF(end of conv.)
case 0:
//no input yet
direction = 0;
break;
default:
ROS_DEBUG("value: 0x%02X\n", c);
switch (c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
direction = 1;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
direction = -1;
dirty = true;
break;
}
}
continuos_input::input_command cmd;
cmd.type = "Keyboard";
cmd.command = direction;
cmd.stamp = ros::Time::now();
key_pub.publish(cmd);
r.sleep();
}
}
I feel that the issue is with your subscriber rather than publisher. I can see that you have used rate to limit the publishing rate to 10Hz. Do confirm the publishing rate using Topic Monitor in rqt. Also setting a lower queue size for the publisher might help. Can't give a more definitive answer without referring to your subscriber node.

Adding Verbosity to a Program

This is a very n00b question but
I'm writing a nix based tool and would like to have verbosity flags, based on the number of vvv's passed in I would go about printing debug/information statements in my program.
My question is how would I go about using opargs for this, since optargs can only parse one character at a time.
Also suppose I know I'm at verbosity level 3, do all my print statements have to be in an if condition? Or there a clever way of going about it using the pre-processor?
Also if someone could point me to some code online which does this, that would be awesome.
Thanks
I figure it out, thought I'd post here if someone else comes across this in the future:
Basically for all my different verbosity statements I defined a special print using the preprocessor like:
#define dprintf \
if (verbosity == 1) printf
I then put in the statements as needed in the code e.g.
dprintf ("Verbosity is at level 1.");
My opt atgs looks something like this
case 'v':
verbosity++;
break;
The verbosity level is not known at compile time so you need to have code ready to handle any level the user selects.
A simple, and easy to understand, way of doing this is to separate your logging functions in an opaque compilation unit with a static variable keeping track of the verbosity level. You then initialize this with something like "set_logging_level(level)" and write your logging functions guarded by this static variable. Then you only expose the initialization and the logging functions and use them as you need them in your code.
static level = 0;
void set_logging_level(int l) { level = l; }
void log_info(char* msg) {
// Will always print
}
void log_debug(char *msg) {
if(level > 0)
// Write to stdout or stderr, whichever fits
}
void log_details(char *msg) {
if(level > 1)
// As above
}
void log_insanity(char *msg) {
if(level > 2)
// As above
}
Edit: Saner conditions for logging. Especially if you want inclusive logging when the verbosity level goes up...
How about Conditional compilation?
You could also simplify by setting a number for verbose level instead of passing that many v's.
#if VERBOSE_LEVEL == 3
print("A verbose message");
#endif
I'm not too sure if this is what you've meant, but this is how I implemented it in another project:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define TRUE 1
#define FALSE 0
int usage( char *name, int quit );
int main (int argc, char **argv) {
int c;
static int vlevel = 0;
while ( (c = getopt(argc, argv, ":abc:d:hv012")) != -1) {
int this_option_optind = optind ? optind : 1;
switch (c) {
case 'v':
vlevel++;
printf ("verbosity level is %d\n", vlevel);
break;
case ':': /* Option without required operand */
fprintf(stderr, "option -%c requires an operand\n", optopt);
break;
case 'h':
case '?':
usage( argv[0], TRUE );
break;
default:
printf ("?? getopt returned character code 0%o ??\n", c);
}
}
if (optind < argc) {
printf ("non-option ARGV-elements:\n");
while (optind < argc)
printf ("\t%s\n", argv[optind++]);
}
exit (0);
}
int usage( char *progname, int quit )
{
printf ( "Usage:\n\t%s [-vh]\n", progname );
if ( quit ) exit( 1 );
return 0;
}
This would give you something like the following:
eroux#smaug:~$ ./testverbose -h
Usage:
./testverbose [-vh]
eroux#smaug:~$ ./testverbose -vvvv
verbosity level is 1
verbosity level is 2
verbosity level is 3
verbosity level is 4
eroux#smaug:~$
From there you should be able to use the vlevel variable [in main()] to print the correct message during the relevant verbosity level.

Non-blocking keyboard read - C/C++

I got this following function with me working now. But what I need to improve is that it would read input from the keyboard (on the terminal) EVEN THOUGH IT IS NOT BEING PRESSED. I need to know when it is NOT pressed (idle) so that the switch case block will fall into the default section. At this point, the read() function waits until there's an input from the user. Can anyone give a suggestion just based on modifying this following code?
NOTE: I'm a Java programmer, and still learning C/C++ so it might be hard to get in my head a little bit. Thanks guys..
EDIT: I found this link, and seems to have something related to what i'm looking for at the line of fcntl(STDIN_FILENO,F_SETFL,flags | O_NONBLOCK); . But since I barely know anything in C, I completely have no Idea what it's saying, yet.
http://www.codeguru.com/forum/showthread.php?t=367082
int kfd = 0;
struct termios cooked, raw;
char c;
bool dirty = false;
//get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &=~ (ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
puts("Reading from keyboard");
puts("=====================");
puts("Use arrow keys to navigate");
while(true){
//get the next event from the keyboard
if(read(kfd, &c, 1) < 0)
{
perror("read():");
exit(-1);
}
linear_ = angular_ = 0;
ROS_DEBUG("value: 0x%02X\n", c);
switch(c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
angular_ = -1.0;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
angular_ = 1.0;
dirty = true;
break;
case KEYCODE_U:
ROS_DEBUG("UP");
linear_ = 1.0;
dirty = true;
break;
case KEYCODE_D:
ROS_DEBUG("DOWN");
linear_ = -1.0;
dirty = true;
break;
default:
ROS_DEBUG("RELEASE");
linear_ = 0;
angular_ = 0;
dirty = true;
break;
}
The OP seems to have answered their question:
I think I solved my problem. Please, anyone, verify and let me know if this is the right way to do it, or is it the complete way to do it (am I missing any other addition step e.g. resetting it back again -if that even makes sense) .
So what i found is to add this 3 lines before entering the while loop:
flags = fcntl(0, F_GETFL, 0); /* get current file status flags */
flags |= O_NONBLOCK; /* turn off blocking flag */
fcntl(0, F_SETFL, flags); /* set up non-blocking read */