src/video/windows/SDL_windowskeyboard.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Jul 2014 10:26:28 -0700
changeset 8976 1a5d959d7b32
parent 8732 c62baa0b8827
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Fixed mingw64 build and warnings
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_WINDOWS
    24 
    25 #include "SDL_windowsvideo.h"
    26 
    27 #include "../../events/SDL_keyboard_c.h"
    28 #include "../../events/scancodes_windows.h"
    29 
    30 #include <imm.h>
    31 #include <oleauto.h>
    32 
    33 #ifndef SDL_DISABLE_WINDOWS_IME
    34 static void IME_Init(SDL_VideoData *videodata, HWND hwnd);
    35 static void IME_Enable(SDL_VideoData *videodata, HWND hwnd);
    36 static void IME_Disable(SDL_VideoData *videodata, HWND hwnd);
    37 static void IME_Quit(SDL_VideoData *videodata);
    38 #endif /* !SDL_DISABLE_WINDOWS_IME */
    39 
    40 #ifndef MAPVK_VK_TO_VSC
    41 #define MAPVK_VK_TO_VSC     0
    42 #endif
    43 #ifndef MAPVK_VSC_TO_VK
    44 #define MAPVK_VSC_TO_VK     1
    45 #endif
    46 #ifndef MAPVK_VK_TO_CHAR
    47 #define MAPVK_VK_TO_CHAR    2
    48 #endif
    49 
    50 /* Alphabetic scancodes for PC keyboards */
    51 void
    52 WIN_InitKeyboard(_THIS)
    53 {
    54     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    55 
    56     data->ime_com_initialized = SDL_FALSE;
    57     data->ime_threadmgr = 0;
    58     data->ime_initialized = SDL_FALSE;
    59     data->ime_enabled = SDL_FALSE;
    60     data->ime_available = SDL_FALSE;
    61     data->ime_hwnd_main = 0;
    62     data->ime_hwnd_current = 0;
    63     data->ime_himc = 0;
    64     data->ime_composition[0] = 0;
    65     data->ime_readingstring[0] = 0;
    66     data->ime_cursor = 0;
    67 
    68     data->ime_candlist = SDL_FALSE;
    69     SDL_memset(data->ime_candidates, 0, sizeof(data->ime_candidates));
    70     data->ime_candcount = 0;
    71     data->ime_candref = 0;
    72     data->ime_candsel = 0;
    73     data->ime_candpgsize = 0;
    74     data->ime_candlistindexbase = 0;
    75     data->ime_candvertical = SDL_TRUE;
    76 
    77     data->ime_dirty = SDL_FALSE;
    78     SDL_memset(&data->ime_rect, 0, sizeof(data->ime_rect));
    79     SDL_memset(&data->ime_candlistrect, 0, sizeof(data->ime_candlistrect));
    80     data->ime_winwidth = 0;
    81     data->ime_winheight = 0;
    82 
    83     data->ime_hkl = 0;
    84     data->ime_himm32 = 0;
    85     data->GetReadingString = 0;
    86     data->ShowReadingWindow = 0;
    87     data->ImmLockIMC = 0;
    88     data->ImmUnlockIMC = 0;
    89     data->ImmLockIMCC = 0;
    90     data->ImmUnlockIMCC = 0;
    91     data->ime_uiless = SDL_FALSE;
    92     data->ime_threadmgrex = 0;
    93     data->ime_uielemsinkcookie = TF_INVALID_COOKIE;
    94     data->ime_alpnsinkcookie = TF_INVALID_COOKIE;
    95     data->ime_openmodesinkcookie = TF_INVALID_COOKIE;
    96     data->ime_convmodesinkcookie = TF_INVALID_COOKIE;
    97     data->ime_uielemsink = 0;
    98     data->ime_ippasink = 0;
    99 
   100     WIN_UpdateKeymap();
   101 
   102     SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
   103     SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows");
   104     SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows");
   105 }
   106 
   107 void
   108 WIN_UpdateKeymap()
   109 {
   110     int i;
   111     SDL_Scancode scancode;
   112     SDL_Keycode keymap[SDL_NUM_SCANCODES];
   113 
   114     SDL_GetDefaultKeymap(keymap);
   115 
   116     for (i = 0; i < SDL_arraysize(windows_scancode_table); i++) {
   117         int vk;
   118         /* Make sure this scancode is a valid character scancode */
   119         scancode = windows_scancode_table[i];
   120         if (scancode == SDL_SCANCODE_UNKNOWN ) {
   121             continue;
   122         }
   123 
   124         /* If this key is one of the non-mappable keys, ignore it */
   125         /* Don't allow the number keys right above the qwerty row to translate or the top left key (grave/backquote) */
   126         /* Not mapping numbers fixes the French layout, giving numeric keycodes for the number keys, which is the expected behavior */
   127         if ((keymap[scancode] & SDLK_SCANCODE_MASK) ||
   128             scancode == SDL_SCANCODE_GRAVE ||
   129             (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0) ) {
   130             continue;
   131         }
   132 
   133         vk =  MapVirtualKey(i, MAPVK_VSC_TO_VK);
   134         if ( vk ) {
   135             int ch = (MapVirtualKey( vk, MAPVK_VK_TO_CHAR ) & 0x7FFF);
   136             if ( ch ) {
   137                 if ( ch >= 'A' && ch <= 'Z' ) {
   138                     keymap[scancode] =  SDLK_a + ( ch - 'A' );
   139                 } else {
   140                     keymap[scancode] = ch;
   141                 }
   142             }
   143         }
   144     }
   145 
   146     SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
   147 }
   148 
   149 void
   150 WIN_QuitKeyboard(_THIS)
   151 {
   152 #ifndef SDL_DISABLE_WINDOWS_IME
   153     IME_Quit((SDL_VideoData *)_this->driverdata);
   154 #endif
   155 }
   156 
   157 void
   158 WIN_StartTextInput(_THIS)
   159 {
   160 #ifndef SDL_DISABLE_WINDOWS_IME
   161     SDL_Window *window = SDL_GetKeyboardFocus();
   162     if (window) {
   163         HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
   164         SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
   165         SDL_GetWindowSize(window, &videodata->ime_winwidth, &videodata->ime_winheight);
   166         IME_Init(videodata, hwnd);
   167         IME_Enable(videodata, hwnd);
   168     }
   169 #endif /* !SDL_DISABLE_WINDOWS_IME */
   170 }
   171 
   172 void
   173 WIN_StopTextInput(_THIS)
   174 {
   175 #ifndef SDL_DISABLE_WINDOWS_IME
   176     SDL_Window *window = SDL_GetKeyboardFocus();
   177     if (window) {
   178         HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
   179         SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
   180         IME_Init(videodata, hwnd);
   181         IME_Disable(videodata, hwnd);
   182     }
   183 #endif /* !SDL_DISABLE_WINDOWS_IME */
   184 }
   185 
   186 void
   187 WIN_SetTextInputRect(_THIS, SDL_Rect *rect)
   188 {
   189     SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
   190 
   191     if (!rect) {
   192         SDL_InvalidParamError("rect");
   193         return;
   194     }
   195 
   196     videodata->ime_rect = *rect;
   197 }
   198 
   199 #ifdef SDL_DISABLE_WINDOWS_IME
   200 
   201 
   202 SDL_bool
   203 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
   204 {
   205     return SDL_FALSE;
   206 }
   207 
   208 void IME_Present(SDL_VideoData *videodata)
   209 {
   210 }
   211 
   212 #else
   213 
   214 #ifdef __GNUC__
   215 #undef DEFINE_GUID
   216 #define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
   217 DEFINE_GUID(IID_ITfInputProcessorProfileActivationSink,        0x71C6E74E,0x0F28,0x11D8,0xA8,0x2A,0x00,0x06,0x5B,0x84,0x43,0x5C);
   218 DEFINE_GUID(IID_ITfUIElementSink,                              0xEA1EA136,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
   219 DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD,                           0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31);
   220 DEFINE_GUID(IID_ITfSource,                                     0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7);
   221 DEFINE_GUID(IID_ITfUIElementMgr,                               0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
   222 DEFINE_GUID(IID_ITfCandidateListUIElement,                     0xEA1EA138,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
   223 DEFINE_GUID(IID_ITfReadingInformationUIElement,                0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
   224 DEFINE_GUID(IID_ITfThreadMgr,                                  0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E);
   225 DEFINE_GUID(CLSID_TF_ThreadMgr,                                0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50);
   226 DEFINE_GUID(IID_ITfThreadMgrEx,                                0x3E90ADE3,0x7594,0x4CB0,0xBB,0x58,0x69,0x62,0x8F,0x5F,0x45,0x8C);
   227 #endif
   228 
   229 #define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
   230 #define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
   231 
   232 #define MAKEIMEVERSION(major,minor) ((DWORD) (((BYTE)(major) << 24) | ((BYTE)(minor) << 16) ))
   233 #define IMEID_VER(id) ((id) & 0xffff0000)
   234 #define IMEID_LANG(id) ((id) & 0x0000ffff)
   235 
   236 #define CHT_HKL_DAYI            ((HKL)0xE0060404)
   237 #define CHT_HKL_NEW_PHONETIC    ((HKL)0xE0080404)
   238 #define CHT_HKL_NEW_CHANG_JIE   ((HKL)0xE0090404)
   239 #define CHT_HKL_NEW_QUICK       ((HKL)0xE00A0404)
   240 #define CHT_HKL_HK_CANTONESE    ((HKL)0xE00B0404)
   241 #define CHT_IMEFILENAME1        "TINTLGNT.IME"
   242 #define CHT_IMEFILENAME2        "CINTLGNT.IME"
   243 #define CHT_IMEFILENAME3        "MSTCIPHA.IME"
   244 #define IMEID_CHT_VER42         (LANG_CHT | MAKEIMEVERSION(4, 2))
   245 #define IMEID_CHT_VER43         (LANG_CHT | MAKEIMEVERSION(4, 3))
   246 #define IMEID_CHT_VER44         (LANG_CHT | MAKEIMEVERSION(4, 4))
   247 #define IMEID_CHT_VER50         (LANG_CHT | MAKEIMEVERSION(5, 0))
   248 #define IMEID_CHT_VER51         (LANG_CHT | MAKEIMEVERSION(5, 1))
   249 #define IMEID_CHT_VER52         (LANG_CHT | MAKEIMEVERSION(5, 2))
   250 #define IMEID_CHT_VER60         (LANG_CHT | MAKEIMEVERSION(6, 0))
   251 #define IMEID_CHT_VER_VISTA     (LANG_CHT | MAKEIMEVERSION(7, 0))
   252 
   253 #define CHS_HKL                 ((HKL)0xE00E0804)
   254 #define CHS_IMEFILENAME1        "PINTLGNT.IME"
   255 #define CHS_IMEFILENAME2        "MSSCIPYA.IME"
   256 #define IMEID_CHS_VER41         (LANG_CHS | MAKEIMEVERSION(4, 1))
   257 #define IMEID_CHS_VER42         (LANG_CHS | MAKEIMEVERSION(4, 2))
   258 #define IMEID_CHS_VER53         (LANG_CHS | MAKEIMEVERSION(5, 3))
   259 
   260 #define LANG() LOWORD((videodata->ime_hkl))
   261 #define PRIMLANG() ((WORD)PRIMARYLANGID(LANG()))
   262 #define SUBLANG() SUBLANGID(LANG())
   263 
   264 static void IME_UpdateInputLocale(SDL_VideoData *videodata);
   265 static void IME_ClearComposition(SDL_VideoData *videodata);
   266 static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd);
   267 static void IME_SetupAPI(SDL_VideoData *videodata);
   268 static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex);
   269 static void IME_SendEditingEvent(SDL_VideoData *videodata);
   270 static void IME_DestroyTextures(SDL_VideoData *videodata);
   271 
   272 #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
   273 #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
   274 
   275 static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata);
   276 static void UILess_ReleaseSinks(SDL_VideoData *videodata);
   277 static void UILess_EnableUIUpdates(SDL_VideoData *videodata);
   278 static void UILess_DisableUIUpdates(SDL_VideoData *videodata);
   279 
   280 static void
   281 IME_Init(SDL_VideoData *videodata, HWND hwnd)
   282 {
   283     if (videodata->ime_initialized)
   284         return;
   285 
   286     videodata->ime_hwnd_main = hwnd;
   287     if (SUCCEEDED(WIN_CoInitialize())) {
   288         videodata->ime_com_initialized = SDL_TRUE;
   289         CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (LPVOID *)&videodata->ime_threadmgr);
   290     }
   291     videodata->ime_initialized = SDL_TRUE;
   292     videodata->ime_himm32 = SDL_LoadObject("imm32.dll");
   293     if (!videodata->ime_himm32) {
   294         videodata->ime_available = SDL_FALSE;
   295         return;
   296     }
   297     videodata->ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMC");
   298     videodata->ImmUnlockIMC = (BOOL (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMC");
   299     videodata->ImmLockIMCC = (LPVOID (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMCC");
   300     videodata->ImmUnlockIMCC = (BOOL (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMCC");
   301 
   302     IME_SetWindow(videodata, hwnd);
   303     videodata->ime_himc = ImmGetContext(hwnd);
   304     ImmReleaseContext(hwnd, videodata->ime_himc);
   305     if (!videodata->ime_himc) {
   306         videodata->ime_available = SDL_FALSE;
   307         IME_Disable(videodata, hwnd);
   308         return;
   309     }
   310     videodata->ime_available = SDL_TRUE;
   311     IME_UpdateInputLocale(videodata);
   312     IME_SetupAPI(videodata);
   313     videodata->ime_uiless = UILess_SetupSinks(videodata);
   314     IME_UpdateInputLocale(videodata);
   315     IME_Disable(videodata, hwnd);
   316 }
   317 
   318 static void
   319 IME_Enable(SDL_VideoData *videodata, HWND hwnd)
   320 {
   321     if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
   322         return;
   323 
   324     if (!videodata->ime_available) {
   325         IME_Disable(videodata, hwnd);
   326         return;
   327     }
   328     if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
   329         ImmAssociateContext(videodata->ime_hwnd_current, videodata->ime_himc);
   330 
   331     videodata->ime_enabled = SDL_TRUE;
   332     IME_UpdateInputLocale(videodata);
   333     UILess_EnableUIUpdates(videodata);
   334 }
   335 
   336 static void
   337 IME_Disable(SDL_VideoData *videodata, HWND hwnd)
   338 {
   339     if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
   340         return;
   341 
   342     IME_ClearComposition(videodata);
   343     if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
   344         ImmAssociateContext(videodata->ime_hwnd_current, (HIMC)0);
   345 
   346     videodata->ime_enabled = SDL_FALSE;
   347     UILess_DisableUIUpdates(videodata);
   348 }
   349 
   350 static void
   351 IME_Quit(SDL_VideoData *videodata)
   352 {
   353     if (!videodata->ime_initialized)
   354         return;
   355 
   356     UILess_ReleaseSinks(videodata);
   357     if (videodata->ime_hwnd_main)
   358         ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc);
   359 
   360     videodata->ime_hwnd_main = 0;
   361     videodata->ime_himc = 0;
   362     if (videodata->ime_himm32) {
   363         SDL_UnloadObject(videodata->ime_himm32);
   364         videodata->ime_himm32 = 0;
   365     }
   366     if (videodata->ime_threadmgr) {
   367         videodata->ime_threadmgr->lpVtbl->Release(videodata->ime_threadmgr);
   368         videodata->ime_threadmgr = 0;
   369     }
   370     if (videodata->ime_com_initialized) {
   371         WIN_CoUninitialize();
   372         videodata->ime_com_initialized = SDL_FALSE;
   373     }
   374     IME_DestroyTextures(videodata);
   375     videodata->ime_initialized = SDL_FALSE;
   376 }
   377 
   378 static void
   379 IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd)
   380 {
   381     DWORD id = 0;
   382     HIMC himc = 0;
   383     WCHAR buffer[16];
   384     WCHAR *s = buffer;
   385     DWORD len = 0;
   386     INT err = 0;
   387     BOOL vertical = FALSE;
   388     UINT maxuilen = 0;
   389 	static OSVERSIONINFOA osversion;
   390 
   391     if (videodata->ime_uiless)
   392         return;
   393 
   394     videodata->ime_readingstring[0] = 0;
   395     if (!osversion.dwOSVersionInfoSize) {
   396         osversion.dwOSVersionInfoSize = sizeof(osversion);
   397         GetVersionExA(&osversion);
   398     }
   399     id = IME_GetId(videodata, 0);
   400     if (!id)
   401         return;
   402 
   403     himc = ImmGetContext(hwnd);
   404     if (!himc)
   405         return;
   406 
   407     if (videodata->GetReadingString) {
   408         len = videodata->GetReadingString(himc, 0, 0, &err, &vertical, &maxuilen);
   409         if (len) {
   410             if (len > SDL_arraysize(buffer))
   411                 len = SDL_arraysize(buffer);
   412 
   413             len = videodata->GetReadingString(himc, len, s, &err, &vertical, &maxuilen);
   414         }
   415         SDL_wcslcpy(videodata->ime_readingstring, s, len);
   416     }
   417     else {
   418         LPINPUTCONTEXT2 lpimc = videodata->ImmLockIMC(himc);
   419         LPBYTE p = 0;
   420         s = 0;
   421         switch (id)
   422         {
   423         case IMEID_CHT_VER42:
   424         case IMEID_CHT_VER43:
   425         case IMEID_CHT_VER44:
   426             p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 24);
   427             if (!p)
   428                 break;
   429 
   430             len = *(DWORD *)(p + 7*4 + 32*4);
   431             s = (WCHAR *)(p + 56);
   432             break;
   433         case IMEID_CHT_VER51:
   434         case IMEID_CHT_VER52:
   435         case IMEID_CHS_VER53:
   436             p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 4);
   437             if (!p)
   438                 break;
   439 
   440             p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);
   441             if (!p)
   442                 break;
   443 
   444             len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
   445             s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
   446             break;
   447         case IMEID_CHS_VER41:
   448             {
   449                 int offset = (IME_GetId(videodata, 1) >= 0x00000002) ? 8 : 7;
   450                 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + offset * 4);
   451                 if (!p)
   452                     break;
   453 
   454                 len = *(DWORD *)(p + 7*4 + 16*2*4);
   455                 s = (WCHAR *)(p + 6*4 + 16*2*1);
   456             }
   457             break;
   458         case IMEID_CHS_VER42:
   459             if (osversion.dwPlatformId != VER_PLATFORM_WIN32_NT)
   460                 break;
   461 
   462             p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 1*4 + 1*4 + 6*4);
   463             if (!p)
   464                 break;
   465 
   466             len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
   467             s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
   468             break;
   469         }
   470         if (s) {
   471             size_t size = SDL_min((size_t)(len + 1), SDL_arraysize(videodata->ime_readingstring));
   472             SDL_wcslcpy(videodata->ime_readingstring, s, size);
   473         }
   474 
   475         videodata->ImmUnlockIMCC(lpimc->hPrivate);
   476         videodata->ImmUnlockIMC(himc);
   477     }
   478     ImmReleaseContext(hwnd, himc);
   479     IME_SendEditingEvent(videodata);
   480 }
   481 
   482 static void
   483 IME_InputLangChanged(SDL_VideoData *videodata)
   484 {
   485     UINT lang = PRIMLANG();
   486     IME_UpdateInputLocale(videodata);
   487     if (!videodata->ime_uiless)
   488         videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
   489 
   490     IME_SetupAPI(videodata);
   491     if (lang != PRIMLANG()) {
   492         IME_ClearComposition(videodata);
   493     }
   494 }
   495 
   496 static DWORD
   497 IME_GetId(SDL_VideoData *videodata, UINT uIndex)
   498 {
   499     static HKL hklprev = 0;
   500     static DWORD dwRet[2] = {0};
   501     DWORD dwVerSize = 0;
   502     DWORD dwVerHandle = 0;
   503     LPVOID lpVerBuffer = 0;
   504     LPVOID lpVerData = 0;
   505     UINT cbVerData = 0;
   506     char szTemp[256];
   507     HKL hkl = 0;
   508     DWORD dwLang = 0;
   509     if (uIndex >= sizeof(dwRet) / sizeof(dwRet[0]))
   510         return 0;
   511 
   512     hkl = videodata->ime_hkl;
   513     if (hklprev == hkl)
   514         return dwRet[uIndex];
   515 
   516     hklprev = hkl;
   517     dwLang = ((DWORD_PTR)hkl & 0xffff);
   518     if (videodata->ime_uiless && LANG() == LANG_CHT) {
   519         dwRet[0] = IMEID_CHT_VER_VISTA;
   520         dwRet[1] = 0;
   521         return dwRet[0];
   522     }
   523     if (hkl != CHT_HKL_NEW_PHONETIC
   524         && hkl != CHT_HKL_NEW_CHANG_JIE
   525         && hkl != CHT_HKL_NEW_QUICK
   526         && hkl != CHT_HKL_HK_CANTONESE
   527         && hkl != CHS_HKL) {
   528         dwRet[0] = dwRet[1] = 0;
   529         return dwRet[uIndex];
   530     }
   531     if (ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1) <= 0) {
   532         dwRet[0] = dwRet[1] = 0;
   533         return dwRet[uIndex];
   534     }
   535     if (!videodata->GetReadingString) {
   536         #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
   537         if (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME1, -1) != 2
   538             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME2, -1) != 2
   539             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME3, -1) != 2
   540             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME1, -1) != 2
   541             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME2, -1) != 2) {
   542             dwRet[0] = dwRet[1] = 0;
   543             return dwRet[uIndex];
   544         }
   545         #undef LCID_INVARIANT
   546         dwVerSize = GetFileVersionInfoSizeA(szTemp, &dwVerHandle);
   547         if (dwVerSize) {
   548             lpVerBuffer = SDL_malloc(dwVerSize);
   549             if (lpVerBuffer) {
   550                 if (GetFileVersionInfoA(szTemp, dwVerHandle, dwVerSize, lpVerBuffer)) {
   551                     if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) {
   552                         #define pVerFixedInfo   ((VS_FIXEDFILEINFO FAR*)lpVerData)
   553                         DWORD dwVer = pVerFixedInfo->dwFileVersionMS;
   554                         dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16;
   555                         if ((videodata->GetReadingString) ||
   556                             ((dwLang == LANG_CHT) && (
   557                             dwVer == MAKEIMEVERSION(4, 2) ||
   558                             dwVer == MAKEIMEVERSION(4, 3) ||
   559                             dwVer == MAKEIMEVERSION(4, 4) ||
   560                             dwVer == MAKEIMEVERSION(5, 0) ||
   561                             dwVer == MAKEIMEVERSION(5, 1) ||
   562                             dwVer == MAKEIMEVERSION(5, 2) ||
   563                             dwVer == MAKEIMEVERSION(6, 0)))
   564                             ||
   565                             ((dwLang == LANG_CHS) && (
   566                             dwVer == MAKEIMEVERSION(4, 1) ||
   567                             dwVer == MAKEIMEVERSION(4, 2) ||
   568                             dwVer == MAKEIMEVERSION(5, 3)))) {
   569                             dwRet[0] = dwVer | dwLang;
   570                             dwRet[1] = pVerFixedInfo->dwFileVersionLS;
   571                             SDL_free(lpVerBuffer);
   572                             return dwRet[0];
   573                         }
   574                         #undef pVerFixedInfo
   575                     }
   576                 }
   577             }
   578             SDL_free(lpVerBuffer);
   579         }
   580     }
   581     dwRet[0] = dwRet[1] = 0;
   582     return dwRet[uIndex];
   583 }
   584 
   585 static void
   586 IME_SetupAPI(SDL_VideoData *videodata)
   587 {
   588     char ime_file[MAX_PATH + 1];
   589     void* hime = 0;
   590     HKL hkl = 0;
   591     videodata->GetReadingString = 0;
   592     videodata->ShowReadingWindow = 0;
   593     if (videodata->ime_uiless)
   594         return;
   595 
   596     hkl = videodata->ime_hkl;
   597     if (ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1) <= 0)
   598         return;
   599 
   600     hime = SDL_LoadObject(ime_file);
   601     if (!hime)
   602         return;
   603 
   604     videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT))
   605         SDL_LoadFunction(hime, "GetReadingString");
   606     videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL))
   607         SDL_LoadFunction(hime, "ShowReadingWindow");
   608 
   609     if (videodata->ShowReadingWindow) {
   610         HIMC himc = ImmGetContext(videodata->ime_hwnd_current);
   611         if (himc) {
   612             videodata->ShowReadingWindow(himc, FALSE);
   613             ImmReleaseContext(videodata->ime_hwnd_current, himc);
   614         }
   615     }
   616 }
   617 
   618 static void
   619 IME_SetWindow(SDL_VideoData* videodata, HWND hwnd)
   620 {
   621     videodata->ime_hwnd_current = hwnd;
   622     if (videodata->ime_threadmgr) {
   623         struct ITfDocumentMgr *document_mgr = 0;
   624         if (SUCCEEDED(videodata->ime_threadmgr->lpVtbl->AssociateFocus(videodata->ime_threadmgr, hwnd, NULL, &document_mgr))) {
   625             if (document_mgr)
   626                 document_mgr->lpVtbl->Release(document_mgr);
   627         }
   628     }
   629 }
   630 
   631 static void
   632 IME_UpdateInputLocale(SDL_VideoData *videodata)
   633 {
   634     static HKL hklprev = 0;
   635     videodata->ime_hkl = GetKeyboardLayout(0);
   636     if (hklprev == videodata->ime_hkl)
   637         return;
   638 
   639     hklprev = videodata->ime_hkl;
   640     switch (PRIMLANG()) {
   641     case LANG_CHINESE:
   642         videodata->ime_candvertical = SDL_TRUE;
   643         if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
   644             videodata->ime_candvertical = SDL_FALSE;
   645 
   646         break;
   647     case LANG_JAPANESE:
   648         videodata->ime_candvertical = SDL_TRUE;
   649         break;
   650     case LANG_KOREAN:
   651         videodata->ime_candvertical = SDL_FALSE;
   652         break;
   653     }
   654 }
   655 
   656 static void
   657 IME_ClearComposition(SDL_VideoData *videodata)
   658 {
   659     HIMC himc = 0;
   660     if (!videodata->ime_initialized)
   661         return;
   662 
   663     himc = ImmGetContext(videodata->ime_hwnd_current);
   664     if (!himc)
   665         return;
   666 
   667     ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   668     if (videodata->ime_uiless)
   669         ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
   670 
   671     ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0);
   672     ImmReleaseContext(videodata->ime_hwnd_current, himc);
   673     SDL_SendEditingText("", 0, 0);
   674 }
   675 
   676 static void
   677 IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
   678 {
   679     LONG length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, sizeof(videodata->ime_composition) - sizeof(videodata->ime_composition[0]));
   680     if (length < 0)
   681         length = 0;
   682 
   683     length /= sizeof(videodata->ime_composition[0]);
   684     videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0));
   685     if (videodata->ime_cursor < SDL_arraysize(videodata->ime_composition) && videodata->ime_composition[videodata->ime_cursor] == 0x3000) {
   686         int i;
   687         for (i = videodata->ime_cursor + 1; i < length; ++i)
   688             videodata->ime_composition[i - 1] = videodata->ime_composition[i];
   689 
   690         --length;
   691     }
   692     videodata->ime_composition[length] = 0;
   693 }
   694 
   695 static void
   696 IME_SendInputEvent(SDL_VideoData *videodata)
   697 {
   698     char *s = 0;
   699     s = WIN_StringToUTF8(videodata->ime_composition);
   700     SDL_SendKeyboardText(s);
   701     SDL_free(s);
   702 
   703     videodata->ime_composition[0] = 0;
   704     videodata->ime_readingstring[0] = 0;
   705     videodata->ime_cursor = 0;
   706 }
   707 
   708 static void
   709 IME_SendEditingEvent(SDL_VideoData *videodata)
   710 {
   711     char *s = 0;
   712     WCHAR buffer[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
   713     const size_t size = SDL_arraysize(buffer);
   714     buffer[0] = 0;
   715     if (videodata->ime_readingstring[0]) {
   716         size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor);
   717         SDL_wcslcpy(buffer, videodata->ime_composition, len + 1);
   718         SDL_wcslcat(buffer, videodata->ime_readingstring, size);
   719         SDL_wcslcat(buffer, &videodata->ime_composition[len], size);
   720     }
   721     else {
   722         SDL_wcslcpy(buffer, videodata->ime_composition, size);
   723     }
   724     s = WIN_StringToUTF8(buffer);
   725     SDL_SendEditingText(s, videodata->ime_cursor + SDL_wcslen(videodata->ime_readingstring), 0);
   726     SDL_free(s);
   727 }
   728 
   729 static void
   730 IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
   731 {
   732     LPWSTR dst = videodata->ime_candidates[i];
   733     *dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
   734     if (videodata->ime_candvertical)
   735         *dst++ = TEXT(' ');
   736 
   737     while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
   738         *dst++ = *candidate++;
   739 
   740     *dst = (WCHAR)'\0';
   741 }
   742 
   743 static void
   744 IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
   745 {
   746     LPCANDIDATELIST cand_list = 0;
   747     DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
   748     if (size) {
   749         cand_list = (LPCANDIDATELIST)SDL_malloc(size);
   750         if (cand_list) {
   751             size = ImmGetCandidateListW(himc, 0, cand_list, size);
   752             if (size) {
   753                 UINT i, j;
   754                 UINT page_start = 0;
   755                 videodata->ime_candsel = cand_list->dwSelection;
   756                 videodata->ime_candcount = cand_list->dwCount;
   757 
   758                 if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) {
   759                     const UINT maxcandchar = 18;
   760                     size_t cchars = 0;
   761 
   762                     for (i = 0; i < videodata->ime_candcount; ++i) {
   763                         size_t len = SDL_wcslen((LPWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i])) + 1;
   764                         if (len + cchars > maxcandchar) {
   765                             if (i > cand_list->dwSelection)
   766                                 break;
   767 
   768                             page_start = i;
   769                             cchars = len;
   770                         }
   771                         else {
   772                             cchars += len;
   773                         }
   774                     }
   775                     videodata->ime_candpgsize = i - page_start;
   776                 } else {
   777                     videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
   778                     page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
   779                 }
   780                 SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   781                 for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++) {
   782                     LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]);
   783                     IME_AddCandidate(videodata, j, candidate);
   784                 }
   785                 if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
   786                     videodata->ime_candsel = -1;
   787 
   788             }
   789             SDL_free(cand_list);
   790         }
   791     }
   792 }
   793 
   794 static void
   795 IME_ShowCandidateList(SDL_VideoData *videodata)
   796 {
   797     videodata->ime_dirty = SDL_TRUE;
   798     videodata->ime_candlist = SDL_TRUE;
   799     IME_DestroyTextures(videodata);
   800     IME_SendEditingEvent(videodata);
   801 }
   802 
   803 static void
   804 IME_HideCandidateList(SDL_VideoData *videodata)
   805 {
   806     videodata->ime_dirty = SDL_FALSE;
   807     videodata->ime_candlist = SDL_FALSE;
   808     IME_DestroyTextures(videodata);
   809     IME_SendEditingEvent(videodata);
   810 }
   811 
   812 SDL_bool
   813 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
   814 {
   815     SDL_bool trap = SDL_FALSE;
   816     HIMC himc = 0;
   817     if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled)
   818         return SDL_FALSE;
   819 
   820     switch (msg) {
   821     case WM_INPUTLANGCHANGE:
   822         IME_InputLangChanged(videodata);
   823         break;
   824     case WM_IME_SETCONTEXT:
   825         *lParam = 0;
   826         break;
   827     case WM_IME_STARTCOMPOSITION:
   828         trap = SDL_TRUE;
   829         break;
   830     case WM_IME_COMPOSITION:
   831         trap = SDL_TRUE;
   832         himc = ImmGetContext(hwnd);
   833         if (*lParam & GCS_RESULTSTR) {
   834             IME_GetCompositionString(videodata, himc, GCS_RESULTSTR);
   835             IME_SendInputEvent(videodata);
   836         }
   837         if (*lParam & GCS_COMPSTR) {
   838             if (!videodata->ime_uiless)
   839                 videodata->ime_readingstring[0] = 0;
   840 
   841             IME_GetCompositionString(videodata, himc, GCS_COMPSTR);
   842             IME_SendEditingEvent(videodata);
   843         }
   844         ImmReleaseContext(hwnd, himc);
   845         break;
   846     case WM_IME_ENDCOMPOSITION:
   847         videodata->ime_composition[0] = 0;
   848         videodata->ime_readingstring[0] = 0;
   849         videodata->ime_cursor = 0;
   850         SDL_SendEditingText("", 0, 0);
   851         break;
   852     case WM_IME_NOTIFY:
   853         switch (wParam) {
   854         case IMN_SETCONVERSIONMODE:
   855         case IMN_SETOPENSTATUS:
   856             IME_UpdateInputLocale(videodata);
   857             break;
   858         case IMN_OPENCANDIDATE:
   859         case IMN_CHANGECANDIDATE:
   860             if (videodata->ime_uiless)
   861                 break;
   862 
   863             trap = SDL_TRUE;
   864             IME_ShowCandidateList(videodata);
   865             himc = ImmGetContext(hwnd);
   866             if (!himc)
   867                 break;
   868 
   869             IME_GetCandidateList(himc, videodata);
   870             ImmReleaseContext(hwnd, himc);
   871             break;
   872         case IMN_CLOSECANDIDATE:
   873             trap = SDL_TRUE;
   874             IME_HideCandidateList(videodata);
   875             break;
   876         case IMN_PRIVATE:
   877             {
   878                 DWORD dwId = IME_GetId(videodata, 0);
   879                 IME_GetReadingString(videodata, hwnd);
   880                 switch (dwId)
   881                 {
   882                 case IMEID_CHT_VER42:
   883                 case IMEID_CHT_VER43:
   884                 case IMEID_CHT_VER44:
   885                 case IMEID_CHS_VER41:
   886                 case IMEID_CHS_VER42:
   887                     if (*lParam == 1 || *lParam == 2)
   888                         trap = SDL_TRUE;
   889 
   890                     break;
   891                 case IMEID_CHT_VER50:
   892                 case IMEID_CHT_VER51:
   893                 case IMEID_CHT_VER52:
   894                 case IMEID_CHT_VER60:
   895                 case IMEID_CHS_VER53:
   896                     if (*lParam == 16
   897                         || *lParam == 17
   898                         || *lParam == 26
   899                         || *lParam == 27
   900                         || *lParam == 28)
   901                         trap = SDL_TRUE;
   902                     break;
   903                 }
   904             }
   905             break;
   906         default:
   907             trap = SDL_TRUE;
   908             break;
   909         }
   910         break;
   911     }
   912     return trap;
   913 }
   914 
   915 static void
   916 IME_CloseCandidateList(SDL_VideoData *videodata)
   917 {
   918     IME_HideCandidateList(videodata);
   919     videodata->ime_candcount = 0;
   920     SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   921 }
   922 
   923 static void
   924 UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
   925 {
   926     UINT selection = 0;
   927     UINT count = 0;
   928     UINT page = 0;
   929     UINT pgcount = 0;
   930     DWORD pgstart = 0;
   931     DWORD pgsize = 0;
   932     UINT i, j;
   933     pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
   934     pcandlist->lpVtbl->GetCount(pcandlist, &count);
   935     pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
   936 
   937     videodata->ime_candsel = selection;
   938     videodata->ime_candcount = count;
   939     IME_ShowCandidateList(videodata);
   940 
   941     pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
   942     if (pgcount > 0) {
   943         UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
   944         if (idxlist) {
   945             pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
   946             pgstart = idxlist[page];
   947             if (page < pgcount - 1)
   948                 pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
   949             else
   950                 pgsize = count - pgstart;
   951 
   952             SDL_free(idxlist);
   953         }
   954     }
   955     videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
   956     videodata->ime_candsel = videodata->ime_candsel - pgstart;
   957 
   958     SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   959     for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++) {
   960         BSTR bstr;
   961         if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr))) {
   962             if (bstr) {
   963                 IME_AddCandidate(videodata, j, bstr);
   964                 SysFreeString(bstr);
   965             }
   966         }
   967     }
   968     if (PRIMLANG() == LANG_KOREAN)
   969         videodata->ime_candsel = -1;
   970 }
   971 
   972 STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
   973 {
   974     return ++sink->refcount;
   975 }
   976 
   977 STDMETHODIMP_(ULONG)TSFSink_Release(TSFSink *sink)
   978 {
   979     --sink->refcount;
   980     if (sink->refcount == 0) {
   981         SDL_free(sink);
   982         return 0;
   983     }
   984     return sink->refcount;
   985 }
   986 
   987 STDMETHODIMP UIElementSink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
   988 {
   989     if (!ppv)
   990         return E_INVALIDARG;
   991 
   992     *ppv = 0;
   993     if (SDL_IsEqualIID(riid, &IID_IUnknown))
   994         *ppv = (IUnknown *)sink;
   995     else if (SDL_IsEqualIID(riid, &IID_ITfUIElementSink))
   996         *ppv = (ITfUIElementSink *)sink;
   997 
   998     if (*ppv) {
   999         TSFSink_AddRef(sink);
  1000         return S_OK;
  1001     }
  1002     return E_NOINTERFACE;
  1003 }
  1004 
  1005 ITfUIElement *UILess_GetUIElement(SDL_VideoData *videodata, DWORD dwUIElementId)
  1006 {
  1007     ITfUIElementMgr *puiem = 0;
  1008     ITfUIElement *pelem = 0;
  1009     ITfThreadMgrEx *threadmgrex = videodata->ime_threadmgrex;
  1010 
  1011     if (SUCCEEDED(threadmgrex->lpVtbl->QueryInterface(threadmgrex, &IID_ITfUIElementMgr, (LPVOID *)&puiem))) {
  1012         puiem->lpVtbl->GetUIElement(puiem, dwUIElementId, &pelem);
  1013         puiem->lpVtbl->Release(puiem);
  1014     }
  1015     return pelem;
  1016 }
  1017 
  1018 STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow)
  1019 {
  1020     ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
  1021     ITfReadingInformationUIElement *preading = 0;
  1022     ITfCandidateListUIElement *pcandlist = 0;
  1023     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1024     if (!element)
  1025         return E_INVALIDARG;
  1026 
  1027     *pbShow = FALSE;
  1028     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
  1029         BSTR bstr;
  1030         if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
  1031             SysFreeString(bstr);
  1032         }
  1033         preading->lpVtbl->Release(preading);
  1034     }
  1035     else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
  1036         videodata->ime_candref++;
  1037         UILess_GetCandidateList(videodata, pcandlist);
  1038         pcandlist->lpVtbl->Release(pcandlist);
  1039     }
  1040     return S_OK;
  1041 }
  1042 
  1043 STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
  1044 {
  1045     ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
  1046     ITfReadingInformationUIElement *preading = 0;
  1047     ITfCandidateListUIElement *pcandlist = 0;
  1048     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1049     if (!element)
  1050         return E_INVALIDARG;
  1051 
  1052     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
  1053         BSTR bstr;
  1054         if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
  1055             WCHAR *s = (WCHAR *)bstr;
  1056             SDL_wcslcpy(videodata->ime_readingstring, s, SDL_arraysize(videodata->ime_readingstring));
  1057             IME_SendEditingEvent(videodata);
  1058             SysFreeString(bstr);
  1059         }
  1060         preading->lpVtbl->Release(preading);
  1061     }
  1062     else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
  1063         UILess_GetCandidateList(videodata, pcandlist);
  1064         pcandlist->lpVtbl->Release(pcandlist);
  1065     }
  1066     return S_OK;
  1067 }
  1068 
  1069 STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
  1070 {
  1071     ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
  1072     ITfReadingInformationUIElement *preading = 0;
  1073     ITfCandidateListUIElement *pcandlist = 0;
  1074     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1075     if (!element)
  1076         return E_INVALIDARG;
  1077 
  1078     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
  1079         videodata->ime_readingstring[0] = 0;
  1080         IME_SendEditingEvent(videodata);
  1081         preading->lpVtbl->Release(preading);
  1082     }
  1083     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
  1084         videodata->ime_candref--;
  1085         if (videodata->ime_candref == 0)
  1086             IME_CloseCandidateList(videodata);
  1087 
  1088         pcandlist->lpVtbl->Release(pcandlist);
  1089     }
  1090     return S_OK;
  1091 }
  1092 
  1093 STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
  1094 {
  1095     if (!ppv)
  1096         return E_INVALIDARG;
  1097 
  1098     *ppv = 0;
  1099     if (SDL_IsEqualIID(riid, &IID_IUnknown))
  1100         *ppv = (IUnknown *)sink;
  1101     else if (SDL_IsEqualIID(riid, &IID_ITfInputProcessorProfileActivationSink))
  1102         *ppv = (ITfInputProcessorProfileActivationSink *)sink;
  1103 
  1104     if (*ppv) {
  1105         TSFSink_AddRef(sink);
  1106         return S_OK;
  1107     }
  1108     return E_NOINTERFACE;
  1109 }
  1110 
  1111 STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
  1112 {
  1113     static const GUID TF_PROFILE_DAYI = { 0x037B2C25, 0x480C, 0x4D7F, { 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A } };
  1114     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1115     videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
  1116     if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
  1117         IME_InputLangChanged((SDL_VideoData *)sink->data);
  1118 
  1119     IME_HideCandidateList(videodata);
  1120     return S_OK;
  1121 }
  1122 
  1123 static void *vtUIElementSink[] = {
  1124     (void *)(UIElementSink_QueryInterface),
  1125     (void *)(TSFSink_AddRef),
  1126     (void *)(TSFSink_Release),
  1127     (void *)(UIElementSink_BeginUIElement),
  1128     (void *)(UIElementSink_UpdateUIElement),
  1129     (void *)(UIElementSink_EndUIElement)
  1130 };
  1131 
  1132 static void *vtIPPASink[] = {
  1133     (void *)(IPPASink_QueryInterface),
  1134     (void *)(TSFSink_AddRef),
  1135     (void *)(TSFSink_Release),
  1136     (void *)(IPPASink_OnActivated)
  1137 };
  1138 
  1139 static void
  1140 UILess_EnableUIUpdates(SDL_VideoData *videodata)
  1141 {
  1142     ITfSource *source = 0;
  1143     if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie != TF_INVALID_COOKIE)
  1144         return;
  1145 
  1146     if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1147         source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie);
  1148         source->lpVtbl->Release(source);
  1149     }
  1150 }
  1151 
  1152 static void
  1153 UILess_DisableUIUpdates(SDL_VideoData *videodata)
  1154 {
  1155     ITfSource *source = 0;
  1156     if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie == TF_INVALID_COOKIE)
  1157         return;
  1158 
  1159     if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1160         source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
  1161         videodata->ime_uielemsinkcookie = TF_INVALID_COOKIE;
  1162         source->lpVtbl->Release(source);
  1163     }
  1164 }
  1165 
  1166 static SDL_bool
  1167 UILess_SetupSinks(SDL_VideoData *videodata)
  1168 {
  1169     TfClientId clientid = 0;
  1170     SDL_bool result = SDL_FALSE;
  1171     ITfSource *source = 0;
  1172     if (FAILED(CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, (LPVOID *)&videodata->ime_threadmgrex)))
  1173         return SDL_FALSE;
  1174 
  1175     if (FAILED(videodata->ime_threadmgrex->lpVtbl->ActivateEx(videodata->ime_threadmgrex, &clientid, TF_TMAE_UIELEMENTENABLEDONLY)))
  1176         return SDL_FALSE;
  1177 
  1178     videodata->ime_uielemsink = SDL_malloc(sizeof(TSFSink));
  1179     videodata->ime_ippasink = SDL_malloc(sizeof(TSFSink));
  1180 
  1181     videodata->ime_uielemsink->lpVtbl = vtUIElementSink;
  1182     videodata->ime_uielemsink->refcount = 1;
  1183     videodata->ime_uielemsink->data = videodata;
  1184 
  1185     videodata->ime_ippasink->lpVtbl = vtIPPASink;
  1186     videodata->ime_ippasink->refcount = 1;
  1187     videodata->ime_ippasink->data = videodata;
  1188 
  1189     if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1190         if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie))) {
  1191             if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfInputProcessorProfileActivationSink, (IUnknown *)videodata->ime_ippasink, &videodata->ime_alpnsinkcookie))) {
  1192                 result = SDL_TRUE;
  1193             }
  1194         }
  1195         source->lpVtbl->Release(source);
  1196     }
  1197     return result;
  1198 }
  1199 
  1200 #define SAFE_RELEASE(p)                             \
  1201 {                                                   \
  1202     if (p) {                                        \
  1203         (p)->lpVtbl->Release((p));                  \
  1204         (p) = 0;                                    \
  1205     }                                               \
  1206 }
  1207 
  1208 static void
  1209 UILess_ReleaseSinks(SDL_VideoData *videodata)
  1210 {
  1211     ITfSource *source = 0;
  1212     if (videodata->ime_threadmgrex && SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1213         source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
  1214         source->lpVtbl->UnadviseSink(source, videodata->ime_alpnsinkcookie);
  1215         SAFE_RELEASE(source);
  1216         videodata->ime_threadmgrex->lpVtbl->Deactivate(videodata->ime_threadmgrex);
  1217         SAFE_RELEASE(videodata->ime_threadmgrex);
  1218         TSFSink_Release(videodata->ime_uielemsink);
  1219         videodata->ime_uielemsink = 0;
  1220         TSFSink_Release(videodata->ime_ippasink);
  1221         videodata->ime_ippasink = 0;
  1222     }
  1223 }
  1224 
  1225 static void *
  1226 StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
  1227 {
  1228     BITMAPINFO info;
  1229     BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
  1230     BYTE *bits = NULL;
  1231     if (hhbm) {
  1232         SDL_zero(info);
  1233         infoHeader->biSize = sizeof(BITMAPINFOHEADER);
  1234         infoHeader->biWidth = width;
  1235         infoHeader->biHeight = -1 * SDL_abs(height);
  1236         infoHeader->biPlanes = 1;
  1237         infoHeader->biBitCount = 32;
  1238         infoHeader->biCompression = BI_RGB;
  1239         *hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
  1240         if (*hhbm)
  1241             SelectObject(hdc, *hhbm);
  1242     }
  1243     return bits;
  1244 }
  1245 
  1246 static void
  1247 StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
  1248 {
  1249     if (hhbm && *hhbm) {
  1250         DeleteObject(*hhbm);
  1251         *hhbm = NULL;
  1252     }
  1253 }
  1254 
  1255 /* This draws only within the specified area and fills the entire region. */
  1256 static void
  1257 DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
  1258 {
  1259     /* The case of no pen (PenSize = 0) is automatically taken care of. */
  1260     const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
  1261     left += pensize / 2;
  1262     top += pensize / 2;
  1263     right -= penadjust;
  1264     bottom -= penadjust;
  1265     Rectangle(hdc, left, top, right, bottom);
  1266 }
  1267 
  1268 static void
  1269 IME_DestroyTextures(SDL_VideoData *videodata)
  1270 {
  1271 }
  1272 
  1273 #define SDL_swap(a,b) { \
  1274     int c = (a);        \
  1275     (a) = (b);          \
  1276     (b) = c;            \
  1277     }
  1278 
  1279 static void
  1280 IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
  1281 {
  1282     int left, top, right, bottom;
  1283     SDL_bool ok = SDL_FALSE;
  1284     int winw = videodata->ime_winwidth;
  1285     int winh = videodata->ime_winheight;
  1286 
  1287     /* Bottom */
  1288     left = videodata->ime_rect.x;
  1289     top = videodata->ime_rect.y + videodata->ime_rect.h;
  1290     right = left + size.cx;
  1291     bottom = top + size.cy;
  1292     if (right >= winw) {
  1293         left -= right - winw;
  1294         right = winw;
  1295     }
  1296     if (bottom < winh)
  1297         ok = SDL_TRUE;
  1298 
  1299     /* Top */
  1300     if (!ok) {
  1301         left = videodata->ime_rect.x;
  1302         top = videodata->ime_rect.y - size.cy;
  1303         right = left + size.cx;
  1304         bottom = videodata->ime_rect.y;
  1305         if (right >= winw) {
  1306             left -= right - winw;
  1307             right = winw;
  1308         }
  1309         if (top >= 0)
  1310             ok = SDL_TRUE;
  1311     }
  1312 
  1313     /* Right */
  1314     if (!ok) {
  1315         left = videodata->ime_rect.x + size.cx;
  1316         top = 0;
  1317         right = left + size.cx;
  1318         bottom = size.cy;
  1319         if (right < winw)
  1320             ok = SDL_TRUE;
  1321     }
  1322 
  1323     /* Left */
  1324     if (!ok) {
  1325         left = videodata->ime_rect.x - size.cx;
  1326         top = 0;
  1327         right = videodata->ime_rect.x;
  1328         bottom = size.cy;
  1329         if (right >= 0)
  1330             ok = SDL_TRUE;
  1331     }
  1332 
  1333     /* Window too small, show at (0,0) */
  1334     if (!ok) {
  1335         left = 0;
  1336         top = 0;
  1337         right = size.cx;
  1338         bottom = size.cy;
  1339     }
  1340 
  1341     videodata->ime_candlistrect.x = left;
  1342     videodata->ime_candlistrect.y = top;
  1343     videodata->ime_candlistrect.w = right - left;
  1344     videodata->ime_candlistrect.h = bottom - top;
  1345 }
  1346 
  1347 static void
  1348 IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
  1349 {
  1350     int i, j;
  1351     SIZE size = {0};
  1352     SIZE candsizes[MAX_CANDLIST];
  1353     SIZE maxcandsize = {0};
  1354     HBITMAP hbm = NULL;
  1355     const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
  1356     SDL_bool vertical = videodata->ime_candvertical;
  1357 
  1358     const int listborder = 1;
  1359     const int listpadding = 0;
  1360     const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
  1361     const int listfillcolor = RGB(255, 255, 255);
  1362 
  1363     const int candborder = 1;
  1364     const int candpadding = 0;
  1365     const int candmargin = 1;
  1366     const COLORREF candbordercolor = RGB(255, 255, 255);
  1367     const COLORREF candfillcolor = RGB(255, 255, 255);
  1368     const COLORREF candtextcolor = RGB(0, 0, 0);
  1369     const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
  1370     const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
  1371     const COLORREF seltextcolor = RGB(0, 0, 0);
  1372     const int horzcandspacing = 5;
  1373 
  1374     HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
  1375     HBRUSH listbrush = CreateSolidBrush(listfillcolor);
  1376     HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
  1377     HBRUSH candbrush = CreateSolidBrush(candfillcolor);
  1378     HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
  1379     HBRUSH selbrush = CreateSolidBrush(selfillcolor);
  1380     HFONT font = CreateFont((int)(1 + videodata->ime_rect.h * 0.75f), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH | FF_SWISS, TEXT("Microsoft Sans Serif"));
  1381 
  1382     SetBkMode(hdc, TRANSPARENT);
  1383     SelectObject(hdc, font);
  1384 
  1385     for (i = 0; i < candcount; ++i) {
  1386         const WCHAR *s = videodata->ime_candidates[i];
  1387         if (!*s)
  1388             break;
  1389 
  1390         GetTextExtentPoint32W(hdc, s, SDL_wcslen(s), &candsizes[i]);
  1391         maxcandsize.cx = SDL_max(maxcandsize.cx, candsizes[i].cx);
  1392         maxcandsize.cy = SDL_max(maxcandsize.cy, candsizes[i].cy);
  1393 
  1394     }
  1395     if (vertical) {
  1396         size.cx =
  1397             (listborder * 2) +
  1398             (listpadding * 2) +
  1399             (candmargin * 2) +
  1400             (candborder * 2) +
  1401             (candpadding * 2) +
  1402             (maxcandsize.cx)
  1403             ;
  1404         size.cy =
  1405             (listborder * 2) +
  1406             (listpadding * 2) +
  1407             ((candcount + 1) * candmargin) +
  1408             (candcount * candborder * 2) +
  1409             (candcount * candpadding * 2) +
  1410             (candcount * maxcandsize.cy)
  1411             ;
  1412     }
  1413     else {
  1414         size.cx =
  1415             (listborder * 2) +
  1416             (listpadding * 2) +
  1417             ((candcount + 1) * candmargin) +
  1418             (candcount * candborder * 2) +
  1419             (candcount * candpadding * 2) +
  1420             ((candcount - 1) * horzcandspacing);
  1421         ;
  1422 
  1423         for (i = 0; i < candcount; ++i)
  1424             size.cx += candsizes[i].cx;
  1425 
  1426         size.cy =
  1427             (listborder * 2) +
  1428             (listpadding * 2) +
  1429             (candmargin * 2) +
  1430             (candborder * 2) +
  1431             (candpadding * 2) +
  1432             (maxcandsize.cy)
  1433             ;
  1434     }
  1435 
  1436     StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
  1437 
  1438     SelectObject(hdc, listpen);
  1439     SelectObject(hdc, listbrush);
  1440     DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
  1441 
  1442     SelectObject(hdc, candpen);
  1443     SelectObject(hdc, candbrush);
  1444     SetTextColor(hdc, candtextcolor);
  1445     SetBkMode(hdc, TRANSPARENT);
  1446 
  1447     for (i = 0; i < candcount; ++i) {
  1448         const WCHAR *s = videodata->ime_candidates[i];
  1449         int left, top, right, bottom;
  1450         if (!*s)
  1451             break;
  1452 
  1453         if (vertical) {
  1454             left = listborder + listpadding + candmargin;
  1455             top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
  1456             right = size.cx - listborder - listpadding - candmargin;
  1457             bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
  1458         }
  1459         else {
  1460             left = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * horzcandspacing);
  1461 
  1462             for (j = 0; j < i; ++j)
  1463                 left += candsizes[j].cx;
  1464 
  1465             top = listborder + listpadding + candmargin;
  1466             right = left + candsizes[i].cx + (candpadding * 2) + (candborder * 2);
  1467             bottom = size.cy - listborder - listpadding - candmargin;
  1468         }
  1469 
  1470         if (i == videodata->ime_candsel) {
  1471             SelectObject(hdc, selpen);
  1472             SelectObject(hdc, selbrush);
  1473             SetTextColor(hdc, seltextcolor);
  1474         }
  1475         else {
  1476             SelectObject(hdc, candpen);
  1477             SelectObject(hdc, candbrush);
  1478             SetTextColor(hdc, candtextcolor);
  1479         }
  1480 
  1481         DrawRect(hdc, left, top, right, bottom, candborder);
  1482         ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, SDL_wcslen(s), NULL);
  1483     }
  1484     StopDrawToBitmap(hdc, &hbm);
  1485 
  1486     DeleteObject(listpen);
  1487     DeleteObject(listbrush);
  1488     DeleteObject(candpen);
  1489     DeleteObject(candbrush);
  1490     DeleteObject(selpen);
  1491     DeleteObject(selbrush);
  1492     DeleteObject(font);
  1493 
  1494     IME_PositionCandidateList(videodata, size);
  1495 }
  1496 
  1497 static void
  1498 IME_Render(SDL_VideoData *videodata)
  1499 {
  1500     HDC hdc = CreateCompatibleDC(NULL);
  1501 
  1502     if (videodata->ime_candlist)
  1503         IME_RenderCandidateList(videodata, hdc);
  1504 
  1505     DeleteDC(hdc);
  1506 
  1507     videodata->ime_dirty = SDL_FALSE;
  1508 }
  1509 
  1510 void IME_Present(SDL_VideoData *videodata)
  1511 {
  1512     if (videodata->ime_dirty)
  1513         IME_Render(videodata);
  1514 
  1515     /* FIXME: Need to show the IME bitmap */
  1516 }
  1517 
  1518 #endif /* SDL_DISABLE_WINDOWS_IME */
  1519 
  1520 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
  1521 
  1522 /* vi: set ts=4 sw=4 expandtab: */