[comp.lang.c] keypressed

eschle@forty2.UUCP (Patrik Eschle) (02/11/88)

How do I write a function keypressed(), that returns 0 if no
char is in the input queue and returns the character otherwise?
Keypressed() should not block, and should leave the terminal in its
initial state after returning.

I can't use curses and have played around with ioctl, but its
really slow.

Any suggestions?                   Patrik 

pardo@june.cs.washington.edu (David Keppel) (02/18/88)

In article <136@forty2.UUCP> eschle@forty2.UUCP (Patrik Eschle) writes:
[ how to do "char=keypressed()" returns 0 if none pressed? ]
>I can't use curses and have played around with ioctl, but its
>really slow.

(Everybody: please include a return address.  I don't have domain uucp!)

On BSD Un*x you can use the "select()" call to wait for a stream to become
ready; if the "timeout" parameter is zero (NOT a zero pointer!) the call
will return immediately.  I have no idea what the performance is compared
to ioctl(), I'd guess it is less device-dependent than ioctl().

On some (many?) PCs, you can read the keyboard or controller directly.

    ;-D on  (Not an answer, but at least not a question!)  Pardo

ok@quintus.UUCP (Richard A. O'Keefe) (02/19/88)

In article <4230@june.cs.washington.edu>, pardo@june.cs.washington.edu (David Keppel) writes:
> In article <136@forty2.UUCP> eschle@forty2.UUCP (Patrik Eschle) writes:
> [ how to do "char=keypressed()" returns 0 if none pressed? ]
> On BSD Un*x you can use the "select()" call to wait for a stream to become
> ready; if the "timeout" parameter is zero (NOT a zero pointer!) the call
> will return immediately.

The original poster simply wanted a way of finding out whether or not
anything had been typed.  BSD systems (and EUUG V7) have a very simple
way of making exactly that test:
	ioctl(file_descriptor, FIONREAD, &count_of_ready_characters);
{The name was different in EUUG V7, but the effect was similar.}
This sets count_of_ready_characters to the number of characters which
are available for input.  See "man 4 tty", which claims that "This
works for files, pipes, and terminals."

In System V it gets more complicated, but one approach is to switch
the terminal into non-blocking mode and try to read a character.
If you get one, there was one.  The snag is that you have to buffer
this character yourself.  But you don't have to wait.

Shekar_Narayanan.SV@Xerox.COM (02/20/88)

Patrik,

	If you are working in Turbo-C or Microsoft-C environment and in the MS-DOS
world, they both have function/procedure which does exactly what you want.
However you may have do more work if you need to scan for special keys such
Function keys (F1-F10) or CTRL+Characters. The function provided in the C
library in Turbo & MSC is "kbhit()" which returns true if any key was pressed
else zero. If you do not want to echo the character read in to the terminal use
the function getch() in C run time library. 

	Hope this helps.

	Shekar

phil@galbp.LBP.HARRIS.COM (Phil McDonald) (02/23/88)

In article <136@forty2.UUCP> eschle@forty2.UUCP (Patrik Eschle) writes:
>How do I write a function keypressed(), that returns 0 if no
>char is in the input queue and returns the character otherwise?
>Keypressed() should not block, and should leave the terminal in its
>initial state after returning.
>
>I can't use curses and have played around with ioctl, but its
>really slow.
>
>Any suggestions?                   Patrik 

===========================================================================
I would suggest using the rdchk(s) command.

Keypressed()
{
char	c;

	if (rdchk(stdin) > 0) {
		read(stdin, &c, sizeof(char));
		return(c);
	}
	else
		return(0);
}

terry@wsccs.UUCP (terry) (03/12/88)

In article <3961@galbp.LBP.HARRIS.COM>, phil@galbp.LBP.HARRIS.COM (Phil McDonald) writes:
> In article <136@forty2.UUCP> eschle@forty2.UUCP (Patrik Eschle) writes:
> >How do I write a function keypressed(), that returns 0 if no
> >char is in the input queue and returns the character otherwise?
> >Keypressed() should not block, and should leave the terminal in its
> >initial state after returning.
> >
> >I can't use curses and have played around with ioctl, but its
> >really slow.
> >
> >Any suggestions?                   Patrik 
> 
> ===========================================================================
> I would suggest using the rdchk(s) command.

	Phil, read the summary line.  I will assume you are using a UNIX
machine, since you mentioned ioctl and you didn't specify otherwise.

It isn't fast, probably because

	1) Your machine isn't fast
or	2) You are using a DMA I/O controller
or	3) You are calling it wrong

There are at least 3 ways to do it and not get into too much trouble:

