- major refactoring on the input code.

* moved the ASCII conversion hackery in SDLayer to a subfunction because this made things just messy.
* integrated the keyboard callback's functionality directly into inputState for consolidation purposes. This was yet another independent layer in the keyboard management.
* hook up D_PostEvent as the central place to dispatch keyboard input. This is now the only function that is getting called from the backend and a major prerequiside for swapping out the backend for GZDoom's.

Todo: Route mouse input through that, too.
This commit is contained in:
Christoph Oelckers 2019-11-04 00:55:49 +01:00
parent b747df60ee
commit b4f91de7d3
5 changed files with 131 additions and 181 deletions

View File

@ -26,12 +26,6 @@ int32_t g_borderless=2;
// input
char inputdevices = 0;
void (*keypresscallback)(int32_t, int32_t);
void keySetCallback(void (*callback)(int32_t, int32_t)) { keypresscallback = callback; }
vec2_t g_mousePos;
vec2_t g_mouseAbs;
int32_t g_mouseBits;

View File

@ -136,6 +136,7 @@ uint16_t joydead[9], joysatur[9];
#define MAX_ERRORTEXT 4096
//==========================================================================
//
// I_Error
@ -1871,6 +1872,85 @@ int32_t handleevents_sdlcommon(SDL_Event *ev)
return 0;
}
// So this is how the engine handles text input?
// Argh. This is just gross.
int scancodetoasciihack(SDL_Event &ev)
{
SDL_Keycode keyvalue = ev.key.keysym.sym;
// Modifiers that have to be held down to be effective
// (excludes KMOD_NUM, for example).
static const int MODIFIERS =
KMOD_LSHIFT|KMOD_RSHIFT|KMOD_LCTRL|KMOD_RCTRL|
KMOD_LALT|KMOD_RALT|KMOD_LGUI|KMOD_RGUI;
// XXX: see osd.c, OSD_HandleChar(), there are more...
if (
(sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER ||
sc == SDL_SCANCODE_ESCAPE ||
sc == SDL_SCANCODE_BACKSPACE ||
sc == SDL_SCANCODE_TAB ||
(((ev.key.keysym.mod) & MODIFIERS) == KMOD_LCTRL &&
(sc >= SDL_SCANCODE_A && sc <= SDL_SCANCODE_Z))))
{
char keyvalue;
switch (sc)
{
case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: keyvalue = '\r'; break;
case SDL_SCANCODE_ESCAPE: keyvalue = 27; break;
case SDL_SCANCODE_BACKSPACE: keyvalue = '\b'; break;
case SDL_SCANCODE_TAB: keyvalue = '\t'; break;
default: keyvalue = sc - SDL_SCANCODE_A + 1; break; // Ctrl+A --> 1, etc.
}
}
else if (
ev.key.keysym.sym != g_keyAsciiTable[OSD_OSDKey()] && !SDL_IsTextInputActive()) // What is this? Preventing key repeat or what? :?
{
/*
Necessary for Duke 3D's method of entering cheats to work without showing IMEs.
SDL_TEXTINPUT is preferable overall, but with bitmap fonts it has no advantage.
*/
// Note that this is not how text input is supposed to be handled!
if ('a' <= keyvalue && keyvalue <= 'z')
{
if (!!(ev.key.keysym.mod & KMOD_SHIFT) ^ !!(ev.key.keysym.mod & KMOD_CAPS))
keyvalue -= 'a'-'A';
}
else if (ev.key.keysym.mod & KMOD_SHIFT)
{
keyvalue = g_keyAsciiTableShift[code];
}
else if (ev.key.keysym.mod & KMOD_NUM) // && !(ev.key.keysym.mod & KMOD_SHIFT)
{
switch (keyvalue)
{
case SDLK_KP_1: keyvalue = '1'; break;
case SDLK_KP_2: keyvalue = '2'; break;
case SDLK_KP_3: keyvalue = '3'; break;
case SDLK_KP_4: keyvalue = '4'; break;
case SDLK_KP_5: keyvalue = '5'; break;
case SDLK_KP_6: keyvalue = '6'; break;
case SDLK_KP_7: keyvalue = '7'; break;
case SDLK_KP_8: keyvalue = '8'; break;
case SDLK_KP_9: keyvalue = '9'; break;
case SDLK_KP_0: keyvalue = '0'; break;
case SDLK_KP_PERIOD: keyvalue = '.'; break;
case SDLK_KP_COMMA: keyvalue = ','; break;
}
}
switch (keyvalue)
{
case SDLK_KP_DIVIDE: keyvalue = '/'; break;
case SDLK_KP_MULTIPLY: keyvalue = '*'; break;
case SDLK_KP_MINUS: keyvalue = '-'; break;
case SDLK_KP_PLUS: keyvalue = '+'; break;
}
}
if (keyvalue >= 0x80) keyvalue = 0; // Sadly ASCII only...
return keyvalue;
}
int32_t handleevents_pollsdl(void);
#if SDL_MAJOR_VERSION != 1
// SDL 2.0 specific event handling
@ -1901,158 +1981,33 @@ int32_t handleevents_pollsdl(void)
case SDL_KEYUP:
{
auto const &sc = ev.key.keysym.scancode;
code = keytranslation[sc];
// Modifiers that have to be held down to be effective
// (excludes KMOD_NUM, for example).
static const int MODIFIERS =
KMOD_LSHIFT|KMOD_RSHIFT|KMOD_LCTRL|KMOD_RCTRL|
KMOD_LALT|KMOD_RALT|KMOD_LGUI|KMOD_RGUI;
// The pause key generates a release event right after
// the pressing one. As a result, it gets unseen
// by the game most of the time.
if (code == 0x59 && ev.type == SDL_KEYUP) // pause
break;
// XXX: see osd.c, OSD_HandleChar(), there are more...
if (ev.key.type == SDL_KEYDOWN && !inputState.keyBufferFull() &&
(sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER ||
sc == SDL_SCANCODE_ESCAPE ||
sc == SDL_SCANCODE_BACKSPACE ||
sc == SDL_SCANCODE_TAB ||
(((ev.key.keysym.mod) & MODIFIERS) == KMOD_LCTRL &&
(sc >= SDL_SCANCODE_A && sc <= SDL_SCANCODE_Z))))
{
char keyvalue;
switch (sc)
{
case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: keyvalue = '\r'; break;
case SDL_SCANCODE_ESCAPE: keyvalue = 27; break;
case SDL_SCANCODE_BACKSPACE: keyvalue = '\b'; break;
case SDL_SCANCODE_TAB: keyvalue = '\t'; break;
default: keyvalue = sc - SDL_SCANCODE_A + 1; break; // Ctrl+A --> 1, etc.
}
if (OSD_HandleChar(keyvalue))
inputState.keyBufferInsert(keyvalue);
}
else if (ev.key.type == SDL_KEYDOWN &&
ev.key.keysym.sym != g_keyAsciiTable[OSD_OSDKey()] && !inputState.keyBufferFull() &&
!SDL_IsTextInputActive())
{
/*
Necessary for Duke 3D's method of entering cheats to work without showing IMEs.
SDL_TEXTINPUT is preferable overall, but with bitmap fonts it has no advantage.
*/
SDL_Keycode keyvalue = ev.key.keysym.sym;
int keyvalue = ev.type == SDL_KEYDOWN? scancodetoasciihack(ev) : 0;
if (keyvalue > 0)
{
keyvalue = OSD_HandleChar(keyvalue); // returns the char if it doesn't process it.
if (keyvalue == 0) break;
}
j = OSD_HandleScanCode(code, (ev.type == SDL_KEYDOWN));
// returns are:
// j == -1: Console was opened
// j == 0: Console is active and used the key
// j == 2: Console is inactive and did not use the key
if (j == -1) inputState.ClearKeysDown(); // Flush the entire keyboard state for the game when the console opens.
if (j <= 0) break; // Do not pass on to the game
if ('a' <= keyvalue && keyvalue <= 'z')
{
if (!!(ev.key.keysym.mod & KMOD_SHIFT) ^ !!(ev.key.keysym.mod & KMOD_CAPS))
keyvalue -= 'a'-'A';
}
else if (ev.key.keysym.mod & KMOD_SHIFT)
{
switch (keyvalue)
{
case '\'': keyvalue = '"'; break;
case ',': keyvalue = '<'; break;
case '-': keyvalue = '_'; break;
case '.': keyvalue = '>'; break;
case '/': keyvalue = '?'; break;
case '0': keyvalue = ')'; break;
case '1': keyvalue = '!'; break;
case '2': keyvalue = '@'; break;
case '3': keyvalue = '#'; break;
case '4': keyvalue = '$'; break;
case '5': keyvalue = '%'; break;
case '6': keyvalue = '^'; break;
case '7': keyvalue = '&'; break;
case '8': keyvalue = '*'; break;
case '9': keyvalue = '('; break;
case ';': keyvalue = ':'; break;
case '=': keyvalue = '+'; break;
case '[': keyvalue = '{'; break;
case '\\': keyvalue = '|'; break;
case ']': keyvalue = '}'; break;
case '`': keyvalue = '~'; break;
}
}
else if (ev.key.keysym.mod & KMOD_NUM) // && !(ev.key.keysym.mod & KMOD_SHIFT)
{
switch (keyvalue)
{
case SDLK_KP_1: keyvalue = '1'; break;
case SDLK_KP_2: keyvalue = '2'; break;
case SDLK_KP_3: keyvalue = '3'; break;
case SDLK_KP_4: keyvalue = '4'; break;
case SDLK_KP_5: keyvalue = '5'; break;
case SDLK_KP_6: keyvalue = '6'; break;
case SDLK_KP_7: keyvalue = '7'; break;
case SDLK_KP_8: keyvalue = '8'; break;
case SDLK_KP_9: keyvalue = '9'; break;
case SDLK_KP_0: keyvalue = '0'; break;
case SDLK_KP_PERIOD: keyvalue = '.'; break;
case SDLK_KP_COMMA: keyvalue = ','; break;
}
}
switch (keyvalue)
{
case SDLK_KP_DIVIDE: keyvalue = '/'; break;
case SDLK_KP_MULTIPLY: keyvalue = '*'; break;
case SDLK_KP_MINUS: keyvalue = '-'; break;
case SDLK_KP_PLUS: keyvalue = '+'; break;
}
if ((unsigned)keyvalue <= 0x7Fu)
{
if (OSD_HandleChar(keyvalue))
inputState.keyBufferInsert(keyvalue);
}
}
// initprintf("SDL2: got key %d, %d, %u\n", ev.key.keysym.scancode, code, ev.key.type);
// hook in the osd
if ((j = OSD_HandleScanCode(code, (ev.key.type == SDL_KEYDOWN))) <= 0)
{
if (j == -1) // osdkey
{
for (j = 0; j < NUMKEYS; ++j)
{
if (inputState.GetKeyStatus(j))
{
if (keypresscallback)
keypresscallback(j, 0);
}
inputState.keySetState(j, 0);
}
}
break;
}
if (ev.key.type == SDL_KEYDOWN)
{
if (!inputState.GetKeyStatus(code))
{
if (keypresscallback)
keypresscallback(code, 1);
}
inputState.keySetState(code, 1);
}
else
{
# if 1
// The pause key generates a release event right after
// the pressing one. As a result, it gets unseen
// by the game most of the time.
if (code == 0x59) // pause
break;
# endif
inputState.keySetState(code, 0);
if (keypresscallback)
keypresscallback(code, 0);
}
event_t ev = { (uint8_t)ev.type == SDL_KEYUP? EV_KeyUp : EV_KeyDown, 0, (int16_t)code, (int16_t)keyvalue };
D_PostEvent(&ev);
break;
}

