Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How do you read the arrow keys?

193 views
Skip to first unread message

Spannring

unread,
Dec 27, 1990, 2:20:24 PM12/27/90
to

I am currently porting some menu routines from MS-DOG to Unix.
What is the proper (terminal independent) way of reading the arrow
and/or function keys?


--
====================================================================
Six of one, 110 (base 2) of | Craig Spannring
another. | icsu...@caesar.cs.montana.edu
----------------------------------+--------------------------------

Michael Meissner

unread,
Dec 28, 1990, 12:39:23 PM12/28/90
to
I tried mailing to icsu...@nero.cs.montana.edu, but got a mail
bounce:

| From: icsu...@nero.cs.montana.edu (Spannring)
| Newsgroups: comp.unix.programmer
| Date: 27 Dec 90 19:20:24 GMT
| Organization: Montana State University, Dept. of Computer Science, Bozeman
|
|
| I am currently porting some menu routines from MS-DOG to Unix.
| What is the proper (terminal independent) way of reading the arrow
| and/or function keys?

If your vendor's curses package is based on System V.2 then that is
provided. If you have the old broken BSD curses, lots of luck -- you
can read what the function keys send with tgetstr and the capabilities
kd, ku, kl, and kr for the arrow keys -- you will have to do the
parsing yourself.

Quoting from the manual on curses:

int getch ( void );
Get a character from stdscr. May be used with mini-
curses. The following function keys might be returned
by the getch() function if keypad() has been enabled:

KEY_BREAK Break key (unreliable)

KEY_DOWN Down arrow key

KEY_UP Up arrow key

KEY_LEFT Left arrow key

KEY_RIGHT Right arrow key

KEY_HOME Home key

KEY_BACKSPACE
Backspace (unreliable)

KEY_F(n) Function key Fn, where n is an integer from 0
to 63

KEY_DL Delete line

KEY_IL Insert line

KEY_DC Delete character

KEY_IC Insert character or enter insert mode

KEY_EIC Exit insert character mode

KEY_CLEAR Clear screen

KEY_EOS Clear to end of screen

KEY_EOL Clear to end of line

KEY_SF Scroll one line forward

KEY_SR Scroll one line backwards (reverse)

KEY_NPAGE Next page

KEY_PPAGE Previous page

KEY_STAB Set tab

KEY_CTAB Clear tab

KEY_CATAB Clear all tabs

KEY_ENTER Enter or send (unreliable)

KEY_SRESET
Soft (partial) reset (unreliable)

KEY_RESET Reset or hard reset (unreliable)

KEY_PRINT Print or copy

KEY_LL Home down or bottom (lower left)

KEY_A1 Upper left key of keypad

KEY_A3 Upper right key of keypad

KEY_B2 Center key of keypad

KEY_C1 Lower left key of keypad

KEY_C3 Lower right key of keypad

Due to lack of definitions in terminfo, or due to the
terminal not transmitting a unique code when the key is
pressed, not all of these keys are supported.

...

int keypad( WINDOW *win, bool bool_flag );
Enable keypad input on window win if bool_flag is true.
--
Michael Meissner email: meis...@osf.org phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

Considering the flames and intolerance, shouldn't USENET be spelled ABUSENET?

Jody Hagins

unread,
Dec 28, 1990, 11:43:36 AM12/28/90
to

The best way to implement terminal independent interfaces is with
curses. Read the manpage, and it will answer all your questions.
However, if you still have problems, no problem in asking.

--

Jody Hagins
hag...@gamecock.rtp.dg.com
Data General Corp.
62 Alexander Dr.
RTP, N.C. 27709
(919) 248-6035

Tim Iverson

unread,
Dec 28, 1990, 2:55:18 PM12/28/90
to
In article <3080@dali> icsu...@nero.cs.montana.edu (Spannring) writes:
> I am currently porting some menu routines from MS-DOG to Unix.
>What is the proper (terminal independent) way of reading the arrow
>and/or function keys?

Reading special keys is relatively easy - just look up the arrow/function
key definitions you wish to interpret in termcap or terminfo (whichever
your users are more likely to be using) and decode them as they come in.

The most general way to decode them is to build a key fetching FSM that
represents the current terminal and then ask it for keys; it, in turn,
gets byte strings from the terminal and turns them into keys.

