[comp.unix.programmer] can i test for keyboard input?

mwnewman@watmsg.uwaterloo.ca (mike newman) (10/03/90)

I would like to be able to respond dynamically to keyboard input
(without doing getchar and waiting for the user to type something) by
either:

     1)	Having some kind of interrupt routine that will be called
	every time a key is pressed.

     2) Calling some OS routine that returns TRUE/FALSE if there
	is/isn't any keyboard input waiting.  I know my little ol' ST
	at home (bless it's heart :-) has such a call: does unix?

     3) Any other way :-).

This seems like it should be possible: musn't the shell do something
along these lines to handle type ahead? (???)

Please email replies: if there is interest, I will post a summary.

mike newman
mwnewman@msg.waterloo.edu

jik@athena.mit.edu (Jonathan I. Kamens) (10/03/90)

  As I believe has been discussed recently in this newsgroup
(comp.unix.programmer), you can use the FASYNC fcntl() operation on BSD-like
systems to turn on asynchronous input mode on standard input, and that should
cause you to get a SIGIO every time a character is typed.

  Furthermore, question 7 on the monthly comp.unix.questions Frequently Asked
Questions posting (most recently posted on October 1) is, "How do I check to
see if there are characters to be read without actually reading?"  It's
usually a good idea to read the FAQ posting before posting a question to a
comp.unix.* newsgroup in general, or to comp.unix.questions in particular.  I
have included the text of the FAQ posting's answer at the end of this message.

  Finally, I have included below the FAQ text the source code for a function
called keypressed() which should illustrate to you the general method you can
use, at least under BSD-like systems.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8495			      Home: 617-782-0710

------------------------------------------------------------------

7)  How do I check to see if there are characters to be read without
    actually reading?

    Certain versions of UNIX provide ways to check whether
    characters are currently available to be read from a file
    descriptor.  In BSD, you can use select(2).  You can also use
    the FIONREAD ioctl (see tty(4)), which returns the number of
    characters waiting to be read, but only works on terminals,
    pipes and sockets.  In System V Release 3, you can use poll(2),
    but that only works on streams.  In Xenix - and therefore
    Unix SysV r3.2 and later - the rdchk() system call reports
    whether a read() call on a given file descriptor will block.

    There is no way to check whether characters are available to be
    read from a FILE pointer.  (You could poke around inside stdio data
    structures to see if the input buffer is nonempty, but that wouldn't
    work since you'd have no way of knowing what will happen the next
    time you try to fill the buffer.)

    Sometimes people ask this question with the intention of writing
	    if (characters available from fd)
		    read(fd, buf, sizeof buf);
    in order to get the effect of a nonblocking read.  This is not the
    best way to do this, because it is possible that characters will
    be available when you test for availability, but will no longer
    be available when you call read.  Instead, set the O_NDELAY flag
    (which is also called FNDELAY under BSD) using the F_SETFL option
    of fcntl(2).  Older systems (Version 7, 4.1 BSD) don't have O_NDELAY;
    on these systems the closest you can get to a nonblocking read is
    to use alarm(2) to time out the read.

------------------------------------------------------------------

/* Procedure to check for waiting input on the tty.  Does not */
/* actually read the input.                                   */

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

#ifdef TEST
main()
{
     char buf[64];
     
     while (1) {
	  if (keypressed())
	       break;
	  else {
	       printf("Waiting...\n");
	       sleep(1);
	  }
     }
     (void) printf("Enter string: ");
     (void) gets(buf);
     exit(0);
}
#endif


	  
int
keypressed()
{
     /* These are for ioctl */
     struct sgttyb tty, ntty;
     int ttyset, stat, arg;

     ttyset = 0;
     
     stat = ioctl(0, TIOCGETP, &tty);
     if (stat == -1) {
	  perror("ioctl");
	  return(-1);
     }
     
     if (! (tty.sg_flags & CBREAK)) {
	  ntty = tty;
	  ttyset = (! ttyset);
	  ntty.sg_flags |= CBREAK;
	  stat = ioctl(0, TIOCSETN, &ntty);
	  if (stat == -1) {
	       perror("ioctl");
	       return(-1);
	  }
     }
     
     stat = ioctl(0, FIONREAD, &arg);
     if (stat == -1) {
	  perror("ioctl");
	  return(-1);
     }
     
     if (ttyset) {
	  stat = ioctl(0, TIOCSETN, &tty);
	  if (stat == -1) {
	       perror("ioctl");
	       return(-1);
	  }
     }
     
     return(arg);
}

rembo@unisoft.UUCP (Tony Rems) (10/06/90)

In article <1990Oct3.041737.2280@watmath.waterloo.edu> mwnewman@watmsg.uwaterloo.ca (mike newman) writes:
>I would like to be able to respond dynamically to keyboard input
>(without doing getchar and waiting for the user to type something) by
>either:
>
>     1)	Having some kind of interrupt routine that will be called
>	every time a key is pressed.
>
>     2) Calling some OS routine that returns TRUE/FALSE if there
>	is/isn't any keyboard input waiting.  I know my little ol' ST
>	at home (bless it's heart :-) has such a call: does unix?
>
>     3) Any other way :-).
>
>This seems like it should be possible: musn't the shell do something
>along these lines to handle type ahead? (???)
>
>Please email replies: if there is interest, I will post a summary.
>

I think what you are looking for is the select(2) routine.  It
will allow you to open your tty and it will wait until there is
data available to be read, so it will return as soon as the user
types something.  

Another solution would be to set your terminal into cbreak mode
and this way input will come to your program without being buffered.
You can do this with ioctl(2).  This differs depending on whether
you are using BSD or SYS V. 

Final, if you want to get fancy, you can look into pseudo-ttys
or curses.  All these should be in your manual set.

If you can't find what you're looking for with these calls
let me know and I'll send you some sample code.

-Tony