From d8c13f992f20fb563d5bfdacc616983ef236f624 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 12 Jul 2021 06:15:22 +0200 Subject: [PATCH] Improve handling of "console key", add in_ignoreConsoleKey CVar If in_ignoreConsoleKey is set, the console can only be opened with Shift+Esc, not `/^/whatever, so you can easily type whatever character is on your "console key" into the game, or even bind that key. Otherwise, with SDL2, that key (KEY_SCANCODE_GRAVE) always generates the newly added K_CONSOLE. in_kbd has a new (SDL2-only) "auto" mode which tries to detect the keyboard layout based on SDL_GetKeyFromScancode( SDL_SCANCODE_GRAVE ). Wherever Sys_GetConsoleKey() is called, I now take the current state of Shift into account, so we don't discard more chars than necessary, esp. when they keyboard-layout (in_kbd) is *not* correctly set. (TBH the only reason besides SDL1.2 to keep in_kbd around is to ignore the char generated by the "console key" in the console..) --- neo/framework/Console.cpp | 7 +- neo/framework/KeyInput.h | 1 + neo/sys/events.cpp | 155 ++++++++++++++++++++++++++------------ neo/ui/EditWindow.cpp | 2 +- 4 files changed, 111 insertions(+), 54 deletions(-) diff --git a/neo/framework/Console.cpp b/neo/framework/Console.cpp index 769d9978..7da453c1 100644 --- a/neo/framework/Console.cpp +++ b/neo/framework/Console.cpp @@ -797,8 +797,9 @@ bool idConsoleLocal::ProcessEvent( const sysEvent_t *event, bool forceAccept ) { bool consoleKey = false; if(event->evType == SE_KEY) { - if( event->evValue == Sys_GetConsoleKey( false ) || event->evValue == Sys_GetConsoleKey( true ) - || (event->evValue == K_ESCAPE && idKeyInput::IsDown( K_SHIFT )) ) // shift+esc should also open console + bool shiftPressed = idKeyInput::IsDown( K_SHIFT ); + if( event->evValue == K_CONSOLE || event->evValue == Sys_GetConsoleKey( shiftPressed ) + || (event->evValue == K_ESCAPE && shiftPressed) ) // shift+esc should also open console { consoleKey = true; } @@ -850,7 +851,7 @@ bool idConsoleLocal::ProcessEvent( const sysEvent_t *event, bool forceAccept ) { // handle key and character events if ( event->evType == SE_CHAR ) { // never send the console key as a character - if ( event->evValue != Sys_GetConsoleKey( false ) && event->evValue != Sys_GetConsoleKey( true ) ) { + if ( event->evValue != Sys_GetConsoleKey( idKeyInput::IsDown( K_SHIFT ) ) ) { consoleField.CharEvent( event->evValue ); } return true; diff --git a/neo/framework/KeyInput.h b/neo/framework/KeyInput.h index 83c22fdd..77179d35 100644 --- a/neo/framework/KeyInput.h +++ b/neo/framework/KeyInput.h @@ -267,6 +267,7 @@ typedef enum { K_LAST_SCANCODE = K_SC_CURRENCYSUBUNIT, // TODO: keep up to date! + K_CONSOLE, // special keycode used for the "console key" and only to open/close the console (not bindable) // FIXME: maybe move everything joystick related here diff --git a/neo/sys/events.cpp b/neo/sys/events.cpp index 4ff2ca47..faa46033 100644 --- a/neo/sys/events.cpp +++ b/neo/sys/events.cpp @@ -60,10 +60,16 @@ If you have questions concerning this license or the applicable additional terms #endif static const char *kbdNames[] = { +#if SDL_VERSION_ATLEAST(2, 0, 0) // auto-detection is only available for SDL2 + "auto", +#endif "english", "french", "german", "italian", "spanish", "turkish", "norwegian", "brazilian", NULL }; -static idCVar in_kbd("in_kbd", "english", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "keyboard layout", kbdNames, idCmdSystem::ArgCompletion_String ); +static idCVar in_kbd("in_kbd", kbdNames[0], CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "keyboard layout", kbdNames, idCmdSystem::ArgCompletion_String ); +// TODO: I'd really like to make in_ignoreConsoleKey default to 1, but I guess there would be too much confusion :-/ +static idCVar in_ignoreConsoleKey("in_ignoreConsoleKey", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL, + "Console only opens with Shift+Esc, not ` or ^ etc"); static idCVar in_grabKeyboard("in_grabKeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL, "if enabled, grabs all keyboard input if mouse is grabbed (so keyboard shortcuts from the OS like Alt-Tab or Windows Key won't work)"); @@ -451,6 +457,7 @@ void Sys_InitInput() { #endif in_kbd.SetModified(); + Sys_GetConsoleKey(false); // initialize consoleKeymappingIdx from in_kbd #if SDL_VERSION_ATLEAST(2, 0, 0) const char* grabKeyboardEnv = SDL_getenv(SDL_HINT_GRAB_KEYBOARD); if ( grabKeyboardEnv ) { @@ -485,46 +492,89 @@ void Sys_InitScanTable() { } #endif + +struct ConsoleKeyMapping { + const char* langName; + unsigned char key; + unsigned char keyShifted; +}; + +static ConsoleKeyMapping consoleKeyMappings[] = { +#if SDL_VERSION_ATLEAST(2, 0, 0) + { "auto", 0 , 0 }, // special case: set current keycode for SDL_SCANCODE_GRAVE (no shifted keycode, though) +#endif + { "english", '`', '~' }, + { "french", '<', '>' }, + { "german", '^', 176 }, // ° + { "italian", '\\', '|' }, + { "spanish", 186, 170 }, // º ª + { "turkish", '"', 233 }, // é + { "norwegian", 124, 167 }, // | § + { "brazilian", '\'', '"' }, +}; +static int consoleKeyMappingIdx = 0; + +static void initConsoleKeyMapping() { + const int numMappings = sizeof(consoleKeyMappings)/sizeof(consoleKeyMappings[0]); + + idStr lang = in_kbd.GetString(); + consoleKeyMappingIdx = 0; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + consoleKeyMappings[0].key = 0; + if ( lang.Length() == 0 || lang.Icmp( "auto") == 0 ) { + // auto-detection (SDL2-only) + int keycode = SDL_GetKeyFromScancode( SDL_SCANCODE_GRAVE ); + if ( keycode > 0 && keycode <= 0xFF ) { + // the SDL keycode and dhewm3 keycode should be identical for the mappings, + // as it's ISO-8859-1 ("High ASCII") chars + for( int i=1; iPrintf( "Detected keyboard layout as \"%s\"\n", consoleKeyMappings[i].langName ); + break; + } + } + if ( consoleKeyMappingIdx == 0 ) { // not found in known mappings + consoleKeyMappings[0].key = keycode; + } + } + } else +#endif + { + for( int i=1; iWarning( "in_kbd is set to \"%s\", but the actual keycode of the 'console key' is %c (%d), not %c (%d), so this might not work that well..\n", + lang.c_str(), (unsigned char)keycode, keycode, consoleKeyMappings[i].key, consoleKeyMappings[i].key ); + } +#endif + break; + } + } + } +} + /* =============== Sys_GetConsoleKey =============== */ -unsigned char Sys_GetConsoleKey(bool shifted) { - static unsigned char keys[2] = { '`', '~' }; +unsigned char Sys_GetConsoleKey( bool shifted ) { - if (in_kbd.IsModified()) { - idStr lang = in_kbd.GetString(); - - if (lang.Length()) { - if (!lang.Icmp("french")) { - keys[0] = '<'; - keys[1] = '>'; - } else if (!lang.Icmp("german")) { - keys[0] = '^'; - keys[1] = 176; // ° - } else if (!lang.Icmp("italian")) { - keys[0] = '\\'; - keys[1] = '|'; - } else if (!lang.Icmp("spanish")) { - keys[0] = 186; // º - keys[1] = 170; // ª - } else if (!lang.Icmp("turkish")) { - keys[0] = '"'; - keys[1] = 233; // é - } else if (!lang.Icmp("norwegian")) { - keys[0] = 124; // | - keys[1] = 167; // § - } else if (!lang.Icmp("brazilian")) { - keys[0] = '\''; - keys[1] = '"'; - } - } + if ( in_ignoreConsoleKey.GetBool() ) { + return 0; + } + if ( in_kbd.IsModified() ) { + initConsoleKeyMapping(); in_kbd.ClearModified(); } - return shifted ? keys[1] : keys[0]; + return shifted ? consoleKeyMappings[consoleKeyMappingIdx].keyShifted : consoleKeyMappings[consoleKeyMappingIdx].key; } /* @@ -665,15 +715,17 @@ sysEvent_t Sys_GetEvent() { #if !SDL_VERSION_ATLEAST(2, 0, 0) key = mapkey(ev.key.keysym.sym); if (!key) { - unsigned char c; - // check if its an unmapped console key - if (ev.key.keysym.unicode == (c = Sys_GetConsoleKey(false))) { - key = c; - } else if (ev.key.keysym.unicode == (c = Sys_GetConsoleKey(true))) { - key = c; - } else { + if ( !in_ignoreConsoleKey.GetBool() ) { + // check if its an unmapped console key + int c = Sys_GetConsoleKey( (ev.key.keysym.mod & KMOD_SHIFT) != 0 ); + if (ev.key.keysym.unicode == c) { + key = c; + } + } + if (!key) { if (ev.type == SDL_KEYDOWN) - common->Warning("unmapped SDL key %d (0x%x)", ev.key.keysym.sym, ev.key.keysym.unicode); + common->Warning( "unmapped SDL key %d (0x%x) - if possible use SDL2 for better keyboard support", + ev.key.keysym.sym, ev.key.keysym.unicode ); continue; // handle next event } } @@ -699,18 +751,19 @@ sysEvent_t Sys_GetEvent() { key = mapkey(ev.key.keysym.sym); } - if(!key) { - if (ev.key.keysym.scancode == SDL_SCANCODE_GRAVE) { // TODO: always do this check? - key = Sys_GetConsoleKey(true); - } else { - // if the key couldn't be mapped so far, try to map the scancode to K_SC_* - key = getKeynumForSDLscancode(sc); - if(!key) { - if (ev.type == SDL_KEYDOWN) { - common->Warning("unmapped SDL key %d (scancode %d)", ev.key.keysym.sym, (int)sc); - } - continue; // handle next event + if ( !in_ignoreConsoleKey.GetBool() && ev.key.keysym.scancode == SDL_SCANCODE_GRAVE ) { + // that key between Esc, Tab and 1 is the console key + key = K_CONSOLE; + } + + if ( !key ) { + // if the key couldn't be mapped so far, try to map the scancode to K_SC_* + key = getKeynumForSDLscancode(sc); + if(!key) { + if (ev.type == SDL_KEYDOWN) { + common->Warning("unmapped SDL key %d (scancode %d)", ev.key.keysym.sym, (int)sc); } + continue; // handle next event } } } @@ -738,6 +791,8 @@ sysEvent_t Sys_GetEvent() { res.evType = SE_CHAR; res.evValue = ev.text.text[0]; + // TODO: translate to "ISO-8859-1" with SDL_iconv() ? + if (ev.text.text[1] != '\0') { memcpy(s, ev.text.text, SDL_TEXTINPUTEVENT_TEXT_SIZE); diff --git a/neo/ui/EditWindow.cpp b/neo/ui/EditWindow.cpp index 35ae0548..39106d86 100644 --- a/neo/ui/EditWindow.cpp +++ b/neo/ui/EditWindow.cpp @@ -210,7 +210,7 @@ const char *idEditWindow::HandleEvent(const sysEvent_t *event, bool *updateVisua int len = text.Length(); if ( event->evType == SE_CHAR ) { - if ( event->evValue == Sys_GetConsoleKey( false ) || event->evValue == Sys_GetConsoleKey( true ) ) { + if ( event->evValue == Sys_GetConsoleKey( idKeyInput::IsDown( K_SHIFT ) ) ) { return ""; }