termios and keyboard medium raw mode


B

Brice Rebsamen

Hi

I wrote the following program that reads the keyboard in medium raw
mode (keycode mode). Here is the initialization code, the full code is
at the end.

fd = open("/dev/tty0", O_RDONLY);
tcgetattr ( fd, &newkbd );
newkbd.c_lflag &= ~ (ECHO | ICANON | ISIG);
newkbd.c_iflag = 0;
newkbd.c_cc[VMIN] = 18;
newkbd.c_cc[VTIME] = 1;
tcsetattr ( fd, TCSAFLUSH, &newkbd );
ioctl ( fd, KDSKBMODE, K_MEDIUMRAW );

It's suppose to control the value of linvel and rotvel (linear and
rotational velocity) when the user presses the arrow keys or the WASD
keys. To handle multi keys at the same time, I set the value when the
user presses the key, and reset it to 0 on release.

However it doesn't work. Let say I press UP, then RIGHT while
maintaining UP pressed, then release UP while maintaining RIGHT
pressed, then release RIGHT. In that case, I receive the following
events: UP pressed, RIGHT pressed, Right released. UP released is
missing. What am I doing wrong? Can you suggest another way of doing
this?

Also, the terminal keeps scrolling down, as if I was constantly
pressing ENTER. Why? Besides, I have to run this code as super user,
which is not really nice...

I am using linux, kernel 2.6.22 and gcc. Portability is not an issue.



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/keyboard.h>

int fd=-1, oldkbmode=K_RAW;
struct termios orig_kb;
float linvel=0, rotvel=0;

void clean_up ( void ) {
ioctl ( fd, KDSKBMODE, oldkbmode );
tcsetattr ( fd, 0, &orig_kb );
close ( fd );
}

void printVel(int i) {
printf("%f %f\n", linvel, rotvel);
alarm(1);
}

int main(){
int keycode, pressed, controlPressed=0;
unsigned char buf[18]; /* divisible by 3 */
int i,n;
struct termios newkbd;

/* Open and configure the keyboard */
fd = open("/dev/tty0", O_RDONLY);
tcgetattr ( fd, &orig_kb );
tcgetattr ( fd, &newkbd );
newkbd.c_lflag &= ~ (ECHO | ICANON | ISIG);
newkbd.c_iflag = 0;
newkbd.c_cc[VMIN] = 18;
newkbd.c_cc[VTIME] = 1;
tcsetattr ( fd, TCSAFLUSH, &newkbd );

/* Set medium raw mode: we receive keycodes. */
ioctl ( fd, KDGKBMODE, &oldkbmode );
ioctl ( fd, KDSKBMODE, K_MEDIUMRAW );

/* Restore the normal keyboard mode on exit */
atexit(clean_up);

/* Every 1 second, print the value of linvel and rotvel */
signal(SIGALRM, printVel);
alarm(1);

while( 1 ){
/* Wait until a key is pressed or released */
n = read ( fd, buf, sizeof ( buf ) );

/* Retrieve the key code and whether the key was pressed or
released */
i = 0;
while ( i < n ) {
pressed = (buf & 0x80)==0x80 ? 0 : 1;
if ( i+2 < n && ( buf & 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 );
}

/* Take appropriate action */
switch ( keycode ) {
case 103: //UP ARROW
case 17: //W
linvel = pressed ? 1 : 0;
break;
case 108: //DOWN ARROW
case 31: //S
linvel = pressed ? -1 : 0;
break;
case 105: //LEFT ARROW
case 30: //A
rotvel = pressed ? -1 : 0;
break;
case 106: //RIGHT ARROW
case 32: //D
rotvel = pressed ? 1 : 0;
break;
case 29: //CTRL
controlPressed = pressed;
break;
case 46: //C
if( controlPressed && pressed ) exit(0);
break;
}
}
return 0;
}
 
Ad

Advertisements

Ad

Advertisements

M

Mark Bluemel

Brice said:
Hi

I wrote the following program that reads the keyboard in medium raw
mode (keycode mode).
[Snip]

I am using linux, kernel 2.6.22 and gcc. Portability is not an issue.

I suggest you try a group appropriate to your platform, as this question
is about platform-specifics rather than the C language.

comp.unix.programmer would probably be a good start.
 

Top