1)	This one's breif, so here's a code fragment:

	#include <stdio.h>
	#include <sgtty.h>
	/*
	 * r e a d c h k ( )
	 *
	 * returns number of characters pendinding input on an 'open()'ed
	 * tty.
	 */
	readchk( fd)
	int fd;
	{
		int cnt;
		ioctl( fd, FIONREAD, &cnt);
		return( cnt);
	}

2)	This isn't breif, so implimentation is left up to the student:

	A. Define a shared memory segment.
	B. Make sure the shared memory segment has room for 2 characters.
	C. Zero the first character.
	D. Fork a child process to hang on a single character read.
	E. When the read completes, the child process puts the character
	   into the second character position of the shared memory, then
	   sets the first to one.
	F. The child process buzz-loops on the first character position,
	   and loops back up to the 'hang-on-read' when the first character
	   is returned to zero.

	meanwhile...

	G. The main program occasionally looks at the first character.
	H. When the first character is non-zero, it moves the second to a
	   local buffer, then zeroes it.

3)	Same as #2, except the child process is another program, which is
	popen()'ed.

	Instead of a fork, a popen( "program", "r") is done, where the
	purpose of the child is to hang on the read and signal the parent
	(via a signal, semaphore, or shared memory) when the read completes,
	then the child writes the character read to the pipe.  A better method
	whold be to write the character to the pipe first.

	When the parent realizes that a character there, via whatever method,
	it reads *from the pipe* the character read by the child.


	All of the methods described above are not the fastest, but they
will work.  Which one you should use depends on your situation, and your
operating system.  The readchk() suggested by Phil basically *DOES* what
is done in method 1, with the exception that it only works on fd 0 (stdin).

	Which one you use depends on your application and the system you are
on.  If you are on a small system (like a 386 box), you are probably not
going to get -----f-a-s-t--->->-> results, no matter what you do.  If you
are using a DMA device, polling via method #1 or rdchk() is not happy, as
it causes I/O to occur, and this (naturally) must occur through a DMA channel.
DMA channels do not like small I/O like this, and they will fight it by
waiting for 1) a full DMA 'packet' (simplified terminology) or 2) a timeout
if there are not enough I/O operations to 'fill' a 'packet'.  This is so they
can avoid causing too many interrupts, as the whole purpose of a DMA device
is to offload processor I/O work so that instead of getting an interrupt
for each character, you get one for each 'bunch' of characters.

	Some systems do NOT support semaphores, or support them such that
you wouldn't want to use them, even if you trusted them, which you don't.
The same goes for shared memory.  Signals are reliable, but can be slow,
depending on the kernal implimentation.  The main problem with BOTH 2 & 3
are that you are giving the system the opportunity to swap out one process
while the other one does it's stuff.  If you are on a small system, it's
probably dying because you are being swapped out on your ioctl() (remember
that on almost _every_ kernal call, you are opening yourself up to be swapped,
_especially_ I/O operations.

	What it boils down to is that I really don't have enough info to
give you a cut and dry answer.  What UNIX are you running on?  What box?
Is it using DMA I/O?  What is the application?  If you could say something
like 'I am running on a VAX 750 under Berkley 4.2 and writing a Kermit
connect mode to go over DMA dhv11 controllers', then I would say that you
should use the selectio() function (a Berkley exclusive) which would wait
for a read to complete on 2 fd's, thereby allowing you to 'queue' your
reads, sort of... whichever completed, you'd go off and do something, then
you'd re-queue.

	If you email me more info, I will email you an answer.  Be sure to
provide adequate references, please, including a return path..

	Flame at will.  I have working code for all the above, and I *love*
it when someone sticks their foot in their mouth so I can give compileable
examples of why they are a fool. :-)

| Terry Lambert           UUCP: ...!decvax!utah-cs!century!terry              |
| @ Century Software       or : ...utah-cs!uplherc!sp7040!obie!wsccs!terry    |
| SLC, Utah                                                                   |
|                   These opinions are not my companies, but if you find them |
|                   useful, send a $20.00 donation to Brisbane Australia...   |
| 'There are monkey boys in the facility.  Do not be alarmed; you are secure' |

laman@ncr-sd.SanDiego.NCR.COM (Mike Laman) (03/16/88)

In article <303@wsccs.UUCP> terry@wsccs.UUCP (terry) writes:
>In article <3961@galbp.LBP.HARRIS.COM>, phil@galbp.LBP.HARRIS.COM (Phil McDonald) writes:
>> In article <136@forty2.UUCP> eschle@forty2.UUCP (Patrik Eschle) writes:
>> >How do I write a function keypressed(), that returns 0 if no
>> >char is in the input queue and returns the character otherwise?
>> >Keypressed() should not block, and should leave the terminal in its
>> >initial state after returning.

	Stuff deleted

