Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

low level keyboard read

0 views
Skip to first unread message

Thomas

unread,
Aug 22, 2002, 2:46:37 AM8/22/02
to
Hi all,
I need a function in C to scan the PC-keyboard in a non-blocking fashion: If
no key is pressed the function should just continue returning NULL or alike.
It is OK if I can only read one char at a time.

Thanks in advance

Thomas


Kevin Easton

unread,
Aug 22, 2002, 2:55:01 AM8/22/02
to

Check out the 'ncurses' API. It contains routines to do most things an
interactive textmode program would want, including your request
(cbreak() and getch()). You almost certainly have the runtime libraries
installed already (bash(1) requires it, among many other things) - just
get the devel files and you'll be set.

- Kevin.

Kasper Dupont

unread,
Aug 22, 2002, 3:25:49 AM8/22/02
to

http://www.daimi.au.dk/~kasperd/comp.os.linux.development.faq.html

--
Kasper Dupont -- der bruger for meget tid på usenet.
For sending spam use mailto:aaa...@daimi.au.dk
or mailto:mcxumhv...@skrammel.yaboo.dk

Floyd Davidson

unread,
Aug 22, 2002, 4:46:11 AM8/22/02
to

You could just use raw mode, but putting that in a loop will make your
process into a cpu hog. Below is a demo program which shows one way to
accomplish what you are asking about.

/*
* kbhit(), a keyboard lookahead monitor
* getch(), a blocking single character input from stdin
*
* Plus a demo main() to illustrate usage.
*/

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>

int getch(void);
int kbhit(void);

#define CMIN 1

#ifdef CTIME
#undef CTIME
#endif

#define CTIME 1

/*
* kbhit() -- a keyboard lookahead monitor
*
* returns the number of characters available to read.
*/
int
kbhit(void)
{
int cnt = 0;
int error;
static struct termios Otty, Ntty;

tcgetattr(0, &Otty);
Ntty = Otty;

Ntty.c_iflag = 0; /* input mode */
Ntty.c_oflag = 0; /* output mode */
Ntty.c_lflag &= ~ICANON; /* raw mode */
Ntty.c_cc[VMIN] = CMIN; /* minimum chars to wait for */
Ntty.c_cc[VTIME] = CTIME; /* minimum wait time */

if (0 == (error = tcsetattr(0, TCSANOW, &Ntty))) {
struct timeval tv;
error += ioctl(0, FIONREAD, &cnt);
error += tcsetattr(0, TCSANOW, &Otty);
tv.tv_sec = 0;
tv.tv_usec = 100; /* insert a minimal delay */
select(1, NULL, NULL, NULL, &tv);
}

return (error == 0 ? cnt : -1 );
}

/*
* getch() -- a blocking single character input from stdin
*
* Returns a character, or -1 if an input error occurs.
*
* Conditionals allow compiling with or without echoing of
* the input characters, and with or without flushing pre-existing
* existing buffered input before blocking.
*
*/
int
getch(void)
{
char ch;
int error;
static struct termios Otty, Ntty;

fflush(stdout);
tcgetattr(0, &Otty);
Ntty = Otty;

Ntty.c_iflag = 0; /* input mode */
Ntty.c_oflag = 0; /* output mode */
Ntty.c_lflag &= ~ICANON; /* line settings */

#if 1
/* disable echoing the char as it is typed */
Ntty.c_lflag &= ~ECHO; /* disable echo */
#else
/* enable echoing the char as it is typed */
Ntty.c_lflag |= ECHO; /* enable echo */
#endif

Ntty.c_cc[VMIN] = CMIN; /* minimum chars to wait for */
Ntty.c_cc[VTIME] = CTIME; /* minimum wait time */

#if 1
/*
* use this to flush the input buffer before blocking for new input
*/
#define FLAG TCSAFLUSH
#else
/*
* use this to return a char from the current input buffer, or block if
* no input is waiting.
*/
#define FLAG TCSANOW

#endif

if (0 == (error = tcsetattr(0, FLAG, &Ntty))) {
error = read(0, &ch, 1 ); /* get char from stdin */
error += tcsetattr(0, FLAG, &Otty); /* restore old settings */
}

return (error == 1 ? (int) ch : -1 );
}


/*
* a cutsie main() to demo how getch() and kbhit() work.
*/

#include <ctype.h>

