Fixed bug 1876 - SDL_TEXTINPUT only returns '?' (0x3F) in event.text.text with Khmer language input
authorSam Lantinga <slouken@libsdl.org>
Fri, 16 Aug 2013 15:35:10 -0700
changeset 7645cc775832d501
parent 7644 037e5a9af745
child 7646 42c3b0c24c6c
Fixed bug 1876 - SDL_TEXTINPUT only returns '?' (0x3F) in event.text.text with Khmer language input

Andreas

The issue comes down to this line on MSDN:
"TranslateMessage produces WM_CHAR messages only for keys that are mapped to ASCII characters by the keyboard driver."

"WM_KEYDOWN and WM_KEYUP combinations produce a WM_CHAR or WM_DEADCHAR message. WM_SYSKEYDOWN and WM_SYSKEYUP combinations produce a WM_SYSCHAR or WM_SYSDEADCHAR message."
Except for WM_CHAR, none of these messages are used in SDL. Hence TranslateMessage should be dropped entirely and proper handling be included in the WM_KEYDOWN event.
Currently TranslateMessage is called for every message even if it must not be called in certain cases (like "An application should not call TranslateMessage if the TranslateAccelerator function returns a nonzero value.").

WM_CHAR message handling should remain for external processes posting these messages - additionally, WM_UNICHAR should be added.

I made a patch for src/video/windows/SDL_windowsevents.c that seems to work fine. It doesn't solve the "missing" composition for Khmer, but at least input for languages that cannot be mapped to ASCII characters (and for which IME is not used) will now work on Windows.
src/video/windows/SDL_windowsevents.c
     1.1 --- a/src/video/windows/SDL_windowsevents.c	Fri Aug 16 14:38:04 2013 -0300
     1.2 +++ b/src/video/windows/SDL_windowsevents.c	Fri Aug 16 15:35:10 2013 -0700
     1.3 @@ -256,6 +256,33 @@
     1.4      }
     1.5  }
     1.6  
     1.7 +static SDL_FORCE_INLINE BOOL
     1.8 +WIN_ConvertUTF32toUTF8(UINT32 codepoint, char * text)
     1.9 +{
    1.10 +    if (codepoint <= 0x7F) {
    1.11 +        text[0] = (char) codepoint;
    1.12 +        text[1] = '\0';
    1.13 +    } else if (codepoint <= 0x7FF) {
    1.14 +        text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
    1.15 +        text[1] = 0x80 | (char) (codepoint & 0x3F);
    1.16 +        text[2] = '\0';
    1.17 +    } else if (codepoint <= 0xFFFF) {
    1.18 +        text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
    1.19 +        text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
    1.20 +        text[2] = 0x80 | (char) (codepoint & 0x3F);
    1.21 +        text[3] = '\0';
    1.22 +    } else if (codepoint <= 0x10FFFF) {
    1.23 +        text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
    1.24 +        text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
    1.25 +        text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
    1.26 +        text[3] = 0x80 | (char) (codepoint & 0x3F);
    1.27 +        text[4] = '\0';
    1.28 +    } else {
    1.29 +        return SDL_FALSE;
    1.30 +    }
    1.31 +    return SDL_TRUE;
    1.32 +}
    1.33 +
    1.34  LRESULT CALLBACK
    1.35  WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    1.36  {
    1.37 @@ -459,8 +486,23 @@
    1.38          break;
    1.39  #endif /* WM_MOUSELEAVE */
    1.40  
    1.41 +    case WM_KEYDOWN:
    1.42 +        {
    1.43 +            BYTE keyboardState[256];
    1.44 +            char text[5];
    1.45 +            UINT32 utf32 = 0;
    1.46 +
    1.47 +            GetKeyboardState(keyboardState);
    1.48 +            if (ToUnicode(wParam, (lParam >> 16) & 0xff, keyboardState, (LPWSTR)&utf32, 1, 0) > 0) {
    1.49 +                WORD repitition;
    1.50 +                for (repitition = lParam & 0xffff; repitition > 0; repitition--) {
    1.51 +                    WIN_ConvertUTF32toUTF8(utf32, text);
    1.52 +                    SDL_SendKeyboardText(text);
    1.53 +                }
    1.54 +            }
    1.55 +        }
    1.56 +        // no break
    1.57      case WM_SYSKEYDOWN:
    1.58 -    case WM_KEYDOWN:
    1.59          {
    1.60              SDL_Scancode code = WindowsScanCodeToSDLScanCode( lParam, wParam );
    1.61              if ( code != SDL_SCANCODE_UNKNOWN ) {
    1.62 @@ -485,24 +527,19 @@
    1.63          returnCode = 0;
    1.64          break;
    1.65  
    1.66 +    case WM_UNICHAR:
    1.67 +        {
    1.68 +            if (wParam == UNICODE_NOCHAR) {
    1.69 +                returnCode = 1;
    1.70 +                break;
    1.71 +            }
    1.72 +        }
    1.73 +        // no break
    1.74      case WM_CHAR:
    1.75          {
    1.76 -            char text[4];
    1.77 +            char text[5];
    1.78  
    1.79 -            /* Convert to UTF-8 and send it on... */
    1.80 -            if (wParam <= 0x7F) {
    1.81 -                text[0] = (char) wParam;
    1.82 -                text[1] = '\0';
    1.83 -            } else if (wParam <= 0x7FF) {
    1.84 -                text[0] = 0xC0 | (char) ((wParam >> 6) & 0x1F);
    1.85 -                text[1] = 0x80 | (char) (wParam & 0x3F);
    1.86 -                text[2] = '\0';
    1.87 -            } else {
    1.88 -                text[0] = 0xE0 | (char) ((wParam >> 12) & 0x0F);
    1.89 -                text[1] = 0x80 | (char) ((wParam >> 6) & 0x3F);
    1.90 -                text[2] = 0x80 | (char) (wParam & 0x3F);
    1.91 -                text[3] = '\0';
    1.92 -            }
    1.93 +            WIN_ConvertUTF32toUTF8(wParam, text);
    1.94              SDL_SendKeyboardText(text);
    1.95          }
    1.96          returnCode = 0;
    1.97 @@ -772,7 +809,6 @@
    1.98      const Uint8 *keystate;
    1.99      MSG msg;
   1.100      while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   1.101 -        TranslateMessage(&msg);
   1.102          DispatchMessage(&msg);
   1.103      }
   1.104