|
_kbhit() for Linux
Submitted by |
The Windows _kbhit()
function returns a nonzero value when a character is waiting in stdin,
otherwise it returns zero. It does not consume the character and does
not block. A common use of this function is to test when the user has
pressed a key in an interactive console application. POSIX (and
therefore Linux) lacks a similar function. Although it does not
directly support this functionality, the GNU Curses library can
be used to implement _kbhit() on Linux. Here I present an
alternate solution to Curses that implements _kbhit() using
only standard libraries. It should port directly to OS X, AIX, and
other Unix-like operating systems as well as Linux. This
implementation has two advantages over a Curses based approach. The
Curses library must be initialized from main() before it can be used.
This implementation is a drop-in replacement for _kbhit() when
porting from Windows and does not require any explicit initialization.
Also, no external library must be installed and linked.
The ioctl()
function is a low level method for controlling I/O drivers. Its
arguments depend on the stream and driver being used. The last group
of lines in _kbhit() uses this function to determine whether data
is waiting on stdin. This implementation was written specifically for
Linux and may not port. A more general implementation can replace these lines
with a call to the select()
function as follows:
timeval timeout;
fd_set rdset;
FD_ZERO(&rdset);
FD_SET(STDIN, &rdset);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
return select(STDIN + 1, &rdset, NULL, NULL, &timeout); |
Console input is typically line buffered on Linux, particularly when
running over Telnet or SSH. This means that a keypress does not
appear on stdin until a newline character is sent. The ioctl()
or select() calls cannot determine if characters are in the
buffer waiting for a newline, and can indicate that there are zero
characters waiting when really several keys have been pressed.
To fix this, the first code block in _kbhit() disables line
buffering. This uses routines from the termios.h
header. Another author offers a longer
method that uses only ioctl() and avoids termios.h.
Because termios.h is a standard header on most systems I see no reason
to avoid it. Both implementations use a static variable to detect the
first call and disable buffering then. Output buffering on stdout is
still enabled. If you wish to print to stdout and see the result
before a newline is sent, use the command flush(stdout) as
shown in the simple demo.
The Linux version of _kbhit() now performs to the same
specification as the Windows version. The actual value of the
non-zero integer returned will be different on the two platforms,
however.
Morgan McGuire
|
Download Associated File: kbhit.txt (922 bytes)
/**
Linux (POSIX) implementation of _kbhit().
Morgan McGuire, morgan@cs.brown.edu
*/
#include <stdio.h>
#include <sys/select.h>
#include <termios.h>
#include <stropts.h>
int _kbhit() {
static const int STDIN = 0;
static bool initialized = false;
if (! initialized) {
// Use termios to turn off line buffering
termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initialized = true;
}
int bytesWaiting;
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
//////////////////////////////////////////////
// Simple demo of _kbhit()
#include <unistd.h>
int main(int argc, char** argv) {
printf("Press any key");
while (! _kbhit()) {
printf(".");
fflush(stdout);
usleep(1000);
}
printf("\nDone.\n");
return 0;
} |
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|