[net.unix-wizards] raw/cooked single char i/o

arnold@gatech.UUCP (06/10/83)

	I am working on and off on a screen editor which I have
adapted to use the (4.1) curses package. The editor makes use of
control characters for doing various things, including things like
DEL and Control-\ which normally send interrupts.
	In order to get one character at a time, I do the following:
(echo is previously turned off, using the noecho() call.)

	raw();
	c = getchar();
	noraw();
	c &= 0177;	/* lop off parity bit */
	return(c);

	The problem when I do this is that I lose all type ahead
when the editor is redrawing the screen or searching or such.
	My question then is, how can I continue to do raw I/O for
the control characters, but keep the type-ahead? I know it's possible,
becuase VI does it.

	Thanks in advance

		Arnold Robbins
		(Unix Apprentice:  Neophyte < Apprentice < Wizard)
-- 
"The parchment and quill pen of Arnold Robbins"

Arnold @ GATech		          (CS Net)
Arnold.GATech @ UDel-Relay        (ARPA)	 School of ICS
...!{sb1, allegra}!gatech!arnold  (uucp)      	 Georgia Tech
...!duke!mcnc!msdc!gatech!arnold                 Atlanta, GA 30332
...!decvax!cornell!allegra!gatech!arnold

guy@rlgvax.UUCP (06/11/83)

Leave the terminal in CBREAK mode (NOT RAW mode - see previous submission)
all the time; if you want to disable the interrupt and quit characters,
set them to '\0377' using the TIOCGETC and TIOCSETC "ioctl" calls.

What you do is:

#include <whatever you are including>
#include <curses.h>
#include <sgtty.h>

main()
{
	struct tchars oldtchars, newtchars;

	initialization;
	enter "curses";
	crmode();
	ioctl(_tty_ch, TIOCGETC, &oldtchars);
	newtchars = oldtchars;
	newtchars.t_intrc = '\0377';
	newtchars.t_quitc = '\0377';
	ioctl(_tty_ch, TIOCSETC, &newtchars);

	do whatever your program does;

	nocrmode();
	ioctl(_tty_ch, TIOCSETC, &oldtchars);
	leave "curses";
	exit(<your exit code>);
}