View File

@ -23,6 +23,7 @@
#include "c_bind.h"
#include "d_event.h"
#include "d_gui.h"
#include "inputstate.h"
int eventhead;
int eventtail;
@ -193,6 +194,15 @@ void D_PostEvent (const event_t *ev)
}
}
#endif
// Add the key to the global keyboard state.
// This is probably the biggest roadblock with the input system as it undermines a proper event driven approach.
// Too much code depends on just checking this instead of waiting for events to happen.
// Here's also definitely not the best place to maintain the keyboard state but right now it's unavoidable to do this outside the event processing because so much code depends on it.
// Once all those busy waiting loops can poll the event queue for a proper skip event, this will mostly go away.
inputState.AddEvent(ev);
// Also add it to the event queue - this is where everything should transition to eventually.
eventhead = (eventhead+1)&(NUM_EVENTS-1);
}
@ -230,4 +240,4 @@ void D_RemoveNextCharEvent()
}
}
}

View File

@ -247,11 +247,14 @@ public:
void keySetState(int32_t key, int32_t state)
{
if (state && !GetKeyStatus(key))
{
KB_LastScan = key;
}
SetKeyStatus(key, state);
event_t ev = { (uint8_t)(state ? EV_KeyDown : EV_KeyUp), 0, (int16_t)key };
D_PostEvent(&ev);
if (state)
{
g_keyFIFO[g_keyFIFOend] = key;
@ -290,6 +293,12 @@ public:
return c;
}
void keySetChar(int key)
{
g_keyAsciiFIFO[g_keyAsciiEnd] = key;
g_keyAsciiEnd = ((g_keyAsciiEnd + 1) & (KEYFIFOSIZ - 1));
}
void keyFlushChars(void)
{
@ -323,19 +332,15 @@ public:
KB_LastScan = 0;
ClearAllKeyStatus();
}
void AddEvent(const event_t *ev)
{
keySetState(ev->data1, ev->type == EV_KeyDown);
if (ev->data2) keySetChar(ev->data2);
}
};
extern InputState inputState;
static inline void KB_KeyEvent(int32_t scancode, int32_t keypressed)
{
if (keypressed) inputState.SetLastScanCode(scancode);
}
void keySetCallback(void (*callback)(int32_t, int32_t));
inline void KB_Startup(void) { keySetCallback(KB_KeyEvent); }
inline void KB_Shutdown(void) { keySetCallback((void (*)(int32_t, int32_t))NULL); }

View File

@ -48,24 +48,10 @@ static FORCE_INLINE void Menu_StartTextInput()
{
inputState.keyFlushChars();
inputState.ClearKeysDown();
#if defined EDUKE32_TOUCH_DEVICES && defined SDL_MAJOR_VERSION && SDL_MAJOR_VERSION > 1
# if defined __ANDROID__
AndroidShowKeyboard(1);
# else
SDL_StartTextInput();
# endif
#endif
}
static FORCE_INLINE void Menu_StopTextInput()
{
#if defined EDUKE32_TOUCH_DEVICES && defined SDL_MAJOR_VERSION && SDL_MAJOR_VERSION > 1
# if defined __ANDROID__
AndroidShowKeyboard(0);
# else
SDL_StopTextInput();
# endif
#endif
}
static FORCE_INLINE void rotatesprite_ybounds(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum, int8_t dashade, uint8_t dapalnum, int32_t dastat, int32_t ydim_upper, int32_t ydim_lower)