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

@@ -1792,6 +1792,10 @@ curses: fix an off-by-one error when deciding whether a long line on the
bottom line of the message window can fit ">>" (curses' "--More--")
curses: selecting a partial stack, unselecting it, and then selecting it
normally did not reset the quantity to the whole stack
curses: if user's terminal was set to 'application keypad mode' (DEC VTxxx
nomenclature; set via 2 char "ESC =") and the terminfo or termcap
entry told the terminal to send 8-bit escape sequences (via 3 char
"ESC SPC G"), nethack wasn't recognizing number pad keys
macOS: Xcode project was failing to build if the path to the NetHack source
tree contained a space; the issue was within some shell script code
contained within the project

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 */