int
main(void)
{
int ch, cnt;
static struct termios Otty, Ntty;

tcgetattr(0, &Otty);
Ntty = Otty;
Ntty.c_lflag &= ~ECHO;

printf("You must enter 10 characters to get\nthis program to continue: ");
fflush(stdout);
/* collect 10 characters */
while (1) {
if (10 <= (cnt = kbhit())) {
break;
}
}

tcsetattr(0, TCSANOW, &Ntty); /* disable echoing of further input */
printf("\nSTOP!");
fflush(stdout);
printf("\nNow type <Enter> to continue!");
fflush(stdout);
ch = getchar();
tcsetattr(0, TCSANOW, &Otty); /* enable echoing of further input */
printf("\n\nThe first five characters are: \"");
/*
* print a few chars, note that calling getch() will flush
* remaining buffered input.
*/
cnt = 4;
do {
printf("%c", ch);
ch = getchar();
} while (--cnt);
printf("%c\"\n\n", ch);

printf("\n\n *** Demo Menu ***\n\n");
printf(" Option Action\n");
printf(" A Exit and print an A word\n");
printf(" B Exit and print a B word\n");
printf(" C Exit and print a C word\n");
printf("\n Enter your choice: ?\b");
fflush(stdout);

while(1) {
switch (ch = getch()) {
case 'a': case 'A':
printf("%c\n\nThat is an *awesome* function!\n", toupper(ch));
break;
case 'b': case 'B':
printf("%c\n\nThat is a *beneficial* function!\n", toupper(ch));
break;
case 'c': case 'C':
printf("%c\n\nThat is a *critical* function!\n", toupper(ch));
break;
default:
continue;
}
break;
}
return 0;
}


--
Floyd L. Davidson <http://www.ptialaska.net/~floyd>
Ukpeagvik (Barrow, Alaska) fl...@barrow.com

Norm Dresner

unread,
Aug 22, 2002, 9:09:59 AM8/22/02
to
Floyd Davidson <fl...@ptialaska.net> wrote in message
news:87znvfw...@barrow.com...

> "Thomas" <notsodi...@hotmail.com> wrote:
> >Hi all,
> >I need a function in C to scan the PC-keyboard in a non-blocking
fashion: If
> >no key is pressed the function should just continue returning NULL or
alike.
> >It is OK if I can only read one char at a time.
> >
> >Thanks in advance
> >
> >Thomas
>
> You could just use raw mode, but putting that in a loop will make your
> process into a cpu hog.


Not if you insert an appropriate delay function which yields the CPU
back to the system. I've found that most users won't detect a delay on the
order of 100-200 milliseconds.

Norm

Floyd Davidson

unread,
Aug 22, 2002, 10:54:45 AM8/22/02
to
"Norm Dresner" <nd...@worldnet.att.net> wrote:

>Floyd Davidson <fl...@ptialaska.net> wrote:
>> You could just use raw mode, but putting that in a loop will make your
>> process into a cpu hog.
>
>
> Not if you insert an appropriate delay function which yields the CPU
>back to the system. I've found that most users won't detect a delay on the
>order of 100-200 milliseconds.

If that were the only benefit, you don't need anything like 200
ms of delay (BTW, it is generally considered that 250-300ms is
the max that will not be annoying to users). Even the minimum
granularity of something like usleep() will be enough, hence
usleep(1) will cause the process to give up the cpu to a context
switch and avoid the most unsocial/obnoxious characteristics of
a cpu hog.

But usually it is not just the user's perception of keyboard
latency that is important, and while in this case the OP did not
indicate what the intended purpose was, it is usually safe to
assume that the reason non-blocking input is needed is to allow
other processing to take place while still watching for a
keyboard event. If that processing is to be intense and/or
speedy, any intentional delay inserted is unwelcome.

Which all leads up to a suggestion for modifying the code that
I posted. The kbhit() function, as posted, includes a delay.

int
kbhit(void)
{
...


if (0 == (error = tcsetattr(0, TCSANOW, &Ntty))) {
struct timeval tv;
error += ioctl(0, FIONREAD, &cnt);
error += tcsetattr(0, TCSANOW, &Otty);
tv.tv_sec = 0;
tv.tv_usec = 100;

select(1, NULL, NULL, NULL, &tv);
}

The call to select is essentially a minimal delay. It will
probably be the least delay that select() can produce. It is in
the code because one person who picked it up off the net (this
code has been posted dozens of times) used it with an empty
loop, and it misses keyboard events without the delay! So it
has a delay... but if it is used in a compute intensive loop
that is not empty, the delay code should be removed.

Hmmm... I should put a comment to that effect in the code.

0 new messages