curses number pad handling

This attempts to address the issue on hardfought (as described by K2
on reddit two weeks ago) which happened after updating their terminfo
database.  It's based on the reported workaround of having users force
their terminals to avoid "application keypad mode".  The fixes entry is
my guess at what is happening so could be wrong....

I don't have a numeric keypad so this is untested; it /ought to work/.
NetHack's curses interface is doing it's own keypad recognition when
the curses library returns a multi-char sequence rather than converting
that into corresponding KEY_xxx token.  Numpad keys that the interface
recognizes begin with 2 char 'ESC O' when transmitted as 7-bit sequences.
This adds support for SS3 (as sent by DEC Vtxxx terminals and emulators;
value is single char 'M-O') for 8-bit sequences.

It shouldn't break typing Alt+O but that's something else that I can't
test properly.  Setting nethack's 'altmeta' option and manually typing
2 char 'ESC O' still works as intended.

This is curses-specific; the tty interface is completely unaffected.
[However, the vms port has supported SS3 with tty for decades. :-]
This commit is contained in:
PatR
2023-08-11 13:14:01 -07:00
parent 7b2a72d083
commit 70834c12df
2 changed files with 28 additions and 8 deletions

View File

@@ -32,7 +32,7 @@ static boolean modifiers_available = FALSE;
static int modified(int ch);
static void update_modifiers(void);
static int parse_escape_sequence(boolean *);
static int parse_escape_sequence(int, boolean *);
int
curses_getch(void)
@@ -866,11 +866,12 @@ curses_convert_keys(int key)
/* Handle arrow and keypad keys, but only when getting a command
(or a command-like keystroke for getpos() or getdir()). */
switch (key) {
case M('O'): /* 8-bit version of ESC 'O' c for keypad key */
case '\033': /* ESC or ^[ */
/* changes ESC c to M-c or number pad key to corresponding digit
(but we only get here via key==ESC if curses' getch() didn't
change the latter to KEY_xyz) */
ret = parse_escape_sequence(&numpad_esc);
ret = parse_escape_sequence(key, &numpad_esc);
reject = ((uchar) ret < 1 || ret > 255);
as_is = !numpad_esc; /* don't perform phonepad inversion */
break;
@@ -1068,11 +1069,11 @@ curses_mouse_support(int mode) /* 0: off, 1: on, 2: alternate on */
#endif
}
/* caller just got an input character of ESC;
/* caller just got an input character of ESC or M-O;
note: curses converts a lot of escape sequences to single values greater
than 255 and those won't look like ESC to caller so won't get here */
static int
parse_escape_sequence(boolean *keypadnum)
parse_escape_sequence(int key, boolean *keypadnum)
{
#ifndef PDCURSES
int ret;
@@ -1082,12 +1083,26 @@ parse_escape_sequence(boolean *keypadnum)
timeout(10);
ret = getch();
if (ret == 'O') { /* Numeric keypad */
/* ESC O <something> */
ret = getch();
if (ret == 'O' || key == M('O')) { /* handle numeric keypad */
/*
* ESC O <pending> or M-O <something|nothing>.
*
* For the former, we don't have the next char yet so get it now.
* If there isn't one, treat ESC O as if user typed M-O (which
* is probably the case, via alt+shift+O combo sending two char
* "ESC O").
*
* For the latter, it there wasn't another char then 'ret' will
* be ERR and we'll treat the result as M-O. However, if there
* is another char and it is O meant as two characters "M-O O"
* we'll be fooled, but that's not a valid escape sequence so
* don't worry about those two characters arriving together.
*/
if (key == '\033')
ret = getch();
if (ret == ERR) {
ret = 'O'; /* there was no third char; treat as M-O below */
ret = 'O'; /* there was no additional char; treat as M-O below */
} else if (ret >= 112 && ret <= 121) { /* 'p'..'y' */
*keypadnum = TRUE; /* convert 'p'..'y' to '0'..'9' below */
}
@@ -1106,6 +1121,7 @@ parse_escape_sequence(boolean *keypadnum)
return ret;
#else
nhUse(key);
nhUse(keypadnum);
return '\033';
#endif /* !PDCURSES */