From: dps@delhi.esd.sgi.com (D.P. Suresh)
Newsgroups: comp.unix.bsd,comp.lang.c
Subject: Re: a unix terminal question
Date: 11 Jan 93 22:48:02 GMT
Organization: Silicon Graphics, Inc.  Mountain View, CA


In article <1993Jan11.215312.2080@fcom.cc.utah.edu>, terry@cs.weber.edu writes:
> 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.

select() or poll() in itself will not get you the desired result.
If my memory of DOS does not fail me, kbhit() is a non-blocking call.
It just tells you *at once* whether zero or more keys are waiting for
his/her highness to be read. To get this effect on UNIX, one should
combine select()/poll() with putting the terminal in raw mode.

If you dont put the terminal in raw mode and try select()/poll()
then even if you press a key, select()/poll() will not detect them
until CR is given.

Here is a code sample:

/* Test code for kbhit function */
#include <stdio.h>
#include <termio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>

kbhit()
{
	fd_set readfds, writefds, exceptfds;
	struct timeval timeout;
	static struct termio    otty, ntty;
	int ret;

	/* Create proper environment for select() */
	FD_ZERO( &readfds );
	FD_ZERO( &writefds );
	FD_ZERO( &exceptfds );
	FD_SET( fileno(stdin), &readfds );

	/* We shall specify 0.5 sec as the waiting time */
	timeout.tv_sec  = 0;	/*   0 seconds */
	timeout.tv_usec = 500;	/* 500 microseconds */

	/* Put tty in raw mode */
	ioctl(fileno(stdout), TCGETA, &otty);
	ntty = otty;
	ntty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
	ntty.c_lflag &= ~ICANON;
	ntty.c_lflag |= ISIG;
	ntty.c_cflag &= ~(CSIZE|PARENB);
	ntty.c_cflag |= CS8;
	ntty.c_iflag &= (ICRNL|ISTRIP);
	ntty.c_cc[VMIN] = ntty.c_cc[VTIME] = 1;
	ioctl(fileno(stderr), TCSETAW, &ntty);

	/* Do a select */
	ret = select( 1, &readfds, &writefds, &exceptfds, &timeout );

	/* Reset the tty back to its original mode */
	ioctl(fileno(stderr), TCSETAW, &otty);

	return( ret );
}

main()
{
	while( !kbhit() ) {
		/* No key was hit. Do your own processing. */
	}
	printf("Hey. you hit a key.\n");
}

/* End of listing */


WARNING:
This feature should be used wisely. One could incur quite a lot of
performance penalties as expressed by terry.

--
D.P.Suresh
dps@esd.sgi.com
Newsgroups: comp.unix.bsd