mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-14 22:50:45 +00:00
Make Unicode input work for SDL
The d3bfg internal SE_CHAR events were documented as "evValue is an ascii char", but are actually at least UTF-16, as returned by Windows WM_CHAR events. We now assume it's UTF-32 (UTF-16 has the same values mostly) and the SDL backend now puts UTF-32 chars into SE_CHAR events. In the Windows backend I make sure that no surrogate UTF-16 chars are emitted + I added support for WM_UNICODE messages. Now I can input Ümläuts intö the conßole window \o/
This commit is contained in:
parent
d691002296
commit
65d1428dd1
3 changed files with 98 additions and 43 deletions
|
@ -163,6 +163,42 @@ static SDL_Scancode KeyNumToSDLScanCode( int keyNum )
|
|||
return SDL_SCANCODE_UNKNOWN;
|
||||
}
|
||||
|
||||
// both strings are expected to have at most SDL_TEXTINPUTEVENT_TEXT_SIZE chars/ints (including terminating null)
|
||||
static void ConvertUTF8toUTF32(const char* utf8str, int32* utf32buf)
|
||||
{
|
||||
static SDL_iconv_t cd = SDL_iconv_t(-1);
|
||||
|
||||
if( cd == SDL_iconv_t(-1) )
|
||||
{
|
||||
const char* toFormat = "UTF-32LE"; // TODO: what does CEGUI expect on big endian machines?
|
||||
cd = SDL_iconv_open(toFormat, "UTF-8");
|
||||
if( cd == SDL_iconv_t(-1) )
|
||||
{
|
||||
common->Warning("Couldn't initialize SDL_iconv for UTF-8 to UTF-32!"); // TODO: or error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len = strlen(utf8str);
|
||||
|
||||
size_t inbytesleft = len;
|
||||
size_t outbytesleft = 4 * SDL_TEXTINPUTEVENT_TEXT_SIZE; // *4 because utf-32 needs 4x as much space as utf-8
|
||||
char* outbuf = (char*)utf32buf;
|
||||
size_t n = SDL_iconv(cd, &utf8str, &inbytesleft, &outbuf, &outbytesleft);
|
||||
|
||||
if( n == size_t(-1) ) // some error occured during iconv
|
||||
{
|
||||
common->Warning("Converting UTF-8 string \"%s\" from SDL_TEXTINPUT to UTF-32 failed!", utf8str);
|
||||
|
||||
// clear utf32-buffer, just to be sure there's no garbage..
|
||||
memset(utf32buf, 0, SDL_TEXTINPUTEVENT_TEXT_SIZE*sizeof(int32));
|
||||
}
|
||||
|
||||
// reset cd so it can be used again
|
||||
SDL_iconv(cd, NULL, &inbytesleft, NULL, &outbytesleft);
|
||||
|
||||
}
|
||||
|
||||
#else // SDL1.2
|
||||
static int SDL_KeyToDoom3Key( SDL_Keycode key, bool& isChar )
|
||||
{
|
||||
|
@ -802,24 +838,26 @@ sysEvent_t Sys_GetEvent()
|
|||
static int previous_hat_state = SDL_HAT_CENTERED;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
static char str[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0};
|
||||
static size_t str_pos = 0;
|
||||
|
||||
if( str_pos != 0 )
|
||||
// utf-32 version of the textinput event
|
||||
static int32 uniStr[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0};
|
||||
static size_t uniStrPos = 0;
|
||||
|
||||
if(uniStr[0] != 0)
|
||||
{
|
||||
res.evType = SE_CHAR;
|
||||
res.evValue = str[str_pos];
|
||||
|
||||
++str_pos;
|
||||
if( !str[str_pos] )
|
||||
res.evValue = uniStr[uniStrPos];
|
||||
|
||||
++uniStrPos;
|
||||
|
||||
if( !uniStr[uniStrPos] || uniStrPos == SDL_TEXTINPUTEVENT_TEXT_SIZE )
|
||||
{
|
||||
memset( str, 0, sizeof( str ) );
|
||||
str_pos = 0;
|
||||
memset(uniStr, 0, sizeof(uniStr));
|
||||
uniStrPos = 0;
|
||||
}
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// DG: fake a "mousewheel not pressed anymore" event for SDL2
|
||||
// so scrolling in menus stops after one step
|
||||
static int mwheelRel = 0;
|
||||
|
@ -834,18 +872,18 @@ sysEvent_t Sys_GetEvent()
|
|||
// DG end
|
||||
#endif
|
||||
|
||||
static byte c = 0;
|
||||
static int32 uniChar = 0;
|
||||
|
||||
if( c )
|
||||
if(uniChar)
|
||||
{
|
||||
res.evType = SE_CHAR;
|
||||
res.evValue = c;
|
||||
|
||||
c = 0;
|
||||
|
||||
res.evValue = uniChar;
|
||||
|
||||
uniChar = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// loop until there is an event we care about (will return then) or no more events
|
||||
while( SDL_PollEvent( &ev ) )
|
||||
{
|
||||
|
@ -979,11 +1017,10 @@ sysEvent_t Sys_GetEvent()
|
|||
|
||||
#if ! SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
// DG: only do this for key-down, don't care about isChar from SDL_KeyToDoom3Key.
|
||||
// if unicode is not 0 and is translatable to ASCII it should work..
|
||||
if( ev.key.state == SDL_PRESSED && ( ev.key.keysym.unicode & 0xff80 ) == 0 )
|
||||
// if unicode is not 0 it should work..
|
||||
if( ev.key.state == SDL_PRESSED )
|
||||
{
|
||||
// FIXME: can we support utf32?
|
||||
c = ev.key.keysym.unicode & 0x7f;
|
||||
uniChar = ev.key.keysym.unicode; // for SE_CHAR
|
||||
}
|
||||
// DG end
|
||||
#endif
|
||||
|
@ -997,7 +1034,7 @@ sysEvent_t Sys_GetEvent()
|
|||
if( ev.key.keysym.scancode == SDL_SCANCODE_GRAVE )
|
||||
{
|
||||
key = K_GRAVE;
|
||||
c = K_BACKSPACE; // bad hack to get empty console inputline..
|
||||
uniChar = K_BACKSPACE; // bad hack to get empty console inputline..
|
||||
} // DG end, the original code is in the else case
|
||||
else
|
||||
{
|
||||
|
@ -1022,24 +1059,23 @@ sysEvent_t Sys_GetEvent()
|
|||
if( uc == Sys_GetConsoleKey( false ) || uc == Sys_GetConsoleKey( true ) )
|
||||
{
|
||||
key = K_GRAVE;
|
||||
c = K_BACKSPACE; // bad hack to get empty console inputline..
|
||||
uniChar = K_BACKSPACE; // bad hack to get empty console inputline..
|
||||
}
|
||||
else
|
||||
{
|
||||
if( c )
|
||||
if(uniChar)
|
||||
{
|
||||
res.evType = SE_CHAR;
|
||||
res.evValue = c;
|
||||
|
||||
c = 0;
|
||||
|
||||
res.evValue = uniChar;
|
||||
|
||||
uniChar = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if( ev.type == SDL_KEYDOWN ) // FIXME: don't complain if this was an ASCII char and the console is open?
|
||||
common->Warning( "unmapped SDL key %d (0x%x) scancode %d", ev.key.keysym.sym, ev.key.keysym.unicode, ev.key.keysym.scancode );
|
||||
|
||||
|
||||
|
||||
continue; // just handle next event
|
||||
}
|
||||
}
|
||||
|
@ -1053,7 +1089,7 @@ sysEvent_t Sys_GetEvent()
|
|||
kbd_polls.Append( kbd_poll_t( key, ev.key.state == SDL_PRESSED ) );
|
||||
|
||||
if( key == K_BACKSPACE && ev.key.state == SDL_PRESSED )
|
||||
c = key;
|
||||
uniChar = key;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -1062,15 +1098,21 @@ sysEvent_t Sys_GetEvent()
|
|||
case SDL_TEXTINPUT:
|
||||
if( ev.text.text[0] != '\0' )
|
||||
{
|
||||
// FIXME: all this really only works for ascii.. convert to unicode etc
|
||||
if( ev.text.text[1] )
|
||||
{
|
||||
// more than 1 char => handle the next chars later
|
||||
idStr::Copynz( str, ev.text.text + 1, sizeof( str ) );
|
||||
}
|
||||
// fill uniStr array for SE_CHAR events
|
||||
ConvertUTF8toUTF32(ev.text.text, uniStr);
|
||||
|
||||
// return an event with the first/only char
|
||||
res.evType = SE_CHAR;
|
||||
res.evValue = ev.text.text[0];
|
||||
res.evValue = uniStr[0];
|
||||
|
||||
uniStrPos = 1;
|
||||
|
||||
if( uniStr[1] == 0)
|
||||
{
|
||||
// it's just this one character, clear uniStr
|
||||
uniStr[0] = 0;
|
||||
uniStrPos = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,11 +101,11 @@ enum sysEventType_t
|
|||
{
|
||||
SE_NONE, // evTime is still valid
|
||||
SE_KEY, // evValue is a key code, evValue2 is the down flag
|
||||
SE_CHAR, // evValue is an ascii char FIXME: not really ascii, supports umlauts...
|
||||
SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves
|
||||
SE_CHAR, // evValue is an Unicode UTF-32 char (or non-surrogate UTF-16)
|
||||
SE_MOUSE, // evValue and evValue2 are relative signed x / y moves
|
||||
SE_MOUSE_ABSOLUTE, // evValue and evValue2 are absolute coordinates in the window's client area.
|
||||
SE_MOUSE_LEAVE, // evValue and evValue2 are meaninless, this indicates the mouse has left the client area.
|
||||
SE_JOYSTICK, // evValue is an axis number and evValue2 is the current state (-127 to 127)
|
||||
SE_JOYSTICK, // evValue is an axis number and evValue2 is the current state (-127 to 127)
|
||||
SE_CONSOLE // evPtr is a char*, from typing something at a non-game console
|
||||
};
|
||||
|
||||
|
|
|
@ -338,6 +338,7 @@ LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|||
key = K_NUMLOCK;
|
||||
}
|
||||
Sys_QueEvent( SE_KEY, key, true, 0, NULL, 0 );
|
||||
|
||||
break;
|
||||
|
||||
case WM_SYSKEYUP:
|
||||
|
@ -359,8 +360,20 @@ LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|||
break;
|
||||
|
||||
case WM_CHAR:
|
||||
// DG: make sure it's an utf-16 non-surrogate character (and thus a valid utf-32 character as well)
|
||||
// TODO: will there ever be two messages with surrogate characters that should be combined?
|
||||
// (probably not, some people claim it's actually UCS-2, not UTF-16)
|
||||
if( wParam < 0xD800 || wParam > 0xDFFF )
|
||||
{
|
||||
Sys_QueEvent( SE_CHAR, wParam, 0, 0, NULL, 0 );
|
||||
}
|
||||
break;
|
||||
|
||||
// DG: support utf-32 input via WM_UNICHAR
|
||||
case WM_UNICHAR:
|
||||
Sys_QueEvent( SE_CHAR, wParam, 0, 0, NULL, 0 );
|
||||
break;
|
||||
// DG end
|
||||
|
||||
case WM_NCLBUTTONDOWN:
|
||||
// win32.movingWindow = true;
|
||||
|
|
Loading…
Reference in a new issue