问题
Extensive searching on the use of raw mode with termios and xterm leads to numerous references to a "timing trick" required to distinguish between an escape-sequence and a lone appearance of the escape character.
So how do you do it?
I don't want to use curses because I don't want to clear the screen. This is for a calculator-style program, so it's important to retain the "ticker-tape" interface.
回答1:
finally found a nice detailed description in an old usenet thread. I quote the relevant message in its entirety.
Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!tut.cis.ohio-state.edu!usenet.ins.cwru.edu!ncoast!allbery From: all...@NCoast.ORG (Brandon S. Allbery KB8JRR) Newsgroups: comp.unix.programmer Subject: Re: How do you read the arrow keys? Message-ID: Date: 1 Jan 91 03:56:56 GMT References: Reply-To: all...@ncoast.ORG (Brandon S. Allbery KB8JRR) Followup-To: comp.unix.programmer Organization: North Coast Computer Resources (ncoast) Lines: 68 As quoted from 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.
I've trimmed this author's signature block, since he acknowledges not to be the original author anyway.
来源:https://stackoverflow.com/questions/34824604/how-do-you-read-the-arrow-keys