Home > Archive > Unix Programming > March 2008 > using keyboard arrows
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
using keyboard arrows
|
|
| Brice Rebsamen 2008-03-13, 8:16 am |
| Hi
I am trying to write a small application to control a mobile robot
from the keyboard. What I really need is to handle multiple keys
pressed at one time. If it's too difficult with arrow keys I don't
mind using another combination like WSAD.
I tried doing it from a console application using termios related
stuffs, taking as example showkey from the kbd package, but I didn't
manage to handle 2 keys at one time. I tried to distinguish between
press and release state though. Besides the console keeps scrolling
down very fast, so it's not easy to debug. Plus it requires super user
privileges to open tty0. The code is at the end of this message.
I don't mind using curse if it does the job... Portability is not an
issue in that case. I have never used ncurse before; reading their
documentation I could not find a proof that ncurse would handle
multiple keys pressed at one time.
Any idea of what would be a good way of doing this?
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/keyboard.h>
int fd=-1, oldkbmode=-1;
struct termios orig_kb;
float linvel=0, rotvel=0;
void initKbd(){
struct termios newkbd;
fd = open("/dev/tty0", O_RDONLY);
tcgetattr ( fd, &orig_kb );
tcgetattr ( fd, &newkbd );
newkbd.c_lflag &= ~ (ECHO | ICANON I ISIG);
newkbd.c_iflag = 0;
newkbd.c_cc[VMIN] = 18;
newkbd.c_cc[VTIME] = 1;
tcsetattr ( fd, TCSAFLUSH, &newkbd );
ioctl ( fd, KDGKBMODE, &oldkbmode );
ioctl ( fd, KDSKBMODE, K_MEDIUMRAW );
atexit(clean_up);
}
void clean_up ( void ) {
ioctl ( fd, KDSKBMODE, oldkbmode );
tcsetattr ( fd, 0, &orig_kb );
close ( fd );
}
void * kbdThFunc(void *dummy){
int keycode, pressed, controlPressed=0;
float linvel=0,rotvel=0;
while( 1 ){
unsigned char buf[18]; /* divisible by 3 */
int n = read ( fd, buf, sizeof ( buf ) );
int i = 0;
while ( i < n ) {
if ( i+2 < n && ( buf[i] & 0x7f ) == 0
&& ( buf[i+1] & 0x80 ) != 0 && ( buf[i+2] & 0x80 ) != 0 ) {
keycode = ( ( buf[i+1] & 0x7f ) << 7 ) | ( buf[i+2] & 0x7f );
i += 3;
}
else keycode = ( buf[i++] & 0x7f );
pressed = (buf[i] & 0x80) ? 0 : 1;
}
switch ( keycode ) {
case 103: //UP ARROW
case 'w':
if( pressed ) linvel = 1;
else linvel = 0;
break;
case 108: //DOWN ARROW
case 's':
if( pressed ) linvel = -1;
else linvel = 0;
break;
case 105: //LEFT ARROW
case 'a':
if( pressed ) rotvel = -1;
else linvel = 0;
break;
case 106: //RIGHT ARROW
case 'd':
if( pressed ) rotvel = 1;
else linvel = 0;
break;
case 29: //CTRL
controlPressed = pressed;
break;
case 46: //C
if( controlPressed && pressed ) exit(0);
break;
}
}
return NULL;
}
int main(){
pthread_t tid;
initKbd();
pthread_create(&tid,NULL,kbdThFunc,NULL);
while(1){
printf("%f %f\n",linvel,rotvel);
sleep(1);
}
return 0;
}
| |
| Kalle Olavi Niemitalo 2008-03-13, 8:16 am |
| Brice Rebsamen <brice.rebsamen@gmail.com> writes:
> I tried doing it from a console application using termios related
> stuffs, taking as example showkey from the kbd package, but I didn't
> manage to handle 2 keys at one time.
The inner loop in kbdThFunc seems wrong. If one read() call
returns with multiple keycodes in the buffer, you should process
all of them. Now you instead store them to the keycode and
pressed variables one at a time and then only use the last.
Also, you're doing this:
> else keycode = ( buf[i++] & 0x7f );
> pressed = (buf[i] & 0x80) ? 0 : 1;
i.e. the values of keycode and pressed are coming from different
elements of buf. That seems wrong too.
> Plus it requires super user privileges to open tty0.
If you just used stdin or /dev/tty, then it'd work without
superuser privileges, provided the program is actually running
directly on a virtual console and not e.g. in Screen or xterm.
You might also consider making an X program and then getting the
keypresses and releases as X events. That would even be
portable. Try with xev for example. If you want things to work
both in the console and in X, there's libsdl for that.
> I don't mind using curse if it does the job... Portability is not an
> issue in that case. I have never used ncurse before; reading their
> documentation I could not find a proof that ncurse would handle
> multiple keys pressed at one time.
Curses won't help.
| |
| Brice Rebsamen 2008-03-13, 8:16 am |
| > > Plus it requires super user privileges to open tty0.
>
> If you just used stdin or /dev/tty, then it'd work without
> superuser privileges
How to use stdin or /dev/tty?
I tried:
fd = fileno(stdin);
// fd = open("/dev/tty", O_RDONLY)
tcgetattr ( fd, &orig_kb );
tcgetattr ( fd, &newkbd );
newkbd.c_lflag &= ~ (ECHO | ICANON | ISIG | IEXTEN);
newkbd.c_iflag = 0;
newkbd.c_cc[VMIN] = 18;
newkbd.c_cc[VTIME] = 1;
tcsetattr ( fd, TCSAFLUSH, &newkbd );
ioctl ( fd, KDGKBMODE, &oldkbmode );
The last call fails with error code EINVAL
> provided the program is actually running
> directly on a virtual console and not e.g. in Screen or xterm.
I am using the KDE terminal Konsole. Is it an xterm or a virtual
console or ... ?
> You might also consider making an X program and then getting the
> keypresses and releases as X events. That would even be
> portable. Try with xev for example. If you want things to work
> both in the console and in X, there's libsdl for that.
I took a quick look at libsdl. It looks promising. If I can't fix my
current code I'll give it a shot.
Thanks for your help.
| |
| Edward Rosten 2008-03-13, 7:52 pm |
| On Mar 12, 11:10 pm, Brice Rebsamen <brice.rebsa...@gmail.com> wrote:
> Hi
>
> I am trying to write a small application to control a mobile robot
> from the keyboard. What I really need is to handle multiple keys
> pressed at one time. If it's too difficult with arrow keys I don't
> mind using another combination like WSAD.
> Any idea of what would be a good way of doing this?
Using X11 might be more suitable. Try this little program:
#include <X11/Xlib.h>
#include <stdio.h>
int main()
{
Display* d = XOpenDisplay(0);
Window w, root;
root = RootWindow(d, DefaultScreen(d));
w = XCreateSimpleWindow(d, root, 100, 100, 640, 480, 0, 0, 0);
XSelectInput(d, w, KeyPressMask | KeyReleaseMask);
XMapWindow(d, w);
XEvent e;
for(;;)
{
XNextEvent(d, &e);
if(e.type == KeyPress || e.type == KeyRelease)
{
KeySym k;
k = XLookupKeysym(&(e.xkey), e.xkey.state);
printf("keycode=%i key=%s %s\n", e.xkey.keycode,
XKeysymToString(k), e.type == KeyPress ?"pressed":"released");
}
}
}
It probably does a goot bit of what you want with regards to key
presses. Note that XNextEvent is a blocking call, which you probably
don't want. However, the non-blocking call XPending(d) tells you if
XNextEvent will block.
-Ed
--
(You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
/d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
| |
| Brice Rebsamen 2008-03-14, 4:44 am |
| On Mar 14, 3:53 am, Edward Rosten <Edward.Ros...@gmail.com> wrote:
> On Mar 12, 11:10 pm, Brice Rebsamen <brice.rebsa...@gmail.com> wrote:
>
>
>
> Using X11 might be more suitable. Try this little program:
>
> #include <X11/Xlib.h>
> #include <stdio.h>
>
> int main()
> {
> Display* d = XOpenDisplay(0);
> Window w, root;
> root = RootWindow(d, DefaultScreen(d));
> w = XCreateSimpleWindow(d, root, 100, 100, 640, 480, 0, 0, 0);
> XSelectInput(d, w, KeyPressMask | KeyReleaseMask);
> XMapWindow(d, w);
>
> XEvent e;
> for(;;)
> {
> XNextEvent(d, &e);
>
> if(e.type == KeyPress || e.type == KeyRelease)
> {
> KeySym k;
> k = XLookupKeysym(&(e.xkey), e.xkey.state);
> printf("keycode=%i key=%s %s\n", e.xkey.keycode,
> XKeysymToString(k), e.type == KeyPress ?"pressed":"released");
> }
> }
>
> }
>
> It probably does a goot bit of what you want with regards to key
> presses. Note that XNextEvent is a blocking call, which you probably
> don't want. However, the non-blocking call XPending(d) tells you if
> XNextEvent will block.
>
> -Ed
> --
> (You can't go wrong with psycho-rats.)(http://mi.eng.cam.ac.uk/~er258)
>
> /d{def}def/f{/Times s selectfont}d/s{11}d/r{roll}d f 2/m{moveto}d -1
> r 230 350 m 0 1 179{ 1 index show 88 rotate 4 mul 0 rmoveto}for/s 12
> d f pop 235 420 translate 0 0 moveto 1 2 scale show showpage
this code is great and is a very good solution to my problem. Can you
just tell me how to map a key to an action. I am using switch case
block like this:
#include <X11/Xlib.h>
#include <X11/keysym.h>
switch ( e.xkey.keycode ) {
case XK_Up: case XK_W: case XK_w: case XK_KP_Up: case XK_KP_8:
linvel = e.type==KeyPress ? 1 : 0;
break;
case XK_Down: case XK_S: case XK_s: case XK_KP_Down: case
XK_KP_2:
linvel = e.type==KeyPress ? -1 : 0;
break;
case XK_Left: case XK_A: case XK_a: case XK_KP_Left: case
XK_KP_4:
rotvel = e.type==KeyPress ? -1 : 0;
break;
case XK_Right: case XK_D: case XK_d: case XK_KP_Right: case
XK_KP_6:
rotvel = e.type==KeyPress ? 1 : 0;
break;
}
But it does not work. How can I know which key is pressed?
| |
| Brice Rebsamen 2008-03-14, 4:44 am |
| On Mar 13, 4:49 pm, Kalle Olavi Niemitalo <k...@iki.fi> wrote:
> Brice Rebsamen <brice.rebsa...@gmail.com> writes:
>
> The inner loop in kbdThFunc seems wrong. If one read() call
> returns with multiple keycodes in the buffer, you should process
> all of them. Now you instead store them to the keycode and
> pressed variables one at a time and then only use the last.
> Also, you're doing this:
>
>
> i.e. the values of keycode and pressed are coming from different
> elements of buf. That seems wrong too.
>
>
> If you just used stdin or /dev/tty, then it'd work without
> superuser privileges, provided the program is actually running
> directly on a virtual console and not e.g. in Screen or xterm.
>
> You might also consider making an X program and then getting the
> keypresses and releases as X events. That would even be
> portable. Try with xev for example. If you want things to work
> both in the console and in X, there's libsdl for that.
>
>
> Curses won't help.
I fixed the keycode interpretation loop. Now it's working properly.
There is still one problem: the terminal keeps scrolling down, as if I
was continuously pressing down the ENTER key. How can I suppress that?
| |
| Kalle Olavi Niemitalo 2008-03-14, 4:44 am |
| Brice Rebsamen <brice.rebsamen@gmail.com> writes:
> I am using the KDE terminal Konsole. Is it an xterm or a virtual
> console or ... ?
If you're going to run the program in KDE, thus X, you should
certainly use the facilities of X rather than ioctl KDSKBMODE.
That way, your program also does not hog the whole keyboard,
rather it only gets the events meant for its own X window.
It isn't terribly hard to implement this with Xlib.
Perhaps it is even easier with libsdl; I don't know.
| |
| Kalle Olavi Niemitalo 2008-03-14, 4:44 am |
| Brice Rebsamen <brice.rebsamen@gmail.com> writes:
> switch ( e.xkey.keycode ) {
> case XK_Up: case XK_W: case XK_w: case XK_KP_Up: case XK_KP_8:
These XK_ constants are keysyms, not keycodes. So you should
compare them to the values returned by XLookupKeysym, not to
e.xkey.keycode.
I suppose it's also possible to examine the keymap beforehand,
make a list of the interesting keycodes, and then use that. It
would be more difficult however, especially with the more complex
keymaps made possible by the X Keyboard Extension (XKB).
| |
| Brice Rebsamen 2008-03-14, 8:14 am |
| On Mar 14, 3:45 pm, Kalle Olavi Niemitalo <k...@iki.fi> wrote:
> Brice Rebsamen <brice.rebsa...@gmail.com> writes:
>
> These XK_ constants are keysyms, not keycodes. So you should
> compare them to the values returned by XLookupKeysym, not to
> e.xkey.keycode.
>
> I suppose it's also possible to examine the keymap beforehand,
> make a list of the interesting keycodes, and then use that. It
> would be more difficult however, especially with the more complex
> keymaps made possible by the X Keyboard Extension (XKB).
Ok, it works well. And it's definitely better than the ioctl
KDSKBMODE: there is no risk that I mess my keyboard anymore. And the
code is so much simpler.
Thanks guys.
|
|
|
|
|