There're going to be alot of responses about getting curses to decode your
keys for you. And, yes, it will do it, but it has a major problem: no
timeouts; e.g. left arrow on a vt100 (or pc ansi console) is <esc>[D, so if
your user hits <esc>, curses waits for the next char to come along before
it knows to return the <esc> as a key.

This is a failing of every package I've seen that purports to handle arrow
keys in a device independent manner, although, it's such a basic problem
that I assume someone somewhere is offering a package that does do it right.

Marc Rochkind has a good book on terminal style I/O (Advanced C Programming
for Displays) that covers all of the pieces of a comprehensive screen I/O
library, read it, but don't use the C code - each piece lacks some major
feature (like early prefix recognition for key input).

> Six of one, 110 (base 2) of | Craig Spannring
> another. | icsu...@caesar.cs.montana.edu

- Tim Iverson
uunet!xstor!iverson

Richard Brittain - VOS hacker

unread,
Dec 29, 1990, 7:45:54 AM12/29/90
to
In article <1990Dec28.1...@ivy.uucp> ive...@ivy.uucp (Tim Iverson) writes:
>In article <3080@dali> icsu...@nero.cs.montana.edu (Spannring) writes:
>> I am currently porting some menu routines from MS-DOG to Unix.
>>What is the proper (terminal independent) way of reading the arrow
>>and/or function keys?
>
>There're going to be alot of responses about getting curses to decode your
>keys for you. And, yes, it will do it, but it has a major problem: no
>timeouts; e.g. left arrow on a vt100 (or pc ansi console) is <esc>[D, so if
>your user hits <esc>, curses waits for the next char to come along before
>it knows to return the <esc> as a key.

Also, be prepared to make the timeout delay configurable if you want this
to work over a network of any kind. I have read function keys in several
programs using a 9600 bps direct connect terminal, with no problems, and
using a very short timeout to detect partial escape sequences. When I
first tried them from a networked workstation via xterm, I couldn't
recognise any sequences because of network delays. Increasing the timeout
(up to 500ms per byte sometimes) was needed to fix it.
It would certainly be nice if there was a standard library that did this
reliably in all cases.


--
Richard Brittain, School of Elect. Eng., Eng. and Theory Center
Cornell university, Ithaca, NY 14853
INTERNET: ric...@calvin.ee.cornell.edu
UUCP: {uunet,uw-beaver,rochester,cmcl2}!cornell!calvin!richard

John Temples

unread,
Dec 29, 1990, 12:05:47 PM12/29/90
to
In article <1990Dec28.1...@ivy.uucp> ive...@ivy.uucp (Tim Iverson) writes:
>There're going to be alot of responses about getting curses to decode your
>keys for you. And, yes, it will do it, but it has a major problem: no
>timeouts; e.g. left arrow on a vt100 (or pc ansi console) is <esc>[D, so if
>your user hits <esc>, curses waits for the next char to come along before
>it knows to return the <esc> as a key.

From the System V/386 Release 3.2 curses man page, under getch():

"If keypad(win, TRUE) has been called, and a function key is pressed,
the token for that function key will be returned instead of the raw
characters. [...] If a character is received that could be the
beginning of a function key (such as escape), curses will set a
timer. If the remainder of the sequence is not received within the
designated time, the character will be passed through, otherwise the
function key value will be returned."

I've used this in curses programs I've written which accept both
cursor keys and the escape key as input, and it works fine. I guess
it's only available in certain versions of curses?
--
John W. Temples -- jo...@jwt.UUCP (uunet!jwt!john)

Dan Bernstein

unread,
Dec 29, 1990, 3:17:47 PM12/29/90
to
In article <1990Dec28.1...@ivy.uucp> ive...@ivy.uucp (Tim Iverson) writes:
[ on the problem of interpreting <esc> given escape sequences ]

> This is a failing of every package I've seen that purports to handle arrow
> keys in a device independent manner, although, it's such a basic problem
> that I assume someone somewhere is offering a package that does do it right.

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.

A terminal could, for example, produce two <esc>s when you hit the
escape key. This solves the problem trivially.

Using timeouts isn't ``doing it right.'' It's an unfortunate kludge to
deal with the failings of current hardware.

---Dan

Kevin W. Reed

unread,
Dec 29, 1990, 9:01:24 PM12/29/90
to
jo...@jwt.UUCP (John Temples) writes:
>From the System V/386 Release 3.2 curses man page, under getch():
>
>"If keypad(win, TRUE) has been called, and a function key is pressed,
>the token for that function key will be returned instead of the raw
>characters. [...] If a character is received that could be the
>beginning of a function key (such as escape), curses will set a
>timer. If the remainder of the sequence is not received within the
>designated time, the character will be passed through, otherwise the
>function key value will be returned."
>
>I've used this in curses programs I've written which accept both
>cursor keys and the escape key as input, and it works fine. I guess
>it's only available in certain versions of curses?

It is not available on SCO Xenix when using termcap curses. It is
supposed to be available via terminfo curses.

Has anyone ever come up with a way to access function keys and arrow
keys using SCO curses termcap?

>--
>John W. Temples -- jo...@jwt.UUCP (uunet!jwt!john)

--
Kevin W. Reed --- TeleSys Development Systems -- PO 17821, San Diego, CA 92177
TeleSys-II BBS & telesys.UUCP 619-483-3890 ----- Telebit PEP Line 619 483 0965
UUCP: {nosc,ucsd}!crash!telesys!kreed -------- Internet: kr...@telesys.cts.com

Mike Stefanik/78125

unread,
Dec 30, 1990, 6:21:23 AM12/30/90
to
In article <1990Dec28.1...@ivy.uucp>, ive...@ivy.uucp (Tim Iverson) writes:
> There're going to be alot of responses about getting curses to decode your
> keys for you. And, yes, it will do it, but it has a major problem: no
> timeouts; e.g. left arrow on a vt100 (or pc ansi console) is <esc>[D, so if
> your user hits <esc>, curses waits for the next char to come along before
> it knows to return the <esc> as a key.

Actually, this isn't true, at least with the curses packages that I've been
using. The idea is to do something like this:

ttystate(mode)
int mode;
{
static struct termio old, new;

if ( mode == 1 ) {
ioctl(0,TCGETA,&old);
ioctl(0,TCGETA,&new);

new.c_lflag &= ~ECHO;
new.c_lflag &= ~ICANON;
new.c_lflag &= ~ISIG;
new.c_cc[VMIN] = MAXCHARS;
new.c_cc[VTIME] = TIMEOUT;

ioctl(0,TCSETA,&new);
}
else
ioctl(0,TCSETA,&old);
}

Where MAXCHARS is approx. the longest number of characters that can be
returned by a function/editing key, and TIMEOUT is usually 1 (1/10th of
a second).

When stdin is read, the read() will return when either:
a) MAXCHARS have been read
b) TIMEOUT has expired

Therefore, if the user just presses ESC, it is easy to determine if
it was an escape key or part of a sequence such as ESC[D ... just look at
the number of characters returned by read(). It gets trickier if the
pesky user is holding down the arrow key, which can generate multiple
escape sequences within the timeframe of the read; that's why it's good
to set the MAXCHARS value to something approximating the length of the
escape sequence ... if the user is holding down the key, the first
sequence with satisfy MAXCHARS, and cause the read() to return, with the
remaining sequences still in the input buffer.

Hope this helps.

-----------------------------------------------------------------------------
Michael Stefanik, Systems Engineer (JOAT), Briareus Corporation
UUCP: ...!uunet!bria!mike
"If it was hard to code, it should be harder to use!"

Brandon S. Allbery KB8JRR

unread,
Dec 31, 1990, 10:56:56 PM12/31/90
to
As quoted from <4927:Dec2920:17:47...@kramden.acf.nyu.edu> by brn...@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: all...@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

Dan Bernstein

unread,
Jan 1, 1991, 2:35:32 AM1/1/91
to
In article <1991Jan1.0...@NCoast.ORG> all...@ncoast.ORG (Brandon S. Allbery KB8JRR) writes:
> As quoted from <4927:Dec2920:17:47...@kramden.acf.nyu.edu> by brn...@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
[ followed by fifty lines of description ]

That is not a nice solution. First of all, it's slow: most of the time
taken by at least one editor I've tested is in reading keys using a
similar method. Second, many networks will split packets at random
spots, so the method will fail every once in a while. Third, it forces
the editor to play with the terminal in ways it should not have to.
Fourth, it is a lot more complex than a simple FSA. Fifth, it introduces
time dependencies into what should be an untimed byte stream; this leads
to race conditions and serves no useful purpose.

It's the only kludge that works given our current broken terminals, but
that doesn't make it nice.

---Dan

Karl-P. Huestegge

unread,
Jan 2, 1991, 10:41:09 AM1/2/91
to
brn...@kramden.acf.nyu.edu (Dan Bernstein) writes:

You're right, but a lot of terminals are able to send 8bit iso-8859
Control codes (CSI instead of 'ESC [' and DCS instead of 'ESC P' etc).

DEC's VT220/VT320 can handle this for example. But I don't know if the
termcap/terminfo libs can handle it... I havn't tried yet.


--
Karl-Peter Huestegge ka...@robot.in-berlin.de
Berlin Friedenau ..unido!fub!geminix!robot!karl

Karl-P. Huestegge

unread,
Jan 2, 1991, 10:49:42 AM1/2/91
to
In <1990Dec30....@telesys.cts.com> kr...@telesys.cts.com (Kevin W. Reed) writes:

>jo...@jwt.UUCP (John Temples) writes:
>>I've used this in curses programs I've written which accept both
>>cursor keys and the escape key as input, and it works fine. I guess
>>it's only available in certain versions of curses?

>It is not available on SCO Xenix when using termcap curses. It is
>supposed to be available via terminfo curses.

>Has anyone ever come up with a way to access function keys and arrow
>keys using SCO curses termcap?

No problem here with SCO 386GT 2.3.3.
It's defined in /etc/termcap (kd,ku,kl,kr) and in /usr/include/tcap.h.

John Temples

unread,
Jan 3, 1991, 2:18:45 PM1/3/91
to
In article <1991Jan1.0...@NCoast.ORG> all...@ncoast.ORG (Brandon S. Allbery KB8JRR) writes:
>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").

Did you mean "timeout of 0" here? Timeout of 1 activates a .1 second
timeout on systems I've used -- although VTIME seems to have no effect when
VMIN == 1. My playing with termio shows that VTIME only takes effect
between characters of multi-character reads. So how does curses
halfdelay() mode work? This lets a single-character read time out in
as little as .1 second. Does the fact that halfdelay() seems to be
broken on many systems imply that there's something more to it than
just a termio call?

Brandon S. Allbery KB8JRR

unread,
Jan 4, 1991, 9:56:02 PM1/4/91
to
As quoted from <1991Jan3.1...@jwt.UUCP> by jo...@jwt.UUCP (John Temples):
+---------------

| In article <1991Jan1.0...@NCoast.ORG> all...@ncoast.ORG (Brandon S. Allbery KB8JRR) writes:
| >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").
|
| Did you mean "timeout of 0" here? Timeout of 1 activates a .1 second
| timeout on systems I've used -- although VTIME seems to have no effect when
| VMIN == 1. My playing with termio shows that VTIME only takes effect
+---------------

Correct as to VMIN=1 disabling timeout: the packet timer starts *after the
first character is received*, so it can block waiting for a packet. That's
why I specified that it is a packet driver, not a general read with a timeout.

I have seen and used a number of implementations; at least two use VTIME=0
to cause the driver to essentially become non-blocking, at least if VMIN=1.
Weird, no?

+---------------


| between characters of multi-character reads. So how does curses
| halfdelay() mode work? This lets a single-character read time out in
| as little as .1 second. Does the fact that halfdelay() seems to be
| broken on many systems imply that there's something more to it than
| just a termio call?

+---------------

I strongly suspect that halfdelay breaks because of the common misconception
that VTIME makes the tty driver do general reads with timeouts, when in fact
it merely times out reads of packets after the first character is received.
The proper way to do halfdelay() is to make the tty driver select()able or
poll()able and use that with a timeout. (Most 386/486 SVR3.2's treat select()
as poll() with a different calling sequence, so there's no difference in
the way they work.)

++Brandon
(P.S. I also suspect that the packet driver mode of the termio driver was
intended as a more general alternative to the UUCP packet device driver in
some V7's --- mainly because I once checked the stty settings on a port under
System III when there was a uucico talking on it, and VMIN was set to the
size of a UUCP G protocol packet....)

0 new messages