From: terry@cs.weber.edu (A Wizard of Earth C)
Subject: Re: a unix terminal question
Organization: Weber State University  (Ogden, UT)
Date: Mon, 11 Jan 93 21:53:12 GMT

In article <1iql6kINNisk@ub.d.umn.edu> cbusch@ub.d.umn.edu (Chris) writes:
>
>   How does one read in a character from standard input without having
>the program wait for the key.  Basically, I want to do something like:
>    if(kbhit()) c=getch();
>Except that is not standard, and I want it to work on all platforms.

This is a bad thing to do, unless you have processing to do when characters
aren't present, and you do your checks relatively infrequently compared to
the procesing itself; otherwise, you will be in a buzz-loop and suck your
CPU through the floor.  This is common practice under DOS where there is
nothing else running, but is a generally bad thing to do.

The CORRECT way to do this:  use the select() or poll() system call to
wait for an interval or a character to be present.  Resoloution is
generally 1/1000th of a second; you can effect a poll by having a
zero-valued timeval struct (as opposed to passing (struct timeval *)NULL).
This will cause the behaviour you have asked for.  When the select()
returns that there are characters available, do a read() on the descriptor
(otherwise, return as if the read() has returned 0 characters).  It
should be noted that some systems are sensitive to select/read pairing,
and if you do this, you should have a select() call prior to every read()
call, regardless of whther or not you use the select information aro simply
read anyway.

Another method would be to put the terminal in raw mode, and set the vmin
to 0 and the vtime based on whether you wanted to screw everyone on your
system every time your program is run (set it to 0 to poll and screw
everyone).  Normal reads will return 0 characters read.

An older mechanism for BSD systems which is no longer in common use is
the rdchk() library routine, which ioctl'ed with a FIONREAD to count
the number of characters pending input (this is still used in SCO Xenix).
If the number was larger than 0, then you do the read.

In general, these mechanisms are not usable with stdio routines.  This is
because the stdio routines operate on user-space buffering.  If I am using
"getchar()" to read characters, and I typ the string "fred", if I type
it fast enough, it will be read into the stdin buffer in its entirety.
When I check the number of characters pending input on the fd, the answer
will be 0, even though there are still 3 characters pending input on the
stdin file pointer.  You can either check that the number of characters
in the stdin buffer is not 0 (extremely system dependant), then check to
see if there are no characters on the fd, and only then skip your getchar.
For portability, you should use raw I/O (read/write/select) rather than
character counting the stdio buffer contents.

If the reason you are doing this is a game, you are probably SOL: you
will eat the machine when you run it.  Even then, using a timeout on
select for the background processing will allow you to adjust the
speed of the background tasks relative to the player's movement,
and will not eat the machine in the majority case.  This also makes
your game timing dependent on your players use/nonuse of the control
entry window rather than on whther or not the player is doing something
(ie: it's bad if by entering commands I cause the game to become "jerky"
in it's movements).

If the reason you need to do this is to read from two file descriptors
at once, then select() or poll() is your best bet (there are other
methods to do this as well, but select/poll are more efficient in terms
of system call overhead).  You may want to do this if you are writing a
terminal emulation or chat program.

Good luck.


					Terry Lambert
					terry@icarus.weber.edu
					terry_lambert@novell.com
---
Any opinions in this posting are my own and not those of my present
or previous employers.
-- 
-------------------------------------------------------------------------------
                                        "I have an 8 user poetic license" - me
 Get the 386bsd FAQ from agate.berkeley.edu:/pub/386BSD/386bsd-0.1/unofficial
-------------------------------------------------------------------------------