--
====================================================================
Six of one, 110 (base 2) of | Craig Spannring
another. | icsu...@caesar.cs.montana.edu
----------------------------------+--------------------------------
| 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?
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
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
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
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)
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
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
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!"
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
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
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
>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.
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?
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....)