Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!uunet!seismo!ut-sally!godzilla!hitchens
From: hitchens@godzilla.cs.utexas.edu (Ron Hitchens, Sun Wiz)
Newsgroups: comp.unix.questions
Subject: Re: Keyboard Input (Revised Question) ...
Message-ID: <8569@ut-sally.UUCP>
Date: Sun, 26-Jul-87 18:25:05 EDT
Article-I.D.: ut-sally.8569
Posted: Sun Jul 26 18:25:05 1987
Date-Received: Sun, 26-Jul-87 22:41:37 EDT
References: <1043@bucsb.bu.edu.UUCP>
Sender: news@ut-sally.UUCP
Reply-To: hitchens@godzilla.cs.utexas.edu (Ron Hitchens, Sun Wiz)
Organization: Central Texas Aphasic Institute
Lines: 107
Keywords: Keyboard
Summary: Use select()

In article <1043@bucsb.bu.edu.UUCP> eap@bucsb.UUCP (Eric Pearce) writes:
>I would like to write a routine that performs a repeated sequence of statements
>(i.e. a loop) that would check for input from the keyboard and do something
>according to what was typed in.  Also, it would be able to continue doing the
>loop regardless of whether or not anything was typed in from the keyboard.
>
>like this:
>
>    Begin Loop
>      check for keyboard input
>      if a key was pressed 
>                     case
>                         1) do something
>                         2) do something else
>                         ...
>                     esac 
>      fi
>      do some more stuff
>    End Loop
>
>Is there a way to do this in C?  Or maybe a different approach to the problem?
>(this is intended for a program on a mainframe)
>                                              ANY ideas welcome!
>                                                           -Eric
>-------------------------------------------------------------------------------
>Several people asked what machine I would be running this on:
>    Encore MULTIMAX 4.3 BSD UNIX
>    and a  VAX 11/750 running the same...
>*******************************************************************************
>* UUCP   : ..!harvard!bu-cs!bucsb!eap |-+-+ +-+-+-+-+-+-+-+\ /-+-+-+-+-+-+-+-+*
>* ARPANET: eap@bucsb.bu.edu           |    > :   :   :    - @ -  |       g    *
>* CSNET  : eap%bucsb@bu-cs            |-+-+-+-+-+-+-+-+-+-+/ \ +-+-+ +-+-+ +-+*
>* BITNET : cscc8vc@bostonu            | |  Blasted by ZAXXON   |  ;  |        *
>*******************************************************************************

   This article is almost two weeks old, I resisted answering until I got
caught up on comp.unix.questions.  A couple of people answered it with 
solutions using non-blocking reads and ioctl()s, but this is a job for
select().  If select() is available, and it is on Eric's BSD systems listed
above, it's much better than using ioctl()s.

   Below is some code which does just what Eric describes, using select().
This code is from a program which displays rwho information on the screen
using curses(), and updates its display every 20 seconds.  This loop does
the "every time" stuff at the top of the loop, and the only keyboard input
it looks for is a refresh command, anything else means to quit.  For an
application like this, select is definitely better, since the process is
asleep in a system call until either input is ready or the timer runs out.
With non-blocking I/O and ioctl()s, your process must run around in a
busy loop polling the keyboard.  A select() polling loop can still be
done by providing the address of a timer set to zero.  See the select() man
page for details.

Ron Hitchens		hitchens@ut-sally.uucp
			hitchens@godzilla.cs.utexas.edu
---------------

loop ()
{
	static	struct	timeval	timer = {REFRESH_DEFAULT, 0};
	char	c;
	int inmask, nfds;

	timer.tv_sec = refresh_time;		/* settable on cmd line */
	while (1) {
		show_stat ();			/* do it */
		inmask = 1;			/* gotta set the mask always */
		if ((nfds = select (32, &inmask, 0, 0, &timer)) < 0) {
			printf ("Error doing select, I'm gettin' outta here\n");
			return;
		}
		if (nfds == 0)
			continue;		/* timer expired */
		read (0, &c, 1);		/* the sucker hit a key */
		switch (c) {
		case 'r':
		case 'R':
		case 0x12:   /* ^R */
		case 'l':
		case 'L':
		case 0x0C:   /* ^L */
			(void) wclear (stdscr);	 /* clear the physical screen */
			(void) wrefresh (stdscr);/* for reassuring feedback */
			break;
		default:
			return;
		}
	}
}

   The above code puts the tty into CBREAK mode before calling loop().
You'll want to do this if you want to get each char as it becomes available.
If you leave it in cooked mode, select() will not indicate data is avaliable
until an entire line has been entered and a newline typed (or some other "break"
character, such as ^C or ESC).  The tty driver will also do backspace
handling transparently to you, if you use CBREAK, you'll have to do it
yourself (if appropriate).

   There is a gotcha to watch out for if you mix select() and stdio.  Select()
works on file descriptors, stdio implements a buffering system between your
code and the raw file descriptor.  This means that the data returned to you
by getchar(), gets(), etc, were probably read earlier and are being returned
from a buffer.  Doing a select() on an fd being used by stdio will only
be indicative of new data available on the fd, it won't know about any data
previously read and buffered by the stdio routines.  In general, it's not
a good idea to mix stdio and fd operations.
---------