Re: Esc key

Search this archive.

From: Russell Marks (russell.marks@dtn.ntl.com)
Date: Sun 12 Mar 2000 - 15:34:24 IST


> > Ok, I'm sure I could figure this one out for myself if I put some time
> > into it, but I'm also thinking someone (like Russell) has maybe already
> > solved it.
> > 
> > So, in a public display of the languor and apathy that I'm feeling today, 
> > I'll post my question here.  ;-P
> > 
> > When using vga_getch() and vga_getkey(), how do you avoid a value of "27"
> > applying to more than just the "Esc" key? I know the directional arrows
> > are "escaped", and so pressing the up arrow sends (at first) the same
> > signal as the Esc key. This is the same problem as you get with ncurses.
> > 
> > So, what's the answer? Can this be done without raw keyboard mode?
[...]
> Use vga_getkey() to read the keyboard, if you see an ESC read it again
> immediately (without waiting). If you get 0 then no other key was
> pressed and you have an ESC key press, if you get anything else then
> treat it as an escape sequence.

In the spirit of "show me the code" :-), there's a working (I hope)
example of how to do this below (after all my verbiage).

BTW, the reason this read-without-waiting works is because Linux dumps
the whole string generated by the key into your input at once (AFAIK),
something you can't depend on over network or serial connections. So
this approach is ok to use with svgalib, but you obviously can't do it
this way for terminals (which is why ncurses has to wait).

Another thing to note - since vga_getkey() returns zero for `no key',
it gives you no way of reading ctrl-space. [1] Admittedly this isn't a
key you're likely to want to read unless you're writing an svgalib
driver for Emacs, but...

(Actually, I was adding a sort of pseudo-compatibility feature to
$PROGRAM_I_END_UP_MENTIONING_IN_EVERY_POST the other day, and I needed
ctrl-space for that. But then again, I don't use vga_getkey() there
(for complicated reasons to do with console switching).)

[1] Rather like CP/M's direct console I/O BDOS function in fact. But,
er, I didn't say that honest, I'm not that sad. :-)

> I forget if you need to explicitly tell SVGALIB to use vga_getkey() as
> non-blocking. If it does, then you'll need to set it to non-blocking.

I think svgalib runs in CBREAK mode, and vga_getkey() uses select() to
avoid blocking.

> I don't know about this ncurses problem, but this method also works with
> normal terminals connected to a unix box, you just need to be sure to

Ummm, for terminals you really ought to wait a bit, rather than
reading the chars after Esc immediately. If it happens to work without
waiting, it's only luck and/or buffering. :-) (I know you mentioned
disabling buffering, but that won't affect a UART - using a multi-byte
buffering UART like a 16550A might be all that's saving you here.)
ncurses may wait a stupidly long time (IMHO), but you really should
wait at least a *short* time. (But as I say, there's no need to wait
when using svgalib, so I'm veering off-topic.)


Anyway, in case actual code might help :-), here's what zgv does. I've
hacked it to use vga_getkey() here and removed some support for (very)
old broken keymaps, but other than that, and a slight hack added
recently to handle more Alt-shifted keys, this is basically from zgv
5.0. It handles most (all?) of the keys which are readable without
using raw mode, and handles both Esc-prefixed meta (Alt) keys and
`real' ones. (It also maps e.g. Alt-A to Alt-a, but it wouldn't be
hard to remove that if you wanted.) RK_* as used below can be any
integers not in the 0..255 range; see zgv's src/readnbkey.h.

(I've not tested it since the hacking, so YMMV :-))


/* returns a 'normal' ASCII value, or one of the values in readnbkey.h,
 * or zero if no key was pressed.
 */
int readnbkey(void)
{
static unsigned char keybuf[1024];
int f,left;

/* this gets all the characters sent by the key into an ASCIIZ string.
 * the no-waiting-for-keys approach depends on the way Linux dumps
 * the string into our input all at once; this is kind of nasty,
 * but zgv *is* Linux-specific, right? :-)
 */
/* now only does this if first char is esc, to save losing keys in
 * other situations (e.g. when getting input)
 */
f=0;
for(left=sizeof(keybuf)-1;left>0 && (keybuf[f++]=vga_getkey())!=0;left--)
  if(keybuf[0]!=27) break;
keybuf[f]=0;    /* end the string */

/* convert Esc-A..Z and Esc-a..z to Meta-a..z, and other Esc-foo to
 * Meta-foo, by converting any 2-char string starting with Esc.
 * (It also allows the end char to be Esc, to avoid quitting when
 * someone holds a key down in some situations. :-))
 */
if(keybuf[0]==27 && keybuf[1] && (!keybuf[2] || keybuf[2]==27))
  return(128+tolower(keybuf[1]));

/* also convert Meta-A..Z to Meta-a..z. */
if(keybuf[0]>=128+'A' && keybuf[0]<=128+'Z' && keybuf[1]==0)
  return(keybuf[0]+32);


/* Deal with function keys, cursors, and the like.
 * this relies on a minimum-unique string approach rather than
 * checking for the trailing ~ (on function keys) or NUL (on everything :-)).
 */
if(keybuf[0]==27 && keybuf[1]=='[')
  {
  switch(keybuf[2])
    {
    case 'A':   return(RK_CURSOR_UP);
    case 'B':   return(RK_CURSOR_DOWN);
    case 'C':   return(RK_CURSOR_RIGHT);
    case 'D':   return(RK_CURSOR_LEFT);
    case '[':
      switch(keybuf[3])
        {
        case 'A':       return(RK_F1);
        case 'B':       return(RK_F2);
        case 'C':       return(RK_F3);
        case 'D':       return(RK_F4);
        case 'E':       return(RK_F5);
        }
      break;
    case '1':
      switch(keybuf[3])
        {
        case '~':       return(RK_HOME);
        case '7':       return(RK_F6);
        case '8':       return(RK_F7);
        case '9':       return(RK_F8);
        }
      break;
    case '2':
      switch(keybuf[3])
        {
        case '~':       return(RK_INSERT);
        case '0':       return(RK_F9);
        case '1':       return(RK_F10);
        case '3':       return(RK_F11);
        case '4':       return(RK_F12);
        case '5':       return(RK_SHIFT_F1);
        case '6':       return(RK_SHIFT_F2);
        case '8':       return(RK_SHIFT_F3);
        case '9':       return(RK_SHIFT_F4);
        }
      break;
    case '3':
      switch(keybuf[3])
        {
        case '~':       return(RK_DELETE);
        case '1':       return(RK_SHIFT_F5);
        case '2':       return(RK_SHIFT_F6);
        case '3':       return(RK_SHIFT_F7);
        case '4':       return(RK_SHIFT_F8);
        }
      break;
    case '4':   return(RK_END);
    case '5':   return(RK_PAGE_UP);
    case '6':   return(RK_PAGE_DOWN);
    }
  }

/* otherwise... */
return(*keybuf);
}



In case you're wondering, Shift-F9..F12 are missing because they don't
seem to generate anything. And most Ctrl/Alt-shifted `special' keys
are indistinguishable from unshifted ones (or are used for console
switching and the like). You also risk losing e.g. multiple presses of
cursor keys if you wait too long between calls. This is actually
rather useful in zgv, but could be a pain in some contexts.

If you need more than the above routine can handle, you probably do
need raw mode.

-Rus.


This archive was generated by hypermail 2.1.4 : Wed 21 Jan 2004 - 22:10:23 IST