>2)	This isn't breif, so implimentation is left up to the student:
>
>	A. Define a shared memory segment.
>	B. Make sure the shared memory segment has room for 2 characters.
>	C. Zero the first character.
>	D. Fork a child process to hang on a single character read.
>	E. When the read completes, the child process puts the character
>	   into the second character position of the shared memory, then
>	   sets the first to one.
>	F. The child process buzz-loops on the first character position,
>	   and loops back up to the 'hang-on-read' when the first character
>	   is returned to zero.
>
>	meanwhile...
>
>	G. The main program occasionally looks at the first character.
>	H. When the first character is non-zero, it moves the second to a
>	   local buffer, then zeroes it.
>
>3)	Same as #2, except the child process is another program, which is
>	popen()'ed.
>
>	Instead of a fork, a popen( "program", "r") is done, where the
>	purpose of the child is to hang on the read and signal the parent
>	(via a signal, semaphore, or shared memory) when the read completes,
	       ^^^^^^
	       BE VERY CAREFUL!  On System V and V.2 this won't work if the
	parent process gets too far behind the reading child process.
	If a signal is "posted" (i.e. generated) from the child process before
	the parent process gets a chance to handle an outstanding signal
	(including reseting the signal catcher to itself for the next signal),
	the signal is lost since the (I believe) the process's (kernel level)
	signal bit flag for that particular signal is already set and ORing it
	"on" again has no effect.  When the bit is already set and another
	signal event for that same signal exists, the ORing makes no internal
	state changes in the process's (kernel level) signal bit flag
	and is thus "lost".  The good news is that one has to generate the stuff
	pretty fast.  This causes the child to generate signals for each
	character, and if the parent gets behind on handling the signal
	then the signal can get "lost".  If your machine is slow, I'd
	suggest being VERY parinoid if you use this scheme.  You might
	also consider what may happen if the system gets busy.  Remember that
	if you lose ONE signal, your parent will be "forever" out of sync,
	unless you make some arrangement in the parent to make sure at
	certain times that you have gotten all of the input.  But then that
	reverts you back to what you were trying to get around.

>	then the child writes the character read to the pipe.  A better method
>	whold be to write the character to the pipe first.

	In the signal case this would even help the parent lag behind the child.

>	When the parent realizes that a character there, via whatever method,
>	it reads *from the pipe* the character read by the child.
				 ^^^^^^^^^^^^^
				 This assumes that your process will receive
	one signal per character.

>	All of the methods described above are not the fastest, but they
>will work.  Which one you should use depends on your situation, and your
>operating system.  ......

	And if the system is slow enough signals will get "lost", and the
	parent process will not read get behind on reading characters since
	the parent assumes only one read per signal.  Also note that if this
	situation occurs, you have degenerated back to the situation in
	which your proposition occurs.

	[ Text deleted ]

>	Some systems do NOT support semaphores, or support them such that
>you wouldn't want to use them, even if you trusted them, which you don't.
>The same goes for shared memory.  Signals are reliable, but can be slow,
				   ^^^^^^^^^^^^^^^^^^^^
				   Not if you push enough of them through
	before they can be serviced.
>depending on the kernal implimentation.  ...
>
>	Flame at will.  I have working code for all the above, and I *love*
	^^^^^^^^^^^^^
	I'm just adding my experiences to the proposal.
>it when someone sticks their foot in their mouth so I can give compileable
								^^^^^^^^^^^
								Compileable
	doesn't mean it will have the semantics that he needs.
>examples of why they are a fool. :-)

	(In the voice of Curly of the three stooges) "I resemble that" :-).

	For what it's worth, the problems I had with the signals on System V
	and System V.2 with "losing" signal, made me very wary of them when
	one approaches pushing them too hard.  Sigh...

	I just thought I'd save you some time/trouble if you get into this
	situation.

	I don't know if there are any problems with BSD signals.  Off hand
	they would seem to be better, but I know of them from a few of the
	user interface routines.  I'm only speaking of System V and System V.2.

Mike Laman
UUCP: ncr-sd!laman

le@auspyr.UUCP (Hung Le) (03/17/88)

-- 
"I'm trying     Joe Angelo, Sr. Sys. Engineer @ Austec, Inc., San Jose, CA.
 to think,      ARPA: auspyr!joe@lll-tis[-b].arpa     PHONE: [408] 279-5533
 but nothin'    UUCP: {sdencore,cbosgd,ptsfa,dana}!aussjo!joe
 happens!"      UUCP: {amdahl,lll-tis,imagen,necntc,dlb,sci,altnet}!auspyr!joe