[comp.lang.c] Keyboard Input

eap@bucsb.bu.edu.UUCP (Eric Pearce) (07/13/87)

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

I used to be able to do this easily on my Apple ][.  You could check a memory
location to see if a key had been pressed and which one it was, without having
the program wait for user input.   (good for games)

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   |  ;  |        *
*******************************************************************************

edw@ius2.cs.cmu.edu (Eddie Wyatt) (07/13/87)

In article <1043@bucsb.bu.edu.UUCP>, eap@bucsb.bu.edu.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.
> 


   This seems to be a common question in Unix. To follow, one
procedure that reads one character without waiting for a return
and one procedure that determines if a port is ready to read from.
Modify them to fit your needs if you want.  Do a man stty and ioctl
to find out what the proper include files are.




/**************************************************************************
 *                                                                        *
 *                              w_read_char                               *
 *                                                                        *
 **************************************************************************

   Purpose :  This function reads a single character from input without
	    waiting for a return to be typed.

   Programmer : Eddie Wyatt
 
   Date : July 1987

   Input : None

   Output : returns the current character from standard input

   Locals : 
	ch - the character read
	omode - the old tty mode (used in restoring mode after the character
	        is read)
	mode - use to change the tty mode so a single character can
	       be read without waiting for a return

   Globals : 
	stdin - not modified

 ************************************************************************/

int w_read_char()
    {
    struct sgttyb omode, mode;
    int ch;

    gtty(fileno(stdin),&mode);
    bcopy((char *) &mode, (char *) &omode,sizeof(struct sgttyb));
    mode.sg_flags |= CBREAK;

    stty(fileno(stdin),&mode);
    ch = getchar();
    stty(fileno(stdin),&omode);

    return(ch);
    }




/**************************************************************************
 *                                                                        *
 *                             ready_to_read                              *
 *                                                                        *
 **************************************************************************

   Purpose :  This function returns TRUE if the port (fd) is ready to
	    read from.

   Programmer : Eddie Wyatt
 
   Date : January 1987

   Input : 
   fd - a port

   Output :  returns TRUE if the port is ready to read

   Locals : 
    num - the number of bytes ready to read

   Globals : None

 ************************************************************************/

int ready_to_read(fd)
    int fd;
    {
    int num;

    ioctl(fd,FIONREAD,(char *)&num);
    return(num > 0);
    }


-- 
					Eddie Wyatt

e-mail: edw@ius2.cs.cmu.edu

terrorist, cryptography, DES, drugs, cipher, secret, decode, NSA, CIA, NRO.

rwhite@nu3b2.UUCP (Robert C. White Jr.) (07/14/87)

In article <1043@bucsb.bu.edu.UUCP>, eap@bucsb.bu.edu.UUCP (Eric Pearce) writes:
> 
> I used to be able to do this easily on my Apple ][.  You could check a memory
> location to see if a key had been pressed and which one it was, without having
> the program wait for user input.   (good for games)
> 
> 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)

It would apear that an apple ][ isnt a mainframe after all ;-)

SERIOUSLY: use the function ioctl to set O_NDELAY on standard input,
use read to read your character. if there are no characters waiting, and you
are not at an EOF condition read will return -1 and errno will be set to
EAGAIN.

This is how it is on our machine, check read(2) in your programmers
refrence [or wherever] for details. AND REMEMBER TO ALWAS CLEAN UP
YOUR ioctl BEFORE BRANCHING/EXITING FROM THE LOOP OR GO UP IN FLAMES!


Robert.

Disclaimer:  My mind is so fragmented by random excursions into a
	wilderness of abstractions and incipient ideas that the
	practical purposes of the moment are often submerged in
	my consciousness and I don't know what I'm doing.
		[my employers certainly have no idea]

john@bby-bc.UUCP (john) (07/17/87)

> SERIOUSLY: use the function ioctl to set O_NDELAY on standard input,
> use read to read your character. if there are no characters waiting, and you
> are not at an EOF condition read will return -1 and errno will be set to
> EAGAIN.
> 
> This is how it is on our machine, check read(2) in your programmers
> refrence [or wherever] for details. AND REMEMBER TO ALWAS CLEAN UP
> YOUR ioctl BEFORE BRANCHING/EXITING FROM THE LOOP OR GO UP IN FLAMES!


