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