diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9eec0ce9b..71390c104 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -781,6 +781,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_recordmultiplayerdemos); CV_RegisterVar(&cv_netdemosyncquality); CV_RegisterVar(&cv_maxdemosize); + CV_RegisterVar(&cv_nativekeyboard); } // ========================================================================= diff --git a/src/keys.h b/src/keys.h index cf3a0d060..d78ff574d 100644 --- a/src/keys.h +++ b/src/keys.h @@ -13,6 +13,10 @@ #ifndef __KEYS_H__ #define __KEYS_H__ +#include "console.h" + +extern consvar_t cv_nativekeyboard; + // These are the key codes as posted by the keyboard handler, // ascii codes are 0->127, // scancodes are 0x80 + 0->127 diff --git a/src/m_menu.c b/src/m_menu.c index 922841404..324b8dc67 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -147,6 +147,8 @@ boolean browselocalskins = false; boolean menuactive = false; boolean fromlevelselect = false; +boolean menu_text_input = false; + static INT32 coolalphatimer = 9; typedef enum @@ -3514,6 +3516,7 @@ boolean M_Responder(event_t *ev) // Handle menuitems which need a specific key handling if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER) { + menu_text_input = true; if (shiftdown && ch >= 32 && ch <= 127) ch = shiftxform[ch]; routine(ch); @@ -3554,7 +3557,7 @@ boolean M_Responder(event_t *ev) { if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING) { - + menu_text_input = true; if (shiftdown && ch >= 32 && ch <= 127) ch = shiftxform[ch]; if (M_ChangeStringCvar(ch)) @@ -3749,6 +3752,7 @@ boolean M_Responder(event_t *ev) case KEY_BACKSPACE: if ((currentMenu->menuitems[itemOn].status) == IT_CONTROL) { + menu_text_input = false; //never use native layout for control setup // detach any keys associated with the game control G_ClearControlKeys(setupcontrols, currentMenu->menuitems[itemOn].alphaKey); S_StartSound(NULL, sfx_shldls); diff --git a/src/m_menu.h b/src/m_menu.h index 8d1ebb723..9a4cc1e92 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -91,6 +91,8 @@ void M_QuitResponse(INT32 ch); // Determines whether to show a level in the list boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); +extern boolean menu_text_input; + // flags for items in the menu // menu handle (what we do when key is pressed #define IT_TYPE 14 // (2+4+8) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 89c7bf555..dbdfee229 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -75,6 +75,7 @@ #include "../command.h" #include "sdlmain.h" #include "../i_system.h" +#include "../hu_stuff.h" // for chat_on #ifdef HWRENDER #include "../hardware/hw_main.h" #include "../hardware/hw_drv.h" @@ -109,6 +110,7 @@ boolean a2c = false; #endif boolean highcolor = false; +consvar_t cv_nativekeyboard = {"nativekeyboard", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; static void Impl_SetVsync(void); @@ -382,6 +384,149 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code) return 0; } +// Get the equivalent ASCII (Unicode?) character for a keypress. +static INT32 GetTypedChar(SDL_Scancode code, SDL_Keysym *sym) +{ + SDL_Event next_event; + + // Special cases, where we always return a fixed value. + switch (sym->sym) + { + case SDLK_BACKSPACE: return KEY_BACKSPACE; + case SDLK_RETURN: return KEY_ENTER; + default: + break; + } + + // The following is a gross hack, but I don't see an easier way + // of doing this within the SDL2 API (in SDL1 it was easier). + // We want to get the fully transformed input character associated + // with this keypress - correct keyboard layout, appropriately + // transformed by any modifier keys, etc. So peek ahead in the SDL + // event queue and see if the key press is immediately followed by + // an SDL_TEXTINPUT event. If it is, it's reasonable to assume the + // key press and the text input are connected. Technically the SDL + // API does not guarantee anything of the sort, but in practice this + // is what happens and I've verified it through manual inspect of + // the SDL source code. + // + // In an ideal world we'd split out ev_keydown into a separate + // ev_textinput event, as SDL2 has done. But this doesn't work + // (I experimented with the idea), because lots of Doom's code is + // based around different responders "eating" events to stop them + // being passed on to another responder. If code is listening for + // a text input, it cannot block the corresponding keydown events + // which can affect other responders. + // + // So we're stuck with this as a rather fragile alternative. + + if (SDL_PeepEvents(&next_event, 1, SDL_PEEKEVENT, + SDL_FIRSTEVENT, SDL_LASTEVENT) == 1 + && next_event.type == SDL_TEXTINPUT) + { + // If an SDL_TEXTINPUT event is found, we always assume it + // matches the key press. The input text must be a single + // ASCII character - if it isn't, it's possible the input + // char is a Unicode value instead; better to send a null + // character than the unshifted key. + if (strlen(next_event.text.text) == 1 && + (next_event.text.text[0] >= ' ' && next_event.text.text[0] <= '~')) { + return next_event.text.text[0]; + } else { + return '\0'; + } + } + + if (code >= SDL_SCANCODE_1 && code <= SDL_SCANCODE_9) + { + return code - SDL_SCANCODE_1 + '1'; + } + else if (code == SDL_SCANCODE_0) + { + return '0'; + } + if (code >= SDL_SCANCODE_F1 && code <= SDL_SCANCODE_F10) + { + return KEY_F1 + (code - SDL_SCANCODE_F1); + } + + switch (code) + { + // F11 and F12 are separated from the rest of the function keys + case SDL_SCANCODE_F11: return KEY_F11; + case SDL_SCANCODE_F12: return KEY_F12; + + case SDL_SCANCODE_KP_0: return KEY_KEYPAD0; + case SDL_SCANCODE_KP_1: return KEY_KEYPAD1; + case SDL_SCANCODE_KP_2: return KEY_KEYPAD2; + case SDL_SCANCODE_KP_3: return KEY_KEYPAD3; + case SDL_SCANCODE_KP_4: return KEY_KEYPAD4; + case SDL_SCANCODE_KP_5: return KEY_KEYPAD5; + case SDL_SCANCODE_KP_6: return KEY_KEYPAD6; + case SDL_SCANCODE_KP_7: return KEY_KEYPAD7; + case SDL_SCANCODE_KP_8: return KEY_KEYPAD8; + case SDL_SCANCODE_KP_9: return KEY_KEYPAD9; + + case SDL_SCANCODE_RETURN: return KEY_ENTER; + case SDL_SCANCODE_ESCAPE: return KEY_ESCAPE; + case SDL_SCANCODE_BACKSPACE: return KEY_BACKSPACE; + case SDL_SCANCODE_TAB: return KEY_TAB; + case SDL_SCANCODE_SPACE: return KEY_SPACE; + case SDL_SCANCODE_MINUS: return KEY_MINUS; + case SDL_SCANCODE_EQUALS: return KEY_EQUALS; + case SDL_SCANCODE_LEFTBRACKET: return '['; + case SDL_SCANCODE_RIGHTBRACKET: return ']'; + case SDL_SCANCODE_BACKSLASH: return '\\'; + case SDL_SCANCODE_NONUSHASH: return '#'; + case SDL_SCANCODE_SEMICOLON: return ';'; + case SDL_SCANCODE_APOSTROPHE: return '\''; + case SDL_SCANCODE_GRAVE: return '`'; + case SDL_SCANCODE_COMMA: return ','; + case SDL_SCANCODE_PERIOD: return '.'; + case SDL_SCANCODE_SLASH: return '/'; + case SDL_SCANCODE_CAPSLOCK: return KEY_CAPSLOCK; + case SDL_SCANCODE_PRINTSCREEN: return 0; // undefined? + case SDL_SCANCODE_SCROLLLOCK: return KEY_SCROLLLOCK; + case SDL_SCANCODE_PAUSE: return KEY_PAUSE; + case SDL_SCANCODE_INSERT: return KEY_INS; + case SDL_SCANCODE_HOME: return KEY_HOME; + case SDL_SCANCODE_PAGEUP: return KEY_PGUP; + case SDL_SCANCODE_DELETE: return KEY_DEL; + case SDL_SCANCODE_END: return KEY_END; + case SDL_SCANCODE_PAGEDOWN: return KEY_PGDN; + case SDL_SCANCODE_RIGHT: return KEY_RIGHTARROW; + case SDL_SCANCODE_LEFT: return KEY_LEFTARROW; + case SDL_SCANCODE_DOWN: return KEY_DOWNARROW; + case SDL_SCANCODE_UP: return KEY_UPARROW; + case SDL_SCANCODE_NUMLOCKCLEAR: return KEY_NUMLOCK; + case SDL_SCANCODE_KP_DIVIDE: return KEY_KPADSLASH; + case SDL_SCANCODE_KP_MULTIPLY: return '*'; // undefined? + case SDL_SCANCODE_KP_MINUS: return KEY_MINUSPAD; + case SDL_SCANCODE_KP_PLUS: return KEY_PLUSPAD; + case SDL_SCANCODE_KP_ENTER: return KEY_ENTER; + case SDL_SCANCODE_KP_PERIOD: return KEY_KPADDEL; + case SDL_SCANCODE_NONUSBACKSLASH: return '\\'; + + case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT; + case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT; + case SDL_SCANCODE_LCTRL: return KEY_LCTRL; + case SDL_SCANCODE_RCTRL: return KEY_RCTRL; + case SDL_SCANCODE_LALT: return KEY_LALT; + case SDL_SCANCODE_RALT: return KEY_RALT; + case SDL_SCANCODE_LGUI: return KEY_LEFTWIN; + case SDL_SCANCODE_RGUI: return KEY_RIGHTWIN; + default: break; + } + + if (code >= SDL_SCANCODE_A && code <= SDL_SCANCODE_Z) + { + // get lowercase ASCII + return code - SDL_SCANCODE_A + 'a'; + } + + return 0; +} + static void SDLdoGrabMouse(void) { SDL_ShowCursor(SDL_DISABLE); @@ -696,7 +841,12 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type) { return; } - event.data1 = Impl_SDL_Scancode_To_Keycode(evt.keysym.scancode); + + if (cv_nativekeyboard.value && (chat_on || CON_Ready() || (menu_text_input && menuactive))) //only use this this if on chat or console or the current menu wants inputs from us (except if its the control setup menu ig) + event.data1 = GetTypedChar(evt.keysym.scancode, &evt.keysym); + else + event.data1 = Impl_SDL_Scancode_To_Keycode(evt.keysym.scancode); + if (event.data1) D_PostEvent(&event); }