[comp.sys.amiga.tech] Urgent Help Needed.

maniac@arrakis.nevada.edu (ERIC SCHWERTFEGER) (12/02/89)

[]

	I have one quick question, and I need a very urgent answer,
(Dec 4 is too late).  I've got a program that runs from the CLI, and
I need to detect when a key is struck, and what key was struck.
	The problem I'm having is that any key I hit is echoed to the
window, which blocks window output, until I press return.  
	I want a way around this, using standard C functions, that will
work with Lattice 4.0, because this program is going to be ported over
to a Unix environment to be turned in for a class project.
	BTW, the method I'm using now is getchar().
Eric J. Schwertfeger, UNLV  maniac@arrakis.nevada.edu

duncant@mbunix.mitre.org (Thomson) (12/04/89)

In article <1083@unsvax.NEVADA.EDU>
 maniac@arrakis.nevada.edu.uucp (ERIC SCHWERTFEGER) writes:

>)...  I've got a program that runs from the CLI, and
>I need to detect when a key is struck, and what key was struck.
>	The problem I'm having is that any key I hit is echoed to the
>window, which blocks window output, until I press return.  
>	I want a way around this, using standard C functions, that will
>work with Lattice 4.0, because this program is going to be ported over
>to a Unix environment to be turned in for a class project.
>	BTW, the method I'm using now is getchar().

I think the problem you are having is actually more accurately described as:
input from the keyboard (stdin in this case) is done using line buffering.
In this mode, an I/O operation is not completed until a return is
pressed (allowing editing of the input line with the backspace key).
Therefore your program is sitting there at the getchar() call, waiting
for the I/O to complete.

The solution SHOULD be to change the buffering mode from line buffering
to no buffering, using the standard library function setvbuf().
Unfortunately, this doesn't work.  It doesn`t work with a lot of compilers
on a lot of systems actually.  I'd be interested in hearing from someone
close to the C language ANSI comittee if there is a reason for this.  Am
I interpreting the standard`s description of setvbuf incorrectly?

The second problem you may have is you want to get rid of character echo.
I'm not sure there is a way to do this in the ANSI C library.  Anyone
else know of a way?

If you're not concerned about portability, you can solve all yourproblems
by sending a message to the console handler which puts it into RAW mode.
I have some code somewhere which does this.  Let me know if you still need it.

By the way, i think you can also do something like:

	FILE *keybd;
	keybd = fopen( "RAW:", "r" );
	c = fgetc( keybd );
	...

I tried this once, and it worked - what you're doing is using the 
console handler in it's raw mode.  I'm not quite sure now whether  
"RAW" is exactly the right name though.

(I'm now writing this message in my office using a PC (yuck) and I don't 
have any amiga manuals or code handy, which is why I'm a but fuzzy on the 
details)

Duncan Thomson

andrewt@watsnew.waterloo.edu (Andrew Thomas) (12/07/89)

I don't know how to do it on the amiga, but here is a method which
works on BSD, SunOS, Ultrix.  I tried to reply by mail but it bounced.
This code is guaranteed not at all.  No claim is made to it either.
Do what you will.

------------------- cbreak.c -----------------------
/* cbreak.c  written by Andrew Thomas
   andrewt@watsnew.waterloo.edu

   If you have ideas on how to make this more portable, I am keen to
   hear them.  I make no guarantees, no claims, no copyrights.
*/

#include	<sgtty.h>
#include	<signal.h>
#include	<stdio.h>
#include	<sys/file.h>
#include	<sys/ioctl.h>

/*  WARNING:  If this file is compiled with gcc (or any ansi compiler)
    	      you must privide the -traditional option, or modify the
	      header files to adhere to ansi standard.
*/

/* The call you really want to use from here is the call to
   get_a_key(noecho,interrupt_status).  If noecho is non-zero, then
   the keys will not be echoed as they are typed.  Interrupt_status is
   explained in more detail later on.
*/

static	int		cbreak_on = 0;
static	int		tty = 0;
static	int		tty_set = 0;
static	int		cbreak_int_status = 0;
static	struct sgttyb	tty_desc;

void cbreak();
void nocbreak();

void cbreak_ignoresignal ()
{
}

void cbreak_killsignal ()
{
  nocbreak ();
  exit (1);
}

void cbreak_stopsignal ()
{
  nocbreak ();
  kill (getpid(), SIGSTOP);
  if (cbreak_on) cbreak(cbreak_on-1);
}

/* interrupt_status tells the system what to do with SIGTSTP, SIGTERM and
   SIGINT
   interrupt status is one of 0, 1, or 2:
   0: do nothing to alter the interrupts, or if interrupt status was
      changed before, make interrupts perform the default action.
   1: make interrupts cause a clean exit, resetting the interrupt status
      on exit and reentry
   2: ignore interrupts.
*/

void cbreak_open_tty (interrupt_status)
int	interrupt_status;
{
  tty_set = 1;
  tty = fileno (stdin);
  if (ioctl (tty, TIOCGETP, &tty_desc) == -1)
    perror ("ioctl: TIOCGETP");

  if (interrupt_status == 0 && cbreak_int_status != 0)
    {
      signal (SIGTSTP, SIG_DFL);
      signal (SIGTERM, SIG_DFL);
      signal (SIGINT, SIG_DFL);
    }
  else if (interrupt_status == 1)
    {
      signal (SIGTSTP, cbreak_stopsignal);
      signal (SIGTERM, cbreak_killsignal);
      signal (SIGINT, cbreak_killsignal);
    }      
  else if (interrupt_status == 2)
    {
      signal (SIGTSTP, cbreak_ignoresignal);
      signal (SIGTERM, cbreak_ignoresignal);
      signal (SIGINT, cbreak_ignoresignal);
    }      
  cbreak_int_status = interrupt_status;
}

/* noecho, if non-zero, causes the key stroke not to be echoed */

void cbreak (noecho, interrupt_status)
int	noecho, interrupt_status;
{
  if (!tty_set || interrupt_status != cbreak_int_status)
    cbreak_open_tty (interrupt_status);
  tty_desc.sg_flags |= CBREAK;
  if (noecho) tty_desc.sg_flags &= ~ECHO;
  if (ioctl (tty, TIOCSETN, &tty_desc) == -1)
    perror ("cbreak: ioctl: TIOCSETN");
}

void nocbreak ()
{
  if (!tty_set) cbreak_open_tty (cbreak_int_status);
  tty_desc.sg_flags &= ~CBREAK;
  tty_desc.sg_flags |= ECHO;
  if (ioctl (tty, TIOCSETN, &tty_desc) == -1)
    perror ("nocbreak: ioctl: TIOCSETN");
}

/* get a keystroke in cbreak mode, echoed or not echoed, with actions on
   interrupts specified.   Clear the stdin buffer first.  This is a
   bit of a kluge.  Suggestions welcome.
*/

char get_a_key (noecho, interrupt_status)
int	noecho, interrupt_status;
{
  char	x;

  while (stdin->_cnt > 0) if ((x=getchar()) != '\n') return (x);

  cbreak (noecho, interrupt_status);
  cbreak_on = 1+(noecho?1:0);
  x = getchar();
  nocbreak ();
  cbreak_on = 0;
  return (x);
}
--------------------------------------------------

Hope this helps.
--

Andrew Thomas
andrewt@watsnew.waterloo.edu	Systems Design Eng.	University of Waterloo
"If a million people do a stupid thing, it's still a stupid thing." - Opus