SDL2: Better non-ASCII supprt (text input, scancode names)

as long as it's chars Doom3 supports, i.e. it can be converted
to ISO-8859-1

also renamed kbdNames to _in_kbdNames to reduce likelyhood of clashes
(as it can't be static)
This commit is contained in:
Daniel Gibson 2021-07-18 03:24:46 +02:00
parent 06ff49c6b6
commit 4ce48965a5

View file

@ -60,14 +60,14 @@ If you have questions concerning this license or the applicable additional terms
#endif
// NOTE: g++-4.7 doesn't like when this is static (for idCmdSystem::ArgCompletion_String<kbdNames>)
const char *kbdNames[] = {
const char *_in_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", kbdNames[0], CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "keyboard layout", kbdNames, idCmdSystem::ArgCompletion_String<kbdNames> );
static idCVar in_kbd("in_kbd", _in_kbdNames[0], CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "keyboard layout", _in_kbdNames, idCmdSystem::ArgCompletion_String<_in_kbdNames> );
// 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");
@ -104,6 +104,11 @@ struct mouse_poll_t {
static idList<kbd_poll_t> kbd_polls;
static idList<mouse_poll_t> mouse_polls;
#if SDL_VERSION_ATLEAST(2, 0, 0)
// for utf8ToISO8859_1() - used for non-ascii text input and Sys_GetLocalizedScancodeName()
static SDL_iconv_t iconvDesc = (SDL_iconv_t)-1;
#endif
struct scancodename_t {
int sdlScancode;
const char* name;
@ -188,6 +193,7 @@ const char* Sys_GetScancodeName( int key ) {
return NULL;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
static bool isAscii( const char* str_ ) {
const unsigned char* str = (const unsigned char*)str_;
while(*str != '\0') {
@ -199,6 +205,40 @@ static bool isAscii( const char* str_ ) {
return true;
}
// convert inbuf (which is expected to be in UTF-8) to outbuf (in ISO-8859-1)
static bool utf8ToISO8859_1(const char* inbuf, char* outbuf, size_t outsize) {
if ( iconvDesc == (SDL_iconv_t)-1 ) {
return false;
}
size_t outbytesleft = outsize;
size_t inbytesleft = strlen( inbuf ) + 1; // + terminating \0
size_t ret = SDL_iconv( iconvDesc, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
while(inbytesleft > 0) {
switch ( ret ) {
case SDL_ICONV_E2BIG:
outbuf[outbytesleft-1] = '\0'; // whatever, just cut it off..
common->DPrintf( "Cutting off UTF-8 to ISO-8859-1 conversion to '%s' because destination is too small for '%s'\n", outbuf, inbuf );
SDL_iconv( iconvDesc, NULL, NULL, NULL, NULL ); // reset descriptor for next conversion
return true;
case SDL_ICONV_EILSEQ:
// try skipping invalid input data
++inbuf;
--inbytesleft;
break;
case SDL_ICONV_EINVAL:
case SDL_ICONV_ERROR:
// we can't recover from this
SDL_iconv( iconvDesc, NULL, NULL, NULL, NULL ); // reset descriptor for next conversion
return false;
}
}
SDL_iconv( iconvDesc, NULL, NULL, NULL, NULL ); // reset descriptor for next conversion
return outbytesleft < outsize; // return false if no char was written
}
#endif // SDL2
// 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.
@ -220,8 +260,15 @@ const char* Sys_GetLocalizedScancodeName( int key ) {
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;
if ( ret && *ret != '\0' ) {
if( isAscii( ret ) ) {
return ret;
}
static char isoName[32];
// try to convert name to ISO8859-1 (Doom3's supported "High ASCII")
if ( utf8ToISO8859_1( ret, isoName, sizeof(isoName) ) && isoName[0] != '\0' ) {
return isoName;
}
}
}
#endif // SDL1.2 doesn't support this, use unlocalized name (also as fallback if we couldn't get a keyname)
@ -455,6 +502,13 @@ void Sys_InitInput() {
#if !SDL_VERSION_ATLEAST(2, 0, 0)
SDL_EnableUNICODE(1);
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
#else // SDL2 - for utf8ToISO8859_1() (non-ascii text input and key naming)
assert(iconvDesc == (SDL_iconv_t)-1);
iconvDesc = SDL_iconv_open( "ISO-8859-1", "UTF-8" );
if( iconvDesc == (SDL_iconv_t)-1 ) {
common->Warning( "Sys_SetInput(): iconv_open( \"ISO-8859-1\", \"UTF-8\" ) failed! Can't translate non-ascii input!\n" );
}
#endif
in_kbd.SetModified();
@ -480,6 +534,9 @@ Sys_ShutdownInput
void Sys_ShutdownInput() {
kbd_polls.Clear();
mouse_polls.Clear();
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_iconv_close( iconvDesc ); // used by utf8ToISO8859_1()
#endif
}
/*
@ -621,7 +678,7 @@ sysEvent_t Sys_GetEvent() {
if (s[0] != '\0') {
res.evType = SE_CHAR;
res.evValue = s[s_pos];
res.evValue = (unsigned char)s[s_pos];
++s_pos;
@ -790,16 +847,24 @@ sysEvent_t Sys_GetEvent() {
case SDL_TEXTINPUT:
if (ev.text.text[0]) {
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);
s_pos = 1; // pos 0 is returned
if ( isAscii(ev.text.text) ) {
res.evValue = ev.text.text[0];
if ( ev.text.text[1] != '\0' ) {
memcpy( s, ev.text.text, SDL_TEXTINPUTEVENT_TEXT_SIZE );
s_pos = 1; // pos 0 is returned
}
return res;
} else if( utf8ToISO8859_1( ev.text.text, s, sizeof(s) ) && s[0] != '\0' ) {
res.evValue = (unsigned char)s[0];
if ( s[1] == '\0' ) {
s_pos = 0;
s[0] = '\0';
} else {
s_pos = 1; // pos 0 is returned
}
return res;
}
return res;
}
continue; // handle next event