The reason typeahead was flushed was that you were going into and out of RAW
mode before each read; going into or out of RAW (or CBREAK) mode flushes all
typeahead.  It also consumes a lot of CPU time; furthermore, it means that
while you aren't waiting for a character your program is vulnerable to
interrupts!  If you are using the job control mechanism, you may want either
to disable their control characters as well (see the manual page TTY(4)) or
catch them.

		Guy Harris
		RLG Corporation
		{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy

guy@rlgvax.UUCP (06/11/83)

The difference between RAW and CBREAK mode, in 25 words or less:

RAW mode means

	8-bit data path, with no parity checking
	no special character interpretation whatsoever
	wakeup on every character is possible
	no special output processing whatsoever

CBREAK mode means

	7-bit data path, with whatever parity checking is enabled
	"line processing" (erase, kill, EOF, special EOL, and retype on
		4.1BSD) is disabled, BUT the interrupt and quit
		characters are enabled (as are the other signalling characters
		on 4.1BSD); furthermore, XON/XOFF processing is also active
	wakeup on every character is possible
	no change to the output processing from COOKED mode whatsoever

Programs which run interactively on a keystroke basis should NEVER run in
RAW mode, only CBREAK mode.  CBREAK mode is intended for exactly those kinds
of programs; RAW mode is SOLELY intended for binary data transmission (like
UUCP).  For USG UNIX, read ~ICANON mode for CBREAK mode and read "some very
complicated set of bits turned on and off" for RAW mode.

"curses" has the "functions" (actually macros, at least in 4.1BSD) "crmode"
and "nocrmode", which function analogously to "raw" and "noraw" except that
they put the terminal in CBREAK mode rather than RAW mode.

I've noticed several references to RAW mode being what screen editors and the
like run in.  This is either due to the fact that RAW mode was all you had
on V6, or due to people simply not being aware of CBREAK mode, or due to
people not being aware how obnoxious RAW mode is for interaction with a
human at a terminal.  In USG UNIX, you CAN turn on 8-bit characters without
turning off the interrupt characters and XON/XOFF, but your terminal better
use the 8th bit for information rather than parity.

		Guy Harris
		RLG Corporation
		{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy

obrien%rand-unix@sri-unix.UUCP (06/13/83)

This message is empty.

ptw@vaxine.UUCP (P. Tucker Withington) (06/13/83)

Just a nit, but CBREAK does have *one* effect on output (which could really
drive you nuts).

The normal tty driver will not let you *send* EOT (it silently ignores it).
This is to "avoid hanging up some terminals".

The bummer is, that some other "terminals" (e.g., Diablo and emulations) use
small numbers for absolute motions and other terminal settings.  We ran into
this problem when we fine-tuned nroff to really use our Spinwriter.  It would
"glitch" now and again.  The problem was solved by printing in CBREAK mode.


				       --Tucker (ptw@vaxine.UUCP)

guy@rlgvax.UUCP (06/14/83)

What really rots about that is that ONLY on 4.1BSD can you output an EOT
in CBREAK mode; it's not in standard V7.  The USG UNIX tty driver solves this
by the simple expedient of not echoing your EOF character, and passing EOT
through in all modes like any other character.  Now if it would only echo
control characters, or at least the special TTY driver control characters,
as "^<whatever>", so you could get visual feedback that the EOT actually
got through to the computer...

		Guy Harris
		RLG Corporation
		{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy

SJOBRG.ANDY%MIT-OZ%mit-ml@sri-unix.UUCP (06/14/83)

Well, perhaps you should run in CBREAK mode, and then set all of the
interrupt characters that remain to be \377 -- the tty driver will then
ignore them, and you will get your buffered input. However, you will
NOT get 8 bits, just the usual 7.

ellis@flairvax.UUCP (06/15/83)

    While we're on this topic, does anyone out there have any idea
    how to switch back & forth between 7-bit & 8-bit character input
    modes WITHOUT flushing queued input characters ?

    Currently, switching to RAW mode is the only way to get all 8 bits,
    and doing this (even if you use TIOCSETN) causes input flushing.

    This is all for a program that reads from a keyboard with a META-shift
    key (which sets the 'parity' bit) and then executes programs expecting
    the normal UNIX tty environment. Currently, all type-ahead is lost.

    I realize that several kernel mods would be required to do what I want..

    Michael Ellis - Fairchild AI Lab - Palo Alto CA - (415) 858 4270

z@cca.UUCP (06/16/83)

Shortly after 4.1BSD came out, I made some rather trivial mods to the
kernel to add L8BIT, a new bit in the local mode word of the tty driver.
Setting L8BIT allows you to transmit and receive all eight bits in
either cooked or CBREAK mode.  My inspiration was also a terminal with a
META key; I wanted to use all eight bits but still have flow control.  I
tried convincing Berkeley to incorporate this into 4.2BSD, but they
weren't interested.  If there is sufficient interest, I can post these
mods to the net.

	Steve Zimmerman

chris@umcp-cs.UUCP (06/16/83)

Now for my two cent's worth:  I'd like a full set of options, like so:

IN_8BIT		/* 8 bit input */
OUT_8BIT	/* 8 bit output */
ECHO		/* echo all input */
CTLECHO		/* echo control chars as ^x */
CBREAK		/* wake up on all input */

the usual tab, CR, NL, FF delay/expansion

ECHONL		/* Echo NL after CR, for half-duplex with ~ECHO */
BREAKNULL	/* Change BREAK to null instead of sending signal */
BREAKIGN	/* Ignore BREAK completely */

and all the special characters in one structure.  No need for a RAW
mode; it's just IN_8BIT|OUT_8BIT|CBREAK|BREAKNULL, with all the special
characters turned off.  The reason for BREAKNULL and BREAKIGN?  Well,
BREAKNULL is for those programs that look for NULL, in RAW, as a change-
speed code (i.e. getty).  BREAKIGN is for noisy lines, where you get
spurious BREAKs, which are currently nearly impossible to get around.
If you set neither, then BREAK should generate SIGINT in ANY mode.

				- Chris
-- 
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs
ARPA:	chris.umcp-cs@UDel-Relay

guy@rlgvax.UUCP (06/16/83)

The USG tty driver's equivalent of these bits:

	IN_8BIT		/* 8 bit input */

Turn off ISTRIP (strip input characters to 7 bits) and INPCK (enable input
parity checking) in c_iflag.

	OUT_8BIT	/* 8 bit output */

Set the character size to CS8 (yes, it also supports CS5 and CS6 as well
as CS7) and turn off PARENB in c_cflag.

The ability to select 7+parity vs. 8 on input independly from on output
depends, of course, on the hardware supporting it; reading the "Terminals
and Communications Handbook", my interpretation is that if you set 7+parity
you will still get handed all 8 bits on input, but you can tell the receiver
not to check parity on input while it still generates parity on output.  The
ISTRIP bit doesn't condition any hardware bits, it merely controls whether
the character is masked with 0177 or 0377.

	ECHO		/* echo all input */

Same name, in c_lflag.

	CTLECHO		/* echo control chars as ^x */

Not in the USG driver, but it should be; call it ECHOCTL (just like CRTERASE
is called ECHOE(!)) and put it in c_lflag.

CBREAK		/* wake up on all input */

Turn off ICANON (canonical input - erase and kill processing) in
c_lflag.

	the usual tab, CR, NL, FF delay/expansion

They are much the same, and are in c_oflag.  All that processing
can be efficiently turned off by turning off OPOST (postprocess output)
in c_oflag - characters are moved from user space to the clist as directly
as possible.

	ECHONL		/* Echo NL after CR, for half-duplex with ~ECHO */

It's there, even under the same name, in c_lflag.

	BREAKNULL	/* Change BREAK to null instead of sending signal */

Turn off IGNBRK (ignore breaks), BRKINT (send SIGINT on breaks), and PARMRK
(mark parity errors framing errors, and overruns with "\377\000" in the
input stream) in c_iflag.

	BREAKIGN	/* Ignore BREAK completely */

Turn on IGNBRK in c_iflag.  Note that the handling of a break in
V7, at least, is a bit of a kludge; it pretends that it received your
interrupt character (actually, the code is buggy and it pretends it received
a RUBOUT) and hands it to ttyinput().  The USG driver handles it separately,
so even if you turn off your interrupt and quit characters you can still send
a SIGINT to your process by hitting the BREAK key.

	and all the special characters in one structure.

Which they are.

	If you set neither, then BREAK should generate SIGINT in ANY mode.

Which it does.

End of commercial, but it would be nice, considering 4.2BSD has already
adopted the USG "open" system call, if the people at Berkeley would consider
adopting an enhanced version of the USG tty driver (including all their
additional modes and control characters) for 4.2BSD or a later release.
Note that the "ioctl" calls have different names - the modes are set by
TCSETA - so the old calls could be kept around for compatibility.  There
are such backward compatibility features in the USG driver, but unfortunately
for those of us not within AT&T they're backwards compatible with UNIX/TS 1.0
rather than with V7 or 4.1BSD.

I can't see any legal arguments against it, since they've already put the
USG "open" system call in (and since you get the System III C compiler, the
PWB/UNIX 2.0? UUCP, the System IV? "sdb", "efl", and two pieces of the
Writer's Workbench with 4.1BSD), and the only technical argument I can see
against it (assuming the old "ioctl"s are left in for backward compatibility)
is that it would take time to design and implement.  And it would probably
reduce the number of complaints about "I can't do this with the vanilla TTY
driver" nearly to zero; you can even talk to Baudot lines (doing your own
translation in your program) if you want.

		Guy Harris
		RLG Corporation
		{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy