From 0aece203e8fdb2848f7b19885c07f477bc03130a Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 21 Jan 2017 02:31:45 +0000 Subject: [PATCH] textscreen: Tweak TXT_GetKeyDescription() again. We use the SDL APIs to get a localized name for keys now. However, we must consider that SDL's key names are in UTF8 format and can therefore contain non-ASCII characters which cannot be displayed on the CP437 text screen. So double-check if key names contain any unprintable characters and if they do, use a fallback set of default key names instead. This fixes #840. Thanks to Julian Nechaevsky for the report. --- src/doomkeys.h | 35 ++++++++++++++++ textscreen/txt_main.h | 2 + textscreen/txt_sdl.c | 96 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/doomkeys.h b/src/doomkeys.h index 3441c07e..35310a6b 100644 --- a/src/doomkeys.h +++ b/src/doomkeys.h @@ -114,5 +114,40 @@ 0, 0, 0, KEYP_EQUALS, /* 100-103 */ \ } +// Default names for keys, to use in English or as fallback. +#define KEY_NAMES_ARRAY { \ + { KEY_BACKSPACE, "BACKSP" }, { KEY_TAB, "TAB" }, \ + { KEY_INS, "INS" }, { KEY_DEL, "DEL" }, \ + { KEY_PGUP, "PGUP" }, { KEY_PGDN, "PGDN" }, \ + { KEY_ENTER, "ENTER" }, { KEY_ESCAPE, "ESC" }, \ + { KEY_F1, "F1" }, { KEY_F2, "F2" }, \ + { KEY_F3, "F3" }, { KEY_F4, "F4" }, \ + { KEY_F5, "F5" }, { KEY_F6, "F6" }, \ + { KEY_F7, "F7" }, { KEY_F8, "F8" }, \ + { KEY_F9, "F9" }, { KEY_F10, "F10" }, \ + { KEY_F11, "F11" }, { KEY_F12, "F12" }, \ + { KEY_HOME, "HOME" }, { KEY_END, "END" }, \ + { KEY_MINUS, "-" }, { KEY_EQUALS, "=" }, \ + { KEY_NUMLOCK, "NUMLCK" }, { KEY_SCRLCK, "SCRLCK" }, \ + { KEY_PAUSE, "PAUSE" }, { KEY_PRTSCR, "PRTSC" }, \ + { KEY_UPARROW, "UP" }, { KEY_DOWNARROW, "DOWN" }, \ + { KEY_LEFTARROW, "LEFT" }, { KEY_RIGHTARROW, "RIGHT" }, \ + { KEY_RALT, "ALT" }, { KEY_LALT, "ALT" }, \ + { KEY_RSHIFT, "SHIFT" }, { KEY_CAPSLOCK, "CAPS" }, \ + { KEY_RCTRL, "CTRL" }, { ' ', "SPACE" }, \ + { 'a', "A" }, { 'b', "B" }, { 'c', "C" }, { 'd', "D" }, \ + { 'e', "E" }, { 'f', "F" }, { 'g', "G" }, { 'h', "H" }, \ + { 'i', "I" }, { 'j', "J" }, { 'k', "K" }, { 'l', "L" }, \ + { 'm', "M" }, { 'n', "N" }, { 'o', "O" }, { 'p', "P" }, \ + { 'q', "Q" }, { 'r', "R" }, { 's', "S" }, { 't', "T" }, \ + { 'u', "U" }, { 'v', "V" }, { 'w', "W" }, { 'x', "X" }, \ + { 'y', "Y" }, { 'z', "Z" }, { '0', "0" }, { '1', "1" }, \ + { '2', "2" }, { '3', "3" }, { '4', "4" }, { '5', "5" }, \ + { '6', "6" }, { '7', "7" }, { '8', "8" }, { '9', "9" }, \ + { '[', "[" }, { ']', "]" }, { ';', ";" }, { '`', "`" }, \ + { ',', "," }, { '.', "." }, { '/', "/" }, { '\\', "\\" }, \ + { '\'', "\'" }, \ +} + #endif // __DOOMKEYS__ diff --git a/textscreen/txt_main.h b/textscreen/txt_main.h index 72aae918..7abc6f1b 100644 --- a/textscreen/txt_main.h +++ b/textscreen/txt_main.h @@ -149,6 +149,8 @@ int TXT_GetModifierState(txt_modifier_t mod); // keyboard (like that returned by TXT_INPUT_RAW), and the resulting string // takes keyboard layout into consideration. For example, // TXT_GetKeyDescription('q') on a French keyboard returns "A". +// The contents of the filled buffer will be in UTF8 format, but will never +// contain characters which can't be shown on the screen. void TXT_GetKeyDescription(int key, char *buf, size_t buf_len); // Retrieve the current position of the mouse diff --git a/textscreen/txt_sdl.c b/textscreen/txt_sdl.c index e936b85a..2162275b 100644 --- a/textscreen/txt_sdl.c +++ b/textscreen/txt_sdl.c @@ -715,30 +715,72 @@ int TXT_GetModifierState(txt_modifier_t mod) } } +// Unicode characters we allow in key names because they're in the +// CP437 extended ASCII range. +static const short unicode_whitelist[] = { + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, + 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, + 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, + 0x00ff, 0x00d6, 0x00dc, 0x00f1, 0x00e1, 0x00ed, 0x00f3, 0x00fa, + 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, + 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x03c6, 0x03b5, 0x2229, 0x00d1, +}; + +static int PrintableChar(int c) +{ + unsigned int i; + + if (c < 0x80) + { + return 1; + } + + for (i = 0; i < arrlen(unicode_whitelist); ++i) + { + if (unicode_whitelist[i] == c) + { + return 1; + } + } + + return 0; +} + +// Returns true if the given UTF8 key name is printable to the screen. +static int PrintableName(const char *s) +{ + const char *p; + unsigned int c; + + p = s; + while (*p != '\0') + { + c = TXT_DecodeUTF8(&p); + if (!PrintableChar(c)) + { + return 0; + } + } + + return 1; +} + +static const struct { + int key; + const char *name; +} key_names[] = KEY_NAMES_ARRAY; + static const char *NameForKey(int key) { + const char *result; int i; + // Overrides purely for aesthetical reasons, so that default + // window accelerator keys match those of setup.exe. switch (key) { - // A few keys which are not in the scancodes table: - case KEY_RSHIFT: return "SHIFT"; - case KEY_RCTRL: return "CTRL"; - case KEY_RALT: return "ALT"; - - // Keys where we want to use specific strings to look more - // like setup.exe: - case KEY_CAPSLOCK: return "CAPS"; - case KEY_BACKSPACE: return "BKSP"; - case KEY_ESCAPE: return "ESC"; - case KEY_ENTER: return "ENTER"; - case KEY_SCRLCK: return "SCRLCK"; - case KEY_PGUP: return "PGUP"; - case KEY_PGDN: return "PGDN"; - case KEY_INS: return "INS"; - case KEY_DEL: return "DEL"; - case KEY_PRTSCR: return "PRTSC"; - + case KEY_ESCAPE: return "ESC"; + case KEY_ENTER: return "ENTER"; default: break; } @@ -750,7 +792,23 @@ static const char *NameForKey(int key) { if (scancode_translate_table[i] == key) { - return SDL_GetKeyName(SDL_GetKeyFromScancode(i)); + result = SDL_GetKeyName(SDL_GetKeyFromScancode(i)); + if (TXT_UTF8_Strlen(result) > 6 || !PrintableName(result)) + { + break; + } + return result; + } + } + + // Use US English fallback names, if the localized name is too long, + // not found in the scancode table, or contains unprintable chars + // (non-extended ASCII character set): + for (i = 0; i < arrlen(key_names); ++i) + { + if (key_names[i].key == key) + { + return key_names[i].name; } }