How do you read the arrow keys?

Brandon S. Allbery KB8JRR allbery at NCoast.ORG
Tue Jan 1 14:56:56 AEST 1991


As quoted from <4927:Dec2920:17:4790 at kramden.acf.nyu.edu> by brnstnd at kramden.acf.nyu.edu (Dan Bernstein):
+---------------
| It's really the terminal's fault, not the programmer's fault. Codes
| coming from the terminal should be uniquely decodable as untimed byte
| streams. In the best situation, no code is a prefix of another.
+---------------

AT&T has a very nice solution to this problem; unfortunately, it depends on
AT&T termio (or POSIX termios), so implementing it under a BSD variant is
difficult.  Although one could conceivably come up with a hack using select,
it would not be quite as reliable.  At least one commercial product I know of
uses this method (termio, not select), but it was documented in at least one
programmer's manual I've read as well.

Termio(s) doesn't really have a "raw" mode; it has a "packet" mode.  The most
common use is with a packet size of 1 and a timeout of 1 (which is treated as
"no timeout").  However, one can set it for other combinations.  The most
useful in this case is to set the packet size to the size of the longest
function key sequence and the timeout to the longest time needed for it to be
sent *as a function key*.  The assumption (usually correct) being that if the
user types it, it will take longer.

Once this is done, you attempt to read() that longest number of characters at
the same time.  read() returns the actual number of characters read before the
timeout, which starts after the first character of the packet is received.
Thus, single keystrokes like ESC are read as such, but given something like a
VT100, PF1 would return 3 characters --- ESC O P (ESC [ P if, like me, you
detest the applications cursor and keypad modes).

	struct termio tbuf; /* POSIX: struct termios */
	int maxlen = 3, len;
	char buf[3];

	ioctl(0, TCGETA, &tbuf); /* POSIX: tcgetattr(0, &tbuf); */
	tbuf.c_lflags &= ~(ICANON|ECHO);
	tbuf.c_cc[VMIN] = maxlen;
	tbuf.c_cc[VTIME] = 2; /* 2/10 sec, good at 9600 baud and up */
	ioctl(0, TCSETAW, &tbuf); /* POSIX: tcsetattr(0, X???WAIT, &tbuf); */
				  /* I forget the exact flag */

	len = read(0, buf, maxlen);
	if (len == 1)
	{
	    /* single character */
	}
	else
	{
	    /* function key sequence */
	}

Getting VTIME correct for various baud rates can be tricky; but it's also a
one-time task.  And I've used this trick in my own programs; it works well.
I believe the function key support in SVR3 curses can be coerced into doing
this if halfdelay() is enabled and works in your port.

For BSD, the most I can say is check to see if your version (e.g. Ultrix 3.x
or SunOS 4.x, etc.) supports a termio interface, or wait for BSD4.4 which
supposedly will have POSIX termios.  (Since BSD4.4 is either out or will be
very soon --- I've been out of touch with it --- no doubt someone will chime in
and tell us.)  Be warned that earlier Ultrix versions claimed to have termio
support, but it didn't work.

++Brandon
-- 
Me: Brandon S. Allbery			    VHF/UHF: KB8JRR on 220, 2m, 440
Internet: allbery at NCoast.ORG		    Packet: KB8JRR @ WA8BXN
America OnLine: KB8JRR			    AMPR: KB8JRR.AmPR.ORG [44.70.4.88]
uunet!usenet.ins.cwru.edu!ncoast!allbery    Delphi: ALLBERY



More information about the Comp.unix.programmer mailing list