Isn't it supposed to return 0  if there are no characters read.  Also
isn't there an ioctl() settable value that specifies the minimum number
of characters which must be in the buffer before a request is satisfied,
along with a time period after which this many characters don't need to
be there?


john

guy%gorodish@Sun.COM (Guy Harris) (07/18/87)

> > SERIOUSLY: use the function ioctl to set O_NDELAY on standard input,
> > use read to read your character. if there are no characters waiting, and you
> > are not at an EOF condition read will return -1 and errno will be set to
> > EAGAIN.

> Isn't it supposed to return 0  if there are no characters read.

It's like this:

In systems that implement 4.[23]BSD-style no-delay I/O, if there are
no characters waiting, "read" will return -1 and "errno" will be set
to EWOULDBLOCK.

In systems that implement System V-style no-delay I/O:

	if the descriptor doesn't refer to a stream, "read" will
	return 0;

	if the descriptor does refer to a stream, "read" will return
	-1 and "errno" will be set to EAGAIN.

S5 prior to R3 didn't have streams.

(Note that some systems may implement both styles of no-delay I/O.)

In POSIX-style non-blocking I/O (selected with O_NONBLOCK, not
O_NDELAY), "read" will return -1 and "errno" will be set to EAGAIN.
I don't know if any systems implement this yet.  (If your system
doesn't define O_NONBLOCK, it doesn't implement it.)

> Also isn't there an ioctl() settable value that specifies the minimum number
> of characters which must be in the buffer before a request is satisfied,
> along with a time period after which this many characters don't need to
> be there?

There is, but not under 4.[23]BSD; the person in question is running
systems based on 4.[23]BSD.  In those systems, there is an "ioctl"
call FIONREAD that will tell you how many characters are waiting to
be read; with this, you don't have to go into no-delay mode (and thus
don't have to leave it, either).  This is arguably more convenient.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

mouse@mcgill-vision.UUCP (der Mouse) (07/24/87)

In article <805@nu3b2.UUCP>, rwhite@nu3b2.UUCP (Robert C. White Jr.) writes:
> In article <1043@bucsb.bu.edu.UUCP>, eap@bucsb.bu.edu.UUCP (Eric Pearce) writes:
>> I used to be able to do this [check for keyboard input without
>> waiting] easily on my Apple ][.
>> Is there a way to do this in C?

This is a system-dependent area; presumably you are talking about C
under UNIX.  C per se does not address this sort of question - all
operating system interface issues are handled through library routines.

Unfortunately, there is enough variation across different versions of
UNIX to make it impossible to give a blanket answer.

> [U]se the function ioctl to set O_NDELAY on standard input,

Please specify what flavor of UNIX!  Under 4.3 BSD, for example,
O_NDELAY is a flag to open(), not something one uses with ioctl() (the
ioctl() is called FIONBIO, also usable with fcntl()).

> [if no input and not EOF], read will return -1 and errno will be set
> to EAGAIN.

Another difference.  Under 4.3, errno is set to EWOULDBLOCK.  Please
qualify such statements with what flavor of operating system your
question or answer is for!  (Yes rwhite, I know you said "on our
system", but that doesn't help eap decide whether it applies to his
system.)

So, to sum up: it depends on your system.  Under most (all?) current
variants of UNIX, it is possible, but the details of how depend on the
particular system.  (I can provide detail for 4.3BSD UNIX, and general
ideas for 4.2BSD, but please send mail rather than cluttering the net!)

					der Mouse

				(mouse@mcgill-vision.uucp)

rsh27@chemabs.UUCP (Robert S. Hall) (07/27/87)

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.

        On a System V version of Unix you could open stdin using O_NDELAY 
(By the way on our Ultrix 1.2 system, in System V emulation mode, a program 
that uses O_NODELAY mode on the keyboard will cause a user to be logged off 
when it terminates.) On 4.[2,3] Berkley Unix you could use 
"ioctl(0,FIONREAD,&lngintvar);" this will return the number of characters 
that are ready to be returned by a read call in a long int variable named 
"lngintvar".  See the "tty(4)" documentation for more information.  6

gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/28/87)

In article <196@chemabs.UUCP> rsh27@UNIX14.UUCP (PUT YOUR NAME HERE) writes:
>(By the way on our Ultrix 1.2 system, in System V emulation mode, a program 
>that uses O_NODELAY mode on the keyboard will cause a user to be logged off 
>when it terminates.)

That's inherent in O_NDELAY.  If you don't reset it, when the shell tries
to read from the terminal it gets back what it interprets as EOF and exits.

acs60222@zach.fit.edu ( ENRIQUEZ) (07/20/89)

I have a simple question regarding keyboard input.  Is is possible to
trap the input (i.e. single character) using a routine such as 

   readln (kbd,char);

in Pascal.

I am tired of getting a bad character AND a \n character when checking
for valid user input.  Also, I don't like having to hit <return> :-(

Thanks.... mark enriquez (acs60222@zach.UUCP)

dhesi@bsu-cs.bsu.edu (Rahul Dhesi) (07/21/89)

In article <538@winnie.fit.edu> acs60222@zach.UUCP ( ENRIQUEZ) writes:
>
>I have a simple question regarding keyboard input.  Is is possible to
>trap the input (i.e. single character) using a routine such as 
>
>   readln (kbd,char);
>
>in Pascal.
>
>I am tired of getting a bad character AND a \n character when checking
>for valid user input.  Also, I don't like having to hit <return> :-(
>
>Thanks.... mark enriquez (acs60222@zach.UUCP)

I am quoting the entire article and directing follow-ups to
comp.lang.pascal.

There may be some shortcomings in your Pascal compiler and/or its
documentation.

The '\n' character is not defined in Pascal.  Let's assume by this you
mean 'newline'.  However, 'readln' in pascal cannot ever return a
newline.  It returns a blank if you do attempt to read a newline
character.

Having to hit 'return' is necessary if you want to edit a
partially-typed line.  Single-character input is probably possible but
a very system-dependent thing.   You will have to tell the terminal
driver to do single-character input, and once you have done so, you
should use 'read' (which can indeed read a single character) rather
than 'readln' (which always scans for and skips past a newline
following anything just read).
-- 
Rahul Dhesi <dhesi@bsu-cs.bsu.edu>
UUCP:    ...!{iuvax,pur-ee}!bsu-cs!dhesi

acs60222@zach.fit.edu ( ENRIQUEZ) (07/22/89)

Hmmmm, it seems I have been misunderstood...

My question was is there a C equivilent to the READ (kbd,char) (not
READLN, my mistake) availible in pascal.  If not, there must be
a C procedure that can be written to accomplish this simple task.
If someone can get me started, I'm sure I can figure it out.

All I want to do is get single-character input without having to
hit return.

mark

dhesi@bsu-cs.bsu.edu (Rahul Dhesi) (07/22/89)

In article <539@winnie.fit.edu> acs60222@zach.UUCP ( ENRIQUEZ) writes:
>Hmmmm, it seems I have been misunderstood...

Oops!  Sorry about that.  I really did misunderstand.

>My question was is there a C [equivalent] to the READ (kbd,char) (not
>READLN, my mistake) [available] in pascal....
...
>All I want to do is get single-character input without having to
>hit return.

"How do I do single-character input without having to hit return" is a
Very Commonly Asked Question.

Probably the closest C analog to the Pascal construct 
"read (file, char-variable)" is something like this:

     c = getc (stdin);   /* can also use c = getchar() */

However, neither the Pascal nor the C construct guarantee "raw" input,
i.e., reading a character from the device without waiting for an entire
line to be typed.  To do raw input you must do something special to
tell the device driver not to wait for a complete line.

Under VAX/VMS you can use an $QIO system call to do raw input.

Under MS-DOS you must use an MS-DOS-specific "ioctl" system call to put
the device driver into raw mode.  (Or you can do an MS-DOS-specific raw
console input system call, for which there is no corresponding standard
library function in C.)

Under the **IX family you can either do an ioctl() (a different one) or
do something like

     system ("stty raw");  /* more portable, slower, than ioctl() */

to put the tty driver into raw mode.

Your best bet, therefore, is to consult both your operating system and
your compiler manuals.

Once you have told the device driver to do raw input, you may still
need to set your stdin stream to unbuffered with a setbuf() function
call.  Alternatively, you could use a low-level system call for the
actual read, which again is highly system-dependent and not part of the
C language.
-- 
Rahul Dhesi <dhesi@bsu-cs.bsu.edu>
UUCP:    ...!{iuvax,pur-ee}!bsu-cs!dhesi