|
|
发表于 2005-7-11 11:34:54
|
显示全部楼层
from beginning linux programming
=----------------
Detecting Keystrokes
People who have programmed MS-DOS often look for the Linux equivalent of the kbhit function,
which detects whether a key has been pressed without actually reading it. Unfortunately, they fail to
find it, since there’s no direct equivalent. UNIX programmers don’t notice the omission because UNIX is
normally programmed in such a way that programs should rarely, if ever, busy-wait on an event. Since
this is the normal use for kbhit, it’s rarely missed on UNIX and Linux.
However, when you’re porting programs from MS-DOS, it’s often convenient to emulate kbhit, which
you can do using the non-canonical input mode.
Try It Out—Your Very Own kbhit
1. We begin with the standard headings and declare a couple of structures for the terminal settings.
peek_character is used in the test of whether a key has been pressed. Then we prototype the
functions we’ll be using later.
#include <stdio.h>
#include <termios.h>
#include <term.h>
#include <curses.h>
#include <unistd.h>
static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard();
void close_keyboard();
int kbhit();
int readch();
2. The main function calls init_keyboard to configure the terminal, then loops once a second,
calling kbhit each time it does. If the key hit is q, close_keyboard returns the behavior to
normal and the program exits.
int main()
{
int ch = 0;
init_keyboard();
while(ch != ‘q’) {
printf(“looping\n”);
sleep(1);
if(kbhit()) {
ch = readch();
printf(“you hit %c\n”,ch);
}
}
close_keyboard();
exit(0);
}
3. init_keyboard and close_keyboard configure the terminal at the start and end of the
program.
void init_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);
}
void close_keyboard()
{
tcsetattr(0, TCSANOW, &initial_settings);
}
4. Now for the function that checks for the keyboard hit:
int kbhit()
{
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;
}
5. The character pressed is read by the next function, readch, which then resets peek_character
to –1 for the next loop.
int readch()
{
char ch;
if(peek_character != -1) {
ch = peek_character;
peek_character = -1;
return ch;
}
read(0,&ch,1);
return ch;
}
When we run the program, we get
$ ./kbhit
looping
looping
looping
you hit h
looping
looping
looping
you hit d
looping
you hit q
$
How It Works
The terminal is configured in init_keyboard to read one character before returning (MIN=1, TIME=0).
kbhit changes this behavior to check for input and return immediately (MIN=0, TIME=0) and then
restores the original settings before exiting.
Notice that we have to read the character that has been pressed but that we store it locally, ready for
returning when it’s required. |
|