mirror of
https://github.com/dhewm/dhewm3.git
synced 2025-03-22 02:31:03 +00:00
Support (hopefully) all keyboard keys via scancodes, #323
If a key is pressed whichs SDL_Keycode isn't known to Doom3 (has no corresponding K_* constant), its SDL_Scancode is mapped to the corresponding newly added K_SC_* scancode constant. I think I have K_SC_* constants for all keys that differ between keyboard layouts (which is mostly printable characters; F1-F12, Ctrl, Shift, ... should be the same on all layouts, which means that e.g. SDL_SCANCODE_F1 always belongs to SDLK_F1 which the old code already maps to Doom3's K_F1). What's extra nice (IMO) is that when Doom3 requests a *localized* name of the key (like for showing in the bindings menu), we actually use the name of the SDL_Keycode that *currently* belongs to the scancode, and esp. the "Western High-ASCII characters" (ISO-8859-1) supported by Doom3 like Ä or Ñ are displayed correctly. (I already implemented a very similar hack in Yamagi Quake II and reused the list of scancodes) This should fix most of the problems reported in #323
This commit is contained in:
parent
61a49a2547
commit
ae2d3a7e99
4 changed files with 274 additions and 10 deletions
|
@ -182,13 +182,12 @@ static const keyname_t keynames[] =
|
|||
|
||||
{"SEMICOLON", ';', "#str_07129"}, // because a raw semicolon separates commands
|
||||
{"APOSTROPHE", '\'', "#str_07130"}, // because a raw apostrophe messes with parsing
|
||||
{"QUOTE", '"', ""}, // raw quote can't be good either
|
||||
|
||||
{NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const int MAX_KEYS = 256;
|
||||
static const int MAX_KEYS = K_LAST_KEY+1; // DG: was 256, made it more flexible
|
||||
|
||||
class idKey {
|
||||
public:
|
||||
|
@ -250,6 +249,13 @@ void idKeyInput::ArgCompletion_KeyName( const idCmdArgs &args, void(*callback)(
|
|||
for ( kn = keynames; kn->name; kn++ ) {
|
||||
callback( va( "%s %s", args.Argv( 0 ), kn->name ) );
|
||||
}
|
||||
|
||||
for( int scKey = K_FIRST_SCANCODE; scKey <= K_LAST_SCANCODE; ++scKey ) {
|
||||
const char* scName = Sys_GetScancodeName( scKey );
|
||||
if ( scName != NULL ) {
|
||||
callback( va( "%s %s", args.Argv( 0 ), scName ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -330,6 +336,11 @@ int idKeyInput::StringToKeyNum( const char *str ) {
|
|||
return n1 * 16 + n2;
|
||||
}
|
||||
|
||||
// DG: scancode names start with "SC_"
|
||||
if ( idStr::Icmpn( str, "SC_", 3 ) == 0 ) {
|
||||
return Sys_GetKeynumForScancodeName( str );
|
||||
}
|
||||
|
||||
// scan for a text match
|
||||
for ( kn = keynames; kn->name; kn++ ) {
|
||||
if ( !idStr::Icmp( str, kn->name ) ) {
|
||||
|
@ -346,6 +357,9 @@ idKeyInput::KeyNumToString
|
|||
|
||||
Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the
|
||||
given keynum.
|
||||
|
||||
NOTE: with localized = true, the returned string is only valid until the next call (at least for K_SC_*)!
|
||||
(currently this is no problem)
|
||||
===================
|
||||
*/
|
||||
const char *idKeyInput::KeyNumToString( int keynum, bool localized ) {
|
||||
|
@ -357,7 +371,7 @@ const char *idKeyInput::KeyNumToString( int keynum, bool localized ) {
|
|||
return "<KEY NOT FOUND>";
|
||||
}
|
||||
|
||||
if ( keynum < 0 || keynum > 255 ) {
|
||||
if ( keynum < 0 || keynum >= MAX_KEYS ) {
|
||||
return "<OUT OF RANGE>";
|
||||
}
|
||||
|
||||
|
@ -368,6 +382,18 @@ const char *idKeyInput::KeyNumToString( int keynum, bool localized ) {
|
|||
return tinystr;
|
||||
}
|
||||
|
||||
if ( keynum >= K_FIRST_SCANCODE && keynum <= K_LAST_SCANCODE ) {
|
||||
const char* scName = NULL;
|
||||
if ( localized ) {
|
||||
scName = Sys_GetLocalizedScancodeName( keynum );
|
||||
} else {
|
||||
scName = Sys_GetScancodeName( keynum );
|
||||
}
|
||||
if ( scName != NULL ) {
|
||||
return scName;
|
||||
}
|
||||
}
|
||||
|
||||
// check for a key string
|
||||
for ( kn = keynames; kn->name; kn++ ) {
|
||||
if ( keynum == kn->keynum ) {
|
||||
|
@ -709,7 +735,7 @@ void idKeyInput::PreliminaryKeyEvent( int keynum, bool down ) {
|
|||
keys[keynum].down = down;
|
||||
|
||||
#ifdef ID_DOOM_LEGACY
|
||||
if ( down ) {
|
||||
if ( down && keynum < 127 ) { // DG: only ASCII keys are of interest here
|
||||
lastKeys[ 0 + ( lastKeyIndex & 15 )] = keynum;
|
||||
lastKeys[16 + ( lastKeyIndex & 15 )] = keynum;
|
||||
lastKeyIndex = ( lastKeyIndex + 1 ) & 15;
|
||||
|
|
|
@ -197,9 +197,79 @@ typedef enum {
|
|||
|
||||
K_PRINT_SCR = 252, // SysRq / PrintScr
|
||||
K_RIGHT_ALT = 253, // used by some languages as "Alt-Gr"
|
||||
K_LAST_KEY = 254 // this better be < 256!
|
||||
|
||||
// DG: map all relevant scancodes from SDL to K_SC_* (taken from Yamagi Quake II)
|
||||
// (relevant are ones that are likely to be keyboardlayout-dependent,
|
||||
// i.e. printable characters of sorts, *not* Ctrl, Alt, F1, Del, ...)
|
||||
K_FIRST_SCANCODE = 256,
|
||||
|
||||
// !!! NOTE: if you add a scancode here, make sure to also add it to !!!
|
||||
// !!! scancodemappings[] in sys/events.cpp (and preserve order) !!!
|
||||
K_SC_A = K_FIRST_SCANCODE,
|
||||
K_SC_B,
|
||||
K_SC_C,
|
||||
K_SC_D,
|
||||
K_SC_E,
|
||||
K_SC_F,
|
||||
K_SC_G,
|
||||
K_SC_H,
|
||||
K_SC_I,
|
||||
K_SC_J,
|
||||
K_SC_K,
|
||||
K_SC_L,
|
||||
K_SC_M,
|
||||
K_SC_N,
|
||||
K_SC_O,
|
||||
K_SC_P,
|
||||
K_SC_Q,
|
||||
K_SC_R,
|
||||
K_SC_S,
|
||||
K_SC_T,
|
||||
K_SC_U,
|
||||
K_SC_V,
|
||||
K_SC_W,
|
||||
K_SC_X,
|
||||
K_SC_Y,
|
||||
K_SC_Z,
|
||||
// leaving out SDL_SCANCODE_1 ... _0, we handle them separately already
|
||||
// also return, escape, backspace, tab, space, already handled as keycodes
|
||||
K_SC_MINUS,
|
||||
K_SC_EQUALS,
|
||||
K_SC_LEFTBRACKET,
|
||||
K_SC_RIGHTBRACKET,
|
||||
K_SC_BACKSLASH,
|
||||
K_SC_NONUSHASH,
|
||||
K_SC_SEMICOLON,
|
||||
K_SC_APOSTROPHE,
|
||||
K_SC_GRAVE,
|
||||
K_SC_COMMA,
|
||||
K_SC_PERIOD,
|
||||
K_SC_SLASH,
|
||||
// leaving out lots of keys incl. from keypad, we already handle them as normal keys
|
||||
K_SC_NONUSBACKSLASH,
|
||||
K_SC_INTERNATIONAL1, /**< used on Asian keyboards, see footnotes in USB doc */
|
||||
K_SC_INTERNATIONAL2,
|
||||
K_SC_INTERNATIONAL3, /**< Yen */
|
||||
K_SC_INTERNATIONAL4,
|
||||
K_SC_INTERNATIONAL5,
|
||||
K_SC_INTERNATIONAL6,
|
||||
K_SC_INTERNATIONAL7,
|
||||
K_SC_INTERNATIONAL8,
|
||||
K_SC_INTERNATIONAL9,
|
||||
K_SC_THOUSANDSSEPARATOR,
|
||||
K_SC_DECIMALSEPARATOR,
|
||||
K_SC_CURRENCYUNIT,
|
||||
K_SC_CURRENCYSUBUNIT,
|
||||
|
||||
K_LAST_SCANCODE = K_SC_CURRENCYSUBUNIT, // TODO: keep up to date!
|
||||
|
||||
|
||||
// FIXME: maybe move everything joystick related here
|
||||
|
||||
K_LAST_KEY // DG: this said "this better be < 256!"; I hope I fixed all places in code assuming this..
|
||||
} keyNum_t;
|
||||
|
||||
enum { K_NUM_SCANCODES = K_LAST_SCANCODE - K_FIRST_SCANCODE + 1 };
|
||||
|
||||
class idKeyInput {
|
||||
public:
|
||||
|
|
|
@ -94,6 +94,157 @@ struct mouse_poll_t {
|
|||
static idList<kbd_poll_t> kbd_polls;
|
||||
static idList<mouse_poll_t> mouse_polls;
|
||||
|
||||
struct scancodename_t {
|
||||
int sdlScancode;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
// scancodenames[keynum - K_FIRST_SCANCODE] belongs to keynum
|
||||
static scancodename_t scancodemappings[] = {
|
||||
// NOTE: must be kept in sync with the K_SC_* section of keyNum_t in framework/KeyInput.h !
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
#define D3_SC_MAPPING(X) { SDL_SCANCODE_ ## X , "SC_" #X }
|
||||
#else // SDL1.2 doesn't have scancodes
|
||||
#define D3_SC_MAPPING(X) { 0 , "SC_" #X }
|
||||
#endif
|
||||
|
||||
D3_SC_MAPPING(A), // { SDL_SCANCODE_A, "SC_A" },
|
||||
D3_SC_MAPPING(B),
|
||||
D3_SC_MAPPING(C),
|
||||
D3_SC_MAPPING(D),
|
||||
D3_SC_MAPPING(E),
|
||||
D3_SC_MAPPING(F),
|
||||
D3_SC_MAPPING(G),
|
||||
D3_SC_MAPPING(H),
|
||||
D3_SC_MAPPING(I),
|
||||
D3_SC_MAPPING(J),
|
||||
D3_SC_MAPPING(K),
|
||||
D3_SC_MAPPING(L),
|
||||
D3_SC_MAPPING(M),
|
||||
D3_SC_MAPPING(N),
|
||||
D3_SC_MAPPING(O),
|
||||
D3_SC_MAPPING(P),
|
||||
D3_SC_MAPPING(Q),
|
||||
D3_SC_MAPPING(R),
|
||||
D3_SC_MAPPING(S),
|
||||
D3_SC_MAPPING(T),
|
||||
D3_SC_MAPPING(U),
|
||||
D3_SC_MAPPING(V),
|
||||
D3_SC_MAPPING(W),
|
||||
D3_SC_MAPPING(X),
|
||||
D3_SC_MAPPING(Y),
|
||||
D3_SC_MAPPING(Z),
|
||||
// leaving out SDL_SCANCODE_1 ... _0, we handle them separately already
|
||||
// also return, escape, backspace, tab, space, already handled as keycodes
|
||||
D3_SC_MAPPING(MINUS),
|
||||
D3_SC_MAPPING(EQUALS),
|
||||
D3_SC_MAPPING(LEFTBRACKET),
|
||||
D3_SC_MAPPING(RIGHTBRACKET),
|
||||
D3_SC_MAPPING(BACKSLASH),
|
||||
D3_SC_MAPPING(NONUSHASH),
|
||||
D3_SC_MAPPING(SEMICOLON),
|
||||
D3_SC_MAPPING(APOSTROPHE),
|
||||
D3_SC_MAPPING(GRAVE),
|
||||
D3_SC_MAPPING(COMMA),
|
||||
D3_SC_MAPPING(PERIOD),
|
||||
D3_SC_MAPPING(SLASH),
|
||||
// leaving out lots of key incl. from keypad, we already handle them as normal keys
|
||||
D3_SC_MAPPING(NONUSBACKSLASH),
|
||||
D3_SC_MAPPING(INTERNATIONAL1), /**< used on Asian keyboards, see footnotes in USB doc */
|
||||
D3_SC_MAPPING(INTERNATIONAL2),
|
||||
D3_SC_MAPPING(INTERNATIONAL3), /**< Yen */
|
||||
D3_SC_MAPPING(INTERNATIONAL4),
|
||||
D3_SC_MAPPING(INTERNATIONAL5),
|
||||
D3_SC_MAPPING(INTERNATIONAL6),
|
||||
D3_SC_MAPPING(INTERNATIONAL7),
|
||||
D3_SC_MAPPING(INTERNATIONAL8),
|
||||
D3_SC_MAPPING(INTERNATIONAL9),
|
||||
D3_SC_MAPPING(THOUSANDSSEPARATOR),
|
||||
D3_SC_MAPPING(DECIMALSEPARATOR),
|
||||
D3_SC_MAPPING(CURRENCYUNIT),
|
||||
D3_SC_MAPPING(CURRENCYSUBUNIT)
|
||||
|
||||
#undef D3_SC_MAPPING
|
||||
};
|
||||
|
||||
// for keynums between K_FIRST_SCANCODE and K_LAST_SCANCODE
|
||||
// returns e.g. "SC_A" for K_SC_A
|
||||
const char* Sys_GetScancodeName( int key ) {
|
||||
if ( key >= K_FIRST_SCANCODE && key <= K_LAST_SCANCODE ) {
|
||||
int scIdx = key - K_FIRST_SCANCODE;
|
||||
return scancodemappings[scIdx].name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool isAscii( const char* str_ ) {
|
||||
const unsigned char* str = (const unsigned char*)str_;
|
||||
while(*str != '\0') {
|
||||
if(*str > 127) {
|
||||
return false;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns localized name of the key (between K_FIRST_SCANCODE and K_LAST_SCANCODE),
|
||||
// regarding the current keyboard layout - if that name is in ASCII or corresponds
|
||||
// to a "High-ASCII" char supported by Doom3.
|
||||
// Otherwise return same name as Sys_GetScancodeName()
|
||||
// !! Returned string is only valid until next call to this function !!
|
||||
const char* Sys_GetLocalizedScancodeName( int key ) {
|
||||
if ( key >= K_FIRST_SCANCODE && key <= K_LAST_SCANCODE ) {
|
||||
int scIdx = key - K_FIRST_SCANCODE;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_Scancode sc = ( SDL_Scancode ) scancodemappings[scIdx].sdlScancode;
|
||||
SDL_Keycode k = SDL_GetKeyFromScancode( sc );
|
||||
if ( k >= 0xA1 && k <= 0xFF ) {
|
||||
// luckily, the "High-ASCII" (ISO-8559-1) chars supported by Doom3
|
||||
// have the same values as the corresponding SDL_Keycodes.
|
||||
static char oneCharStr[2] = {0, 0};
|
||||
oneCharStr[0] = (unsigned char)k;
|
||||
return oneCharStr;
|
||||
} else if ( k != SDLK_UNKNOWN ) {
|
||||
const char *ret = SDL_GetKeyName( k );
|
||||
// the keyname from SDL2 is in UTF-8, which Doom3 can't print,
|
||||
// so only return the name if it's ASCII, otherwise fall back to "SC_bla"
|
||||
if ( ret && *ret != '\0' && isAscii( ret ) ) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif // SDL1.2 doesn't support this, use unlocalized name (also as fallback if we couldn't get a keyname)
|
||||
return scancodemappings[scIdx].name;
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// returns keyNum_t (K_SC_* constant) for given scancode name (like "SC_A")
|
||||
// only makes sense to call it if name starts with "SC_" (or "sc_")
|
||||
// returns -1 if not found
|
||||
int Sys_GetKeynumForScancodeName( const char* name ) {
|
||||
for( int scIdx = 0; scIdx < K_NUM_SCANCODES; ++scIdx ) {
|
||||
if ( idStr::Icmp( name, scancodemappings[scIdx].name ) == 0 ) {
|
||||
return scIdx + K_FIRST_SCANCODE;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
static int getKeynumForSDLscancode( SDL_Scancode scancode ) {
|
||||
int sc = scancode;
|
||||
for ( int scIdx=0; scIdx < K_NUM_SCANCODES; ++scIdx ) {
|
||||
if ( scancodemappings[scIdx].sdlScancode == sc ) {
|
||||
return scIdx + K_FIRST_SCANCODE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static byte mapkey(SDL_Keycode key) {
|
||||
switch (key) {
|
||||
case SDLK_BACKSPACE:
|
||||
|
@ -285,6 +436,8 @@ void Sys_InitInput() {
|
|||
kbd_polls.SetGranularity(64);
|
||||
mouse_polls.SetGranularity(64);
|
||||
|
||||
assert(sizeof(scancodemappings)/sizeof(scancodemappings[0]) == K_NUM_SCANCODES && "scancodemappings incomplete?");
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_EnableUNICODE(1);
|
||||
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
|
||||
|
@ -389,7 +542,7 @@ Sys_GetEvent
|
|||
sysEvent_t Sys_GetEvent() {
|
||||
SDL_Event ev;
|
||||
sysEvent_t res = { };
|
||||
byte key;
|
||||
int key;
|
||||
|
||||
static const sysEvent_t res_none = { SE_NONE, 0, 0, 0, NULL };
|
||||
|
||||
|
@ -532,10 +685,14 @@ sysEvent_t Sys_GetEvent() {
|
|||
if (ev.key.keysym.scancode == SDL_SCANCODE_GRAVE) { // TODO: always do this check?
|
||||
key = Sys_GetConsoleKey(true);
|
||||
} else {
|
||||
if (ev.type == SDL_KEYDOWN) {
|
||||
common->Warning("unmapped SDL key %d", ev.key.keysym.sym);
|
||||
// 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
|
||||
}
|
||||
continue; // handle next event
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,6 +166,17 @@ unsigned char Sys_GetConsoleKey( bool shifted );
|
|||
// does nothing on win32, as SE_KEY == SE_CHAR there
|
||||
// on other OSes, consider the keyboard mapping
|
||||
unsigned char Sys_MapCharForKey( int key );
|
||||
// for keynums between K_FIRST_SCANCODE and K_LAST_SCANCODE
|
||||
// returns e.g. "SC_A" for K_SC_A
|
||||
const char* Sys_GetScancodeName( int key );
|
||||
// returns localized name of the key (between K_FIRST_SCANCODE and K_LAST_SCANCODE),
|
||||
// regarding the current keyboard layout - if that name is in ASCII or corresponds
|
||||
// to a "High-ASCII" char supported by Doom3.
|
||||
// Otherwise return same name as Sys_GetScancodeName()
|
||||
// !! Returned string is only valid until next call to this function !!
|
||||
const char* Sys_GetLocalizedScancodeName( int key );
|
||||
// returns keyNum_t (K_SC_* constant) for given scancode name (like "SC_A")
|
||||
int Sys_GetKeynumForScancodeName( const char* name );
|
||||
|
||||
// keyboard input polling
|
||||
int Sys_PollKeyboardInputEvents( void );
|
||||
|
|
Loading…
Reference in a new issue