src/video/windows/SDL_windowskeyboard.c
author Ryan C. Gordon
Mon, 23 Jan 2017 12:06:10 -0500
changeset 10837 c2f241c2f6ad
parent 10737 3406a0f8b041
child 10885 4528f2a99fb7
permissions -rw-r--r--
audio: Fix same bug as last commit, but for _mm_bslli_si128 vs _mm_slli_si128.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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)0xE0060404)
   299 #define CHT_HKL_NEW_PHONETIC    ((HKL)0xE0080404)
   300 #define CHT_HKL_NEW_CHANG_JIE   ((HKL)0xE0090404)
   301 #define CHT_HKL_NEW_QUICK       ((HKL)0xE00A0404)
   302 #define CHT_HKL_HK_CANTONESE    ((HKL)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)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 #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
   335 #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
   336 
   337 static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata);
   338 static void UILess_ReleaseSinks(SDL_VideoData *videodata);
   339 static void UILess_EnableUIUpdates(SDL_VideoData *videodata);
   340 static void UILess_DisableUIUpdates(SDL_VideoData *videodata);
   341 
   342 static void
   343 IME_Init(SDL_VideoData *videodata, HWND hwnd)
   344 {
   345     if (videodata->ime_initialized)
   346         return;
   347 
   348     videodata->ime_hwnd_main = hwnd;
   349     if (SUCCEEDED(WIN_CoInitialize())) {
   350         videodata->ime_com_initialized = SDL_TRUE;
   351         CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (LPVOID *)&videodata->ime_threadmgr);
   352     }
   353     videodata->ime_initialized = SDL_TRUE;
   354     videodata->ime_himm32 = SDL_LoadObject("imm32.dll");
   355     if (!videodata->ime_himm32) {
   356         videodata->ime_available = SDL_FALSE;
   357         return;
   358     }
   359     videodata->ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMC");
   360     videodata->ImmUnlockIMC = (BOOL (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMC");
   361     videodata->ImmLockIMCC = (LPVOID (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMCC");
   362     videodata->ImmUnlockIMCC = (BOOL (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMCC");
   363 
   364     IME_SetWindow(videodata, hwnd);
   365     videodata->ime_himc = ImmGetContext(hwnd);
   366     ImmReleaseContext(hwnd, videodata->ime_himc);
   367     if (!videodata->ime_himc) {
   368         videodata->ime_available = SDL_FALSE;
   369         IME_Disable(videodata, hwnd);
   370         return;
   371     }
   372     videodata->ime_available = SDL_TRUE;
   373     IME_UpdateInputLocale(videodata);
   374     IME_SetupAPI(videodata);
   375     videodata->ime_uiless = UILess_SetupSinks(videodata);
   376     IME_UpdateInputLocale(videodata);
   377     IME_Disable(videodata, hwnd);
   378 }
   379 
   380 static void
   381 IME_Enable(SDL_VideoData *videodata, HWND hwnd)
   382 {
   383     if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
   384         return;
   385 
   386     if (!videodata->ime_available) {
   387         IME_Disable(videodata, hwnd);
   388         return;
   389     }
   390     if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
   391         ImmAssociateContext(videodata->ime_hwnd_current, videodata->ime_himc);
   392 
   393     videodata->ime_enabled = SDL_TRUE;
   394     IME_UpdateInputLocale(videodata);
   395     UILess_EnableUIUpdates(videodata);
   396 }
   397 
   398 static void
   399 IME_Disable(SDL_VideoData *videodata, HWND hwnd)
   400 {
   401     if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
   402         return;
   403 
   404     IME_ClearComposition(videodata);
   405     if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
   406         ImmAssociateContext(videodata->ime_hwnd_current, (HIMC)0);
   407 
   408     videodata->ime_enabled = SDL_FALSE;
   409     UILess_DisableUIUpdates(videodata);
   410 }
   411 
   412 static void
   413 IME_Quit(SDL_VideoData *videodata)
   414 {
   415     if (!videodata->ime_initialized)
   416         return;
   417 
   418     UILess_ReleaseSinks(videodata);
   419     if (videodata->ime_hwnd_main)
   420         ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc);
   421 
   422     videodata->ime_hwnd_main = 0;
   423     videodata->ime_himc = 0;
   424     if (videodata->ime_himm32) {
   425         SDL_UnloadObject(videodata->ime_himm32);
   426         videodata->ime_himm32 = 0;
   427     }
   428     if (videodata->ime_threadmgr) {
   429         videodata->ime_threadmgr->lpVtbl->Release(videodata->ime_threadmgr);
   430         videodata->ime_threadmgr = 0;
   431     }
   432     if (videodata->ime_com_initialized) {
   433         WIN_CoUninitialize();
   434         videodata->ime_com_initialized = SDL_FALSE;
   435     }
   436     IME_DestroyTextures(videodata);
   437     videodata->ime_initialized = SDL_FALSE;
   438 }
   439 
   440 static void
   441 IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd)
   442 {
   443     DWORD id = 0;
   444     HIMC himc = 0;
   445     WCHAR buffer[16];
   446     WCHAR *s = buffer;
   447     DWORD len = 0;
   448     INT err = 0;
   449     BOOL vertical = FALSE;
   450     UINT maxuilen = 0;
   451     static OSVERSIONINFOA osversion;
   452 
   453     if (videodata->ime_uiless)
   454         return;
   455 
   456     videodata->ime_readingstring[0] = 0;
   457     if (!osversion.dwOSVersionInfoSize) {
   458         osversion.dwOSVersionInfoSize = sizeof(osversion);
   459         GetVersionExA(&osversion);
   460     }
   461     id = IME_GetId(videodata, 0);
   462     if (!id)
   463         return;
   464 
   465     himc = ImmGetContext(hwnd);
   466     if (!himc)
   467         return;
   468 
   469     if (videodata->GetReadingString) {
   470         len = videodata->GetReadingString(himc, 0, 0, &err, &vertical, &maxuilen);
   471         if (len) {
   472             if (len > SDL_arraysize(buffer))
   473                 len = SDL_arraysize(buffer);
   474 
   475             len = videodata->GetReadingString(himc, len, s, &err, &vertical, &maxuilen);
   476         }
   477         SDL_wcslcpy(videodata->ime_readingstring, s, len);
   478     }
   479     else {
   480         LPINPUTCONTEXT2 lpimc = videodata->ImmLockIMC(himc);
   481         LPBYTE p = 0;
   482         s = 0;
   483         switch (id)
   484         {
   485         case IMEID_CHT_VER42:
   486         case IMEID_CHT_VER43:
   487         case IMEID_CHT_VER44:
   488             p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 24);
   489             if (!p)
   490                 break;
   491 
   492             len = *(DWORD *)(p + 7*4 + 32*4);
   493             s = (WCHAR *)(p + 56);
   494             break;
   495         case IMEID_CHT_VER51:
   496         case IMEID_CHT_VER52:
   497         case IMEID_CHS_VER53:
   498             p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 4);
   499             if (!p)
   500                 break;
   501 
   502             p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);
   503             if (!p)
   504                 break;
   505 
   506             len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
   507             s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
   508             break;
   509         case IMEID_CHS_VER41:
   510             {
   511                 int offset = (IME_GetId(videodata, 1) >= 0x00000002) ? 8 : 7;
   512                 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + offset * 4);
   513                 if (!p)
   514                     break;
   515 
   516                 len = *(DWORD *)(p + 7*4 + 16*2*4);
   517                 s = (WCHAR *)(p + 6*4 + 16*2*1);
   518             }
   519             break;
   520         case IMEID_CHS_VER42:
   521             if (osversion.dwPlatformId != VER_PLATFORM_WIN32_NT)
   522                 break;
   523 
   524             p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 1*4 + 1*4 + 6*4);
   525             if (!p)
   526                 break;
   527 
   528             len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
   529             s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
   530             break;
   531         }
   532         if (s) {
   533             size_t size = SDL_min((size_t)(len + 1), SDL_arraysize(videodata->ime_readingstring));
   534             SDL_wcslcpy(videodata->ime_readingstring, s, size);
   535         }
   536 
   537         videodata->ImmUnlockIMCC(lpimc->hPrivate);
   538         videodata->ImmUnlockIMC(himc);
   539     }
   540     ImmReleaseContext(hwnd, himc);
   541     IME_SendEditingEvent(videodata);
   542 }
   543 
   544 static void
   545 IME_InputLangChanged(SDL_VideoData *videodata)
   546 {
   547     UINT lang = PRIMLANG();
   548     IME_UpdateInputLocale(videodata);
   549     if (!videodata->ime_uiless)
   550         videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
   551 
   552     IME_SetupAPI(videodata);
   553     if (lang != PRIMLANG()) {
   554         IME_ClearComposition(videodata);
   555     }
   556 }
   557 
   558 static DWORD
   559 IME_GetId(SDL_VideoData *videodata, UINT uIndex)
   560 {
   561     static HKL hklprev = 0;
   562     static DWORD dwRet[2] = {0};
   563     DWORD dwVerSize = 0;
   564     DWORD dwVerHandle = 0;
   565     LPVOID lpVerBuffer = 0;
   566     LPVOID lpVerData = 0;
   567     UINT cbVerData = 0;
   568     char szTemp[256];
   569     HKL hkl = 0;
   570     DWORD dwLang = 0;
   571     if (uIndex >= sizeof(dwRet) / sizeof(dwRet[0]))
   572         return 0;
   573 
   574     hkl = videodata->ime_hkl;
   575     if (hklprev == hkl)
   576         return dwRet[uIndex];
   577 
   578     hklprev = hkl;
   579     dwLang = ((DWORD_PTR)hkl & 0xffff);
   580     if (videodata->ime_uiless && LANG() == LANG_CHT) {
   581         dwRet[0] = IMEID_CHT_VER_VISTA;
   582         dwRet[1] = 0;
   583         return dwRet[0];
   584     }
   585     if (hkl != CHT_HKL_NEW_PHONETIC
   586         && hkl != CHT_HKL_NEW_CHANG_JIE
   587         && hkl != CHT_HKL_NEW_QUICK
   588         && hkl != CHT_HKL_HK_CANTONESE
   589         && hkl != CHS_HKL) {
   590         dwRet[0] = dwRet[1] = 0;
   591         return dwRet[uIndex];
   592     }
   593     if (ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1) <= 0) {
   594         dwRet[0] = dwRet[1] = 0;
   595         return dwRet[uIndex];
   596     }
   597     if (!videodata->GetReadingString) {
   598         #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
   599         if (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME1, -1) != 2
   600             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME2, -1) != 2
   601             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME3, -1) != 2
   602             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME1, -1) != 2
   603             && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME2, -1) != 2) {
   604             dwRet[0] = dwRet[1] = 0;
   605             return dwRet[uIndex];
   606         }
   607         #undef LCID_INVARIANT
   608         dwVerSize = GetFileVersionInfoSizeA(szTemp, &dwVerHandle);
   609         if (dwVerSize) {
   610             lpVerBuffer = SDL_malloc(dwVerSize);
   611             if (lpVerBuffer) {
   612                 if (GetFileVersionInfoA(szTemp, dwVerHandle, dwVerSize, lpVerBuffer)) {
   613                     if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) {
   614                         #define pVerFixedInfo   ((VS_FIXEDFILEINFO FAR*)lpVerData)
   615                         DWORD dwVer = pVerFixedInfo->dwFileVersionMS;
   616                         dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16;
   617                         if ((videodata->GetReadingString) ||
   618                             ((dwLang == LANG_CHT) && (
   619                             dwVer == MAKEIMEVERSION(4, 2) ||
   620                             dwVer == MAKEIMEVERSION(4, 3) ||
   621                             dwVer == MAKEIMEVERSION(4, 4) ||
   622                             dwVer == MAKEIMEVERSION(5, 0) ||
   623                             dwVer == MAKEIMEVERSION(5, 1) ||
   624                             dwVer == MAKEIMEVERSION(5, 2) ||
   625                             dwVer == MAKEIMEVERSION(6, 0)))
   626                             ||
   627                             ((dwLang == LANG_CHS) && (
   628                             dwVer == MAKEIMEVERSION(4, 1) ||
   629                             dwVer == MAKEIMEVERSION(4, 2) ||
   630                             dwVer == MAKEIMEVERSION(5, 3)))) {
   631                             dwRet[0] = dwVer | dwLang;
   632                             dwRet[1] = pVerFixedInfo->dwFileVersionLS;
   633                             SDL_free(lpVerBuffer);
   634                             return dwRet[0];
   635                         }
   636                         #undef pVerFixedInfo
   637                     }
   638                 }
   639             }
   640             SDL_free(lpVerBuffer);
   641         }
   642     }
   643     dwRet[0] = dwRet[1] = 0;
   644     return dwRet[uIndex];
   645 }
   646 
   647 static void
   648 IME_SetupAPI(SDL_VideoData *videodata)
   649 {
   650     char ime_file[MAX_PATH + 1];
   651     void* hime = 0;
   652     HKL hkl = 0;
   653     videodata->GetReadingString = 0;
   654     videodata->ShowReadingWindow = 0;
   655     if (videodata->ime_uiless)
   656         return;
   657 
   658     hkl = videodata->ime_hkl;
   659     if (ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1) <= 0)
   660         return;
   661 
   662     hime = SDL_LoadObject(ime_file);
   663     if (!hime)
   664         return;
   665 
   666     videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT))
   667         SDL_LoadFunction(hime, "GetReadingString");
   668     videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL))
   669         SDL_LoadFunction(hime, "ShowReadingWindow");
   670 
   671     if (videodata->ShowReadingWindow) {
   672         HIMC himc = ImmGetContext(videodata->ime_hwnd_current);
   673         if (himc) {
   674             videodata->ShowReadingWindow(himc, FALSE);
   675             ImmReleaseContext(videodata->ime_hwnd_current, himc);
   676         }
   677     }
   678 }
   679 
   680 static void
   681 IME_SetWindow(SDL_VideoData* videodata, HWND hwnd)
   682 {
   683     videodata->ime_hwnd_current = hwnd;
   684     if (videodata->ime_threadmgr) {
   685         struct ITfDocumentMgr *document_mgr = 0;
   686         if (SUCCEEDED(videodata->ime_threadmgr->lpVtbl->AssociateFocus(videodata->ime_threadmgr, hwnd, NULL, &document_mgr))) {
   687             if (document_mgr)
   688                 document_mgr->lpVtbl->Release(document_mgr);
   689         }
   690     }
   691 }
   692 
   693 static void
   694 IME_UpdateInputLocale(SDL_VideoData *videodata)
   695 {
   696     static HKL hklprev = 0;
   697     videodata->ime_hkl = GetKeyboardLayout(0);
   698     if (hklprev == videodata->ime_hkl)
   699         return;
   700 
   701     hklprev = videodata->ime_hkl;
   702     switch (PRIMLANG()) {
   703     case LANG_CHINESE:
   704         videodata->ime_candvertical = SDL_TRUE;
   705         if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
   706             videodata->ime_candvertical = SDL_FALSE;
   707 
   708         break;
   709     case LANG_JAPANESE:
   710         videodata->ime_candvertical = SDL_TRUE;
   711         break;
   712     case LANG_KOREAN:
   713         videodata->ime_candvertical = SDL_FALSE;
   714         break;
   715     }
   716 }
   717 
   718 static void
   719 IME_ClearComposition(SDL_VideoData *videodata)
   720 {
   721     HIMC himc = 0;
   722     if (!videodata->ime_initialized)
   723         return;
   724 
   725     himc = ImmGetContext(videodata->ime_hwnd_current);
   726     if (!himc)
   727         return;
   728 
   729     ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   730     if (videodata->ime_uiless)
   731         ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
   732 
   733     ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0);
   734     ImmReleaseContext(videodata->ime_hwnd_current, himc);
   735     SDL_SendEditingText("", 0, 0);
   736 }
   737 
   738 static void
   739 IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
   740 {
   741     LONG length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, sizeof(videodata->ime_composition) - sizeof(videodata->ime_composition[0]));
   742     if (length < 0)
   743         length = 0;
   744 
   745     length /= sizeof(videodata->ime_composition[0]);
   746     videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0));
   747     if (videodata->ime_cursor < SDL_arraysize(videodata->ime_composition) && videodata->ime_composition[videodata->ime_cursor] == 0x3000) {
   748         int i;
   749         for (i = videodata->ime_cursor + 1; i < length; ++i)
   750             videodata->ime_composition[i - 1] = videodata->ime_composition[i];
   751 
   752         --length;
   753     }
   754     videodata->ime_composition[length] = 0;
   755 }
   756 
   757 static void
   758 IME_SendInputEvent(SDL_VideoData *videodata)
   759 {
   760     char *s = 0;
   761     s = WIN_StringToUTF8(videodata->ime_composition);
   762     SDL_SendKeyboardText(s);
   763     SDL_free(s);
   764 
   765     videodata->ime_composition[0] = 0;
   766     videodata->ime_readingstring[0] = 0;
   767     videodata->ime_cursor = 0;
   768 }
   769 
   770 static void
   771 IME_SendEditingEvent(SDL_VideoData *videodata)
   772 {
   773     char *s = 0;
   774     WCHAR buffer[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
   775     const size_t size = SDL_arraysize(buffer);
   776     buffer[0] = 0;
   777     if (videodata->ime_readingstring[0]) {
   778         size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor);
   779         SDL_wcslcpy(buffer, videodata->ime_composition, len + 1);
   780         SDL_wcslcat(buffer, videodata->ime_readingstring, size);
   781         SDL_wcslcat(buffer, &videodata->ime_composition[len], size);
   782     }
   783     else {
   784         SDL_wcslcpy(buffer, videodata->ime_composition, size);
   785     }
   786     s = WIN_StringToUTF8(buffer);
   787     SDL_SendEditingText(s, videodata->ime_cursor + (int)SDL_wcslen(videodata->ime_readingstring), 0);
   788     SDL_free(s);
   789 }
   790 
   791 static void
   792 IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
   793 {
   794     LPWSTR dst = videodata->ime_candidates[i];
   795     *dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
   796     if (videodata->ime_candvertical)
   797         *dst++ = TEXT(' ');
   798 
   799     while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
   800         *dst++ = *candidate++;
   801 
   802     *dst = (WCHAR)'\0';
   803 }
   804 
   805 static void
   806 IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
   807 {
   808     LPCANDIDATELIST cand_list = 0;
   809     DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
   810     if (size) {
   811         cand_list = (LPCANDIDATELIST)SDL_malloc(size);
   812         if (cand_list) {
   813             size = ImmGetCandidateListW(himc, 0, cand_list, size);
   814             if (size) {
   815                 UINT i, j;
   816                 UINT page_start = 0;
   817                 videodata->ime_candsel = cand_list->dwSelection;
   818                 videodata->ime_candcount = cand_list->dwCount;
   819 
   820                 if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) {
   821                     const UINT maxcandchar = 18;
   822                     size_t cchars = 0;
   823 
   824                     for (i = 0; i < videodata->ime_candcount; ++i) {
   825                         size_t len = SDL_wcslen((LPWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i])) + 1;
   826                         if (len + cchars > maxcandchar) {
   827                             if (i > cand_list->dwSelection)
   828                                 break;
   829 
   830                             page_start = i;
   831                             cchars = len;
   832                         }
   833                         else {
   834                             cchars += len;
   835                         }
   836                     }
   837                     videodata->ime_candpgsize = i - page_start;
   838                 } else {
   839                     videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
   840                     page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
   841                 }
   842                 SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   843                 for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++) {
   844                     LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]);
   845                     IME_AddCandidate(videodata, j, candidate);
   846                 }
   847                 if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
   848                     videodata->ime_candsel = -1;
   849 
   850             }
   851             SDL_free(cand_list);
   852         }
   853     }
   854 }
   855 
   856 static void
   857 IME_ShowCandidateList(SDL_VideoData *videodata)
   858 {
   859     videodata->ime_dirty = SDL_TRUE;
   860     videodata->ime_candlist = SDL_TRUE;
   861     IME_DestroyTextures(videodata);
   862     IME_SendEditingEvent(videodata);
   863 }
   864 
   865 static void
   866 IME_HideCandidateList(SDL_VideoData *videodata)
   867 {
   868     videodata->ime_dirty = SDL_FALSE;
   869     videodata->ime_candlist = SDL_FALSE;
   870     IME_DestroyTextures(videodata);
   871     IME_SendEditingEvent(videodata);
   872 }
   873 
   874 SDL_bool
   875 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
   876 {
   877     SDL_bool trap = SDL_FALSE;
   878     HIMC himc = 0;
   879     if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled)
   880         return SDL_FALSE;
   881 
   882     switch (msg) {
   883     case WM_INPUTLANGCHANGE:
   884         IME_InputLangChanged(videodata);
   885         break;
   886     case WM_IME_SETCONTEXT:
   887         *lParam = 0;
   888         break;
   889     case WM_IME_STARTCOMPOSITION:
   890         trap = SDL_TRUE;
   891         break;
   892     case WM_IME_COMPOSITION:
   893         trap = SDL_TRUE;
   894         himc = ImmGetContext(hwnd);
   895         if (*lParam & GCS_RESULTSTR) {
   896             IME_GetCompositionString(videodata, himc, GCS_RESULTSTR);
   897             IME_SendInputEvent(videodata);
   898         }
   899         if (*lParam & GCS_COMPSTR) {
   900             if (!videodata->ime_uiless)
   901                 videodata->ime_readingstring[0] = 0;
   902 
   903             IME_GetCompositionString(videodata, himc, GCS_COMPSTR);
   904             IME_SendEditingEvent(videodata);
   905         }
   906         ImmReleaseContext(hwnd, himc);
   907         break;
   908     case WM_IME_ENDCOMPOSITION:
   909         videodata->ime_composition[0] = 0;
   910         videodata->ime_readingstring[0] = 0;
   911         videodata->ime_cursor = 0;
   912         SDL_SendEditingText("", 0, 0);
   913         break;
   914     case WM_IME_NOTIFY:
   915         switch (wParam) {
   916         case IMN_SETCONVERSIONMODE:
   917         case IMN_SETOPENSTATUS:
   918             IME_UpdateInputLocale(videodata);
   919             break;
   920         case IMN_OPENCANDIDATE:
   921         case IMN_CHANGECANDIDATE:
   922             if (videodata->ime_uiless)
   923                 break;
   924 
   925             trap = SDL_TRUE;
   926             IME_ShowCandidateList(videodata);
   927             himc = ImmGetContext(hwnd);
   928             if (!himc)
   929                 break;
   930 
   931             IME_GetCandidateList(himc, videodata);
   932             ImmReleaseContext(hwnd, himc);
   933             break;
   934         case IMN_CLOSECANDIDATE:
   935             trap = SDL_TRUE;
   936             IME_HideCandidateList(videodata);
   937             break;
   938         case IMN_PRIVATE:
   939             {
   940                 DWORD dwId = IME_GetId(videodata, 0);
   941                 IME_GetReadingString(videodata, hwnd);
   942                 switch (dwId)
   943                 {
   944                 case IMEID_CHT_VER42:
   945                 case IMEID_CHT_VER43:
   946                 case IMEID_CHT_VER44:
   947                 case IMEID_CHS_VER41:
   948                 case IMEID_CHS_VER42:
   949                     if (*lParam == 1 || *lParam == 2)
   950                         trap = SDL_TRUE;
   951 
   952                     break;
   953                 case IMEID_CHT_VER50:
   954                 case IMEID_CHT_VER51:
   955                 case IMEID_CHT_VER52:
   956                 case IMEID_CHT_VER60:
   957                 case IMEID_CHS_VER53:
   958                     if (*lParam == 16
   959                         || *lParam == 17
   960                         || *lParam == 26
   961                         || *lParam == 27
   962                         || *lParam == 28)
   963                         trap = SDL_TRUE;
   964                     break;
   965                 }
   966             }
   967             break;
   968         default:
   969             trap = SDL_TRUE;
   970             break;
   971         }
   972         break;
   973     }
   974     return trap;
   975 }
   976 
   977 static void
   978 IME_CloseCandidateList(SDL_VideoData *videodata)
   979 {
   980     IME_HideCandidateList(videodata);
   981     videodata->ime_candcount = 0;
   982     SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
   983 }
   984 
   985 static void
   986 UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
   987 {
   988     UINT selection = 0;
   989     UINT count = 0;
   990     UINT page = 0;
   991     UINT pgcount = 0;
   992     DWORD pgstart = 0;
   993     DWORD pgsize = 0;
   994     UINT i, j;
   995     pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
   996     pcandlist->lpVtbl->GetCount(pcandlist, &count);
   997     pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
   998 
   999     videodata->ime_candsel = selection;
  1000     videodata->ime_candcount = count;
  1001     IME_ShowCandidateList(videodata);
  1002 
  1003     pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
  1004     if (pgcount > 0) {
  1005         UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
  1006         if (idxlist) {
  1007             pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
  1008             pgstart = idxlist[page];
  1009             if (page < pgcount - 1)
  1010                 pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
  1011             else
  1012                 pgsize = count - pgstart;
  1013 
  1014             SDL_free(idxlist);
  1015         }
  1016     }
  1017     videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
  1018     videodata->ime_candsel = videodata->ime_candsel - pgstart;
  1019 
  1020     SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
  1021     for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++) {
  1022         BSTR bstr;
  1023         if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr))) {
  1024             if (bstr) {
  1025                 IME_AddCandidate(videodata, j, bstr);
  1026                 SysFreeString(bstr);
  1027             }
  1028         }
  1029     }
  1030     if (PRIMLANG() == LANG_KOREAN)
  1031         videodata->ime_candsel = -1;
  1032 }
  1033 
  1034 STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
  1035 {
  1036     return ++sink->refcount;
  1037 }
  1038 
  1039 STDMETHODIMP_(ULONG) TSFSink_Release(TSFSink *sink)
  1040 {
  1041     --sink->refcount;
  1042     if (sink->refcount == 0) {
  1043         SDL_free(sink);
  1044         return 0;
  1045     }
  1046     return sink->refcount;
  1047 }
  1048 
  1049 STDMETHODIMP UIElementSink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
  1050 {
  1051     if (!ppv)
  1052         return E_INVALIDARG;
  1053 
  1054     *ppv = 0;
  1055     if (SDL_IsEqualIID(riid, &IID_IUnknown))
  1056         *ppv = (IUnknown *)sink;
  1057     else if (SDL_IsEqualIID(riid, &IID_ITfUIElementSink))
  1058         *ppv = (ITfUIElementSink *)sink;
  1059 
  1060     if (*ppv) {
  1061         TSFSink_AddRef(sink);
  1062         return S_OK;
  1063     }
  1064     return E_NOINTERFACE;
  1065 }
  1066 
  1067 ITfUIElement *UILess_GetUIElement(SDL_VideoData *videodata, DWORD dwUIElementId)
  1068 {
  1069     ITfUIElementMgr *puiem = 0;
  1070     ITfUIElement *pelem = 0;
  1071     ITfThreadMgrEx *threadmgrex = videodata->ime_threadmgrex;
  1072 
  1073     if (SUCCEEDED(threadmgrex->lpVtbl->QueryInterface(threadmgrex, &IID_ITfUIElementMgr, (LPVOID *)&puiem))) {
  1074         puiem->lpVtbl->GetUIElement(puiem, dwUIElementId, &pelem);
  1075         puiem->lpVtbl->Release(puiem);
  1076     }
  1077     return pelem;
  1078 }
  1079 
  1080 STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow)
  1081 {
  1082     ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
  1083     ITfReadingInformationUIElement *preading = 0;
  1084     ITfCandidateListUIElement *pcandlist = 0;
  1085     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1086     if (!element)
  1087         return E_INVALIDARG;
  1088 
  1089     *pbShow = FALSE;
  1090     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
  1091         BSTR bstr;
  1092         if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
  1093             SysFreeString(bstr);
  1094         }
  1095         preading->lpVtbl->Release(preading);
  1096     }
  1097     else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
  1098         videodata->ime_candref++;
  1099         UILess_GetCandidateList(videodata, pcandlist);
  1100         pcandlist->lpVtbl->Release(pcandlist);
  1101     }
  1102     return S_OK;
  1103 }
  1104 
  1105 STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
  1106 {
  1107     ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
  1108     ITfReadingInformationUIElement *preading = 0;
  1109     ITfCandidateListUIElement *pcandlist = 0;
  1110     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1111     if (!element)
  1112         return E_INVALIDARG;
  1113 
  1114     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
  1115         BSTR bstr;
  1116         if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
  1117             WCHAR *s = (WCHAR *)bstr;
  1118             SDL_wcslcpy(videodata->ime_readingstring, s, SDL_arraysize(videodata->ime_readingstring));
  1119             IME_SendEditingEvent(videodata);
  1120             SysFreeString(bstr);
  1121         }
  1122         preading->lpVtbl->Release(preading);
  1123     }
  1124     else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
  1125         UILess_GetCandidateList(videodata, pcandlist);
  1126         pcandlist->lpVtbl->Release(pcandlist);
  1127     }
  1128     return S_OK;
  1129 }
  1130 
  1131 STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
  1132 {
  1133     ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
  1134     ITfReadingInformationUIElement *preading = 0;
  1135     ITfCandidateListUIElement *pcandlist = 0;
  1136     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1137     if (!element)
  1138         return E_INVALIDARG;
  1139 
  1140     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
  1141         videodata->ime_readingstring[0] = 0;
  1142         IME_SendEditingEvent(videodata);
  1143         preading->lpVtbl->Release(preading);
  1144     }
  1145     if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
  1146         videodata->ime_candref--;
  1147         if (videodata->ime_candref == 0)
  1148             IME_CloseCandidateList(videodata);
  1149 
  1150         pcandlist->lpVtbl->Release(pcandlist);
  1151     }
  1152     return S_OK;
  1153 }
  1154 
  1155 STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
  1156 {
  1157     if (!ppv)
  1158         return E_INVALIDARG;
  1159 
  1160     *ppv = 0;
  1161     if (SDL_IsEqualIID(riid, &IID_IUnknown))
  1162         *ppv = (IUnknown *)sink;
  1163     else if (SDL_IsEqualIID(riid, &IID_ITfInputProcessorProfileActivationSink))
  1164         *ppv = (ITfInputProcessorProfileActivationSink *)sink;
  1165 
  1166     if (*ppv) {
  1167         TSFSink_AddRef(sink);
  1168         return S_OK;
  1169     }
  1170     return E_NOINTERFACE;
  1171 }
  1172 
  1173 STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
  1174 {
  1175     static const GUID TF_PROFILE_DAYI = { 0x037B2C25, 0x480C, 0x4D7F, { 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A } };
  1176     SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
  1177     videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
  1178     if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
  1179         IME_InputLangChanged((SDL_VideoData *)sink->data);
  1180 
  1181     IME_HideCandidateList(videodata);
  1182     return S_OK;
  1183 }
  1184 
  1185 static void *vtUIElementSink[] = {
  1186     (void *)(UIElementSink_QueryInterface),
  1187     (void *)(TSFSink_AddRef),
  1188     (void *)(TSFSink_Release),
  1189     (void *)(UIElementSink_BeginUIElement),
  1190     (void *)(UIElementSink_UpdateUIElement),
  1191     (void *)(UIElementSink_EndUIElement)
  1192 };
  1193 
  1194 static void *vtIPPASink[] = {
  1195     (void *)(IPPASink_QueryInterface),
  1196     (void *)(TSFSink_AddRef),
  1197     (void *)(TSFSink_Release),
  1198     (void *)(IPPASink_OnActivated)
  1199 };
  1200 
  1201 static void
  1202 UILess_EnableUIUpdates(SDL_VideoData *videodata)
  1203 {
  1204     ITfSource *source = 0;
  1205     if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie != TF_INVALID_COOKIE)
  1206         return;
  1207 
  1208     if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1209         source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie);
  1210         source->lpVtbl->Release(source);
  1211     }
  1212 }
  1213 
  1214 static void
  1215 UILess_DisableUIUpdates(SDL_VideoData *videodata)
  1216 {
  1217     ITfSource *source = 0;
  1218     if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie == TF_INVALID_COOKIE)
  1219         return;
  1220 
  1221     if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1222         source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
  1223         videodata->ime_uielemsinkcookie = TF_INVALID_COOKIE;
  1224         source->lpVtbl->Release(source);
  1225     }
  1226 }
  1227 
  1228 static SDL_bool
  1229 UILess_SetupSinks(SDL_VideoData *videodata)
  1230 {
  1231     TfClientId clientid = 0;
  1232     SDL_bool result = SDL_FALSE;
  1233     ITfSource *source = 0;
  1234     if (FAILED(CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, (LPVOID *)&videodata->ime_threadmgrex)))
  1235         return SDL_FALSE;
  1236 
  1237     if (FAILED(videodata->ime_threadmgrex->lpVtbl->ActivateEx(videodata->ime_threadmgrex, &clientid, TF_TMAE_UIELEMENTENABLEDONLY)))
  1238         return SDL_FALSE;
  1239 
  1240     videodata->ime_uielemsink = SDL_malloc(sizeof(TSFSink));
  1241     videodata->ime_ippasink = SDL_malloc(sizeof(TSFSink));
  1242 
  1243     videodata->ime_uielemsink->lpVtbl = vtUIElementSink;
  1244     videodata->ime_uielemsink->refcount = 1;
  1245     videodata->ime_uielemsink->data = videodata;
  1246 
  1247     videodata->ime_ippasink->lpVtbl = vtIPPASink;
  1248     videodata->ime_ippasink->refcount = 1;
  1249     videodata->ime_ippasink->data = videodata;
  1250 
  1251     if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1252         if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie))) {
  1253             if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfInputProcessorProfileActivationSink, (IUnknown *)videodata->ime_ippasink, &videodata->ime_alpnsinkcookie))) {
  1254                 result = SDL_TRUE;
  1255             }
  1256         }
  1257         source->lpVtbl->Release(source);
  1258     }
  1259     return result;
  1260 }
  1261 
  1262 #define SAFE_RELEASE(p)                             \
  1263 {                                                   \
  1264     if (p) {                                        \
  1265         (p)->lpVtbl->Release((p));                  \
  1266         (p) = 0;                                    \
  1267     }                                               \
  1268 }
  1269 
  1270 static void
  1271 UILess_ReleaseSinks(SDL_VideoData *videodata)
  1272 {
  1273     ITfSource *source = 0;
  1274     if (videodata->ime_threadmgrex && SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
  1275         source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
  1276         source->lpVtbl->UnadviseSink(source, videodata->ime_alpnsinkcookie);
  1277         SAFE_RELEASE(source);
  1278         videodata->ime_threadmgrex->lpVtbl->Deactivate(videodata->ime_threadmgrex);
  1279         SAFE_RELEASE(videodata->ime_threadmgrex);
  1280         TSFSink_Release(videodata->ime_uielemsink);
  1281         videodata->ime_uielemsink = 0;
  1282         TSFSink_Release(videodata->ime_ippasink);
  1283         videodata->ime_ippasink = 0;
  1284     }
  1285 }
  1286 
  1287 static void *
  1288 StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
  1289 {
  1290     BITMAPINFO info;
  1291     BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
  1292     BYTE *bits = NULL;
  1293     if (hhbm) {
  1294         SDL_zero(info);
  1295         infoHeader->biSize = sizeof(BITMAPINFOHEADER);
  1296         infoHeader->biWidth = width;
  1297         infoHeader->biHeight = -1 * SDL_abs(height);
  1298         infoHeader->biPlanes = 1;
  1299         infoHeader->biBitCount = 32;
  1300         infoHeader->biCompression = BI_RGB;
  1301         *hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
  1302         if (*hhbm)
  1303             SelectObject(hdc, *hhbm);
  1304     }
  1305     return bits;
  1306 }
  1307 
  1308 static void
  1309 StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
  1310 {
  1311     if (hhbm && *hhbm) {
  1312         DeleteObject(*hhbm);
  1313         *hhbm = NULL;
  1314     }
  1315 }
  1316 
  1317 /* This draws only within the specified area and fills the entire region. */
  1318 static void
  1319 DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
  1320 {
  1321     /* The case of no pen (PenSize = 0) is automatically taken care of. */
  1322     const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
  1323     left += pensize / 2;
  1324     top += pensize / 2;
  1325     right -= penadjust;
  1326     bottom -= penadjust;
  1327     Rectangle(hdc, left, top, right, bottom);
  1328 }
  1329 
  1330 static void
  1331 IME_DestroyTextures(SDL_VideoData *videodata)
  1332 {
  1333 }
  1334 
  1335 #define SDL_swap(a,b) { \
  1336     int c = (a);        \
  1337     (a) = (b);          \
  1338     (b) = c;            \
  1339     }
  1340 
  1341 static void
  1342 IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
  1343 {
  1344     int left, top, right, bottom;
  1345     SDL_bool ok = SDL_FALSE;
  1346     int winw = videodata->ime_winwidth;
  1347     int winh = videodata->ime_winheight;
  1348 
  1349     /* Bottom */
  1350     left = videodata->ime_rect.x;
  1351     top = videodata->ime_rect.y + videodata->ime_rect.h;
  1352     right = left + size.cx;
  1353     bottom = top + size.cy;
  1354     if (right >= winw) {
  1355         left -= right - winw;
  1356         right = winw;
  1357     }
  1358     if (bottom < winh)
  1359         ok = SDL_TRUE;
  1360 
  1361     /* Top */
  1362     if (!ok) {
  1363         left = videodata->ime_rect.x;
  1364         top = videodata->ime_rect.y - size.cy;
  1365         right = left + size.cx;
  1366         bottom = videodata->ime_rect.y;
  1367         if (right >= winw) {
  1368             left -= right - winw;
  1369             right = winw;
  1370         }
  1371         if (top >= 0)
  1372             ok = SDL_TRUE;
  1373     }
  1374 
  1375     /* Right */
  1376     if (!ok) {
  1377         left = videodata->ime_rect.x + size.cx;
  1378         top = 0;
  1379         right = left + size.cx;
  1380         bottom = size.cy;
  1381         if (right < winw)
  1382             ok = SDL_TRUE;
  1383     }
  1384 
  1385     /* Left */
  1386     if (!ok) {
  1387         left = videodata->ime_rect.x - size.cx;
  1388         top = 0;
  1389         right = videodata->ime_rect.x;
  1390         bottom = size.cy;
  1391         if (right >= 0)
  1392             ok = SDL_TRUE;
  1393     }
  1394 
  1395     /* Window too small, show at (0,0) */
  1396     if (!ok) {
  1397         left = 0;
  1398         top = 0;
  1399         right = size.cx;
  1400         bottom = size.cy;
  1401     }
  1402 
  1403     videodata->ime_candlistrect.x = left;
  1404     videodata->ime_candlistrect.y = top;
  1405     videodata->ime_candlistrect.w = right - left;
  1406     videodata->ime_candlistrect.h = bottom - top;
  1407 }
  1408 
  1409 static void
  1410 IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
  1411 {
  1412     int i, j;
  1413     SIZE size = {0};
  1414     SIZE candsizes[MAX_CANDLIST];
  1415     SIZE maxcandsize = {0};
  1416     HBITMAP hbm = NULL;
  1417     const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
  1418     SDL_bool vertical = videodata->ime_candvertical;
  1419 
  1420     const int listborder = 1;
  1421     const int listpadding = 0;
  1422     const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
  1423     const int listfillcolor = RGB(255, 255, 255);
  1424 
  1425     const int candborder = 1;
  1426     const int candpadding = 0;
  1427     const int candmargin = 1;
  1428     const COLORREF candbordercolor = RGB(255, 255, 255);
  1429     const COLORREF candfillcolor = RGB(255, 255, 255);
  1430     const COLORREF candtextcolor = RGB(0, 0, 0);
  1431     const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
  1432     const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
  1433     const COLORREF seltextcolor = RGB(0, 0, 0);
  1434     const int horzcandspacing = 5;
  1435 
  1436     HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
  1437     HBRUSH listbrush = CreateSolidBrush(listfillcolor);
  1438     HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
  1439     HBRUSH candbrush = CreateSolidBrush(candfillcolor);
  1440     HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
  1441     HBRUSH selbrush = CreateSolidBrush(selfillcolor);
  1442     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"));
  1443 
  1444     SetBkMode(hdc, TRANSPARENT);
  1445     SelectObject(hdc, font);
  1446 
  1447     for (i = 0; i < candcount; ++i) {
  1448         const WCHAR *s = videodata->ime_candidates[i];
  1449         if (!*s)
  1450             break;
  1451 
  1452         GetTextExtentPoint32W(hdc, s, (int)SDL_wcslen(s), &candsizes[i]);
  1453         maxcandsize.cx = SDL_max(maxcandsize.cx, candsizes[i].cx);
  1454         maxcandsize.cy = SDL_max(maxcandsize.cy, candsizes[i].cy);
  1455 
  1456     }
  1457     if (vertical) {
  1458         size.cx =
  1459             (listborder * 2) +
  1460             (listpadding * 2) +
  1461             (candmargin * 2) +
  1462             (candborder * 2) +
  1463             (candpadding * 2) +
  1464             (maxcandsize.cx)
  1465             ;
  1466         size.cy =
  1467             (listborder * 2) +
  1468             (listpadding * 2) +
  1469             ((candcount + 1) * candmargin) +
  1470             (candcount * candborder * 2) +
  1471             (candcount * candpadding * 2) +
  1472             (candcount * maxcandsize.cy)
  1473             ;
  1474     }
  1475     else {
  1476         size.cx =
  1477             (listborder * 2) +
  1478             (listpadding * 2) +
  1479             ((candcount + 1) * candmargin) +
  1480             (candcount * candborder * 2) +
  1481             (candcount * candpadding * 2) +
  1482             ((candcount - 1) * horzcandspacing);
  1483         ;
  1484 
  1485         for (i = 0; i < candcount; ++i)
  1486             size.cx += candsizes[i].cx;
  1487 
  1488         size.cy =
  1489             (listborder * 2) +
  1490             (listpadding * 2) +
  1491             (candmargin * 2) +
  1492             (candborder * 2) +
  1493             (candpadding * 2) +
  1494             (maxcandsize.cy)
  1495             ;
  1496     }
  1497 
  1498     StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
  1499 
  1500     SelectObject(hdc, listpen);
  1501     SelectObject(hdc, listbrush);
  1502     DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
  1503 
  1504     SelectObject(hdc, candpen);
  1505     SelectObject(hdc, candbrush);
  1506     SetTextColor(hdc, candtextcolor);
  1507     SetBkMode(hdc, TRANSPARENT);
  1508 
  1509     for (i = 0; i < candcount; ++i) {
  1510         const WCHAR *s = videodata->ime_candidates[i];
  1511         int left, top, right, bottom;
  1512         if (!*s)
  1513             break;
  1514 
  1515         if (vertical) {
  1516             left = listborder + listpadding + candmargin;
  1517             top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
  1518             right = size.cx - listborder - listpadding - candmargin;
  1519             bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
  1520         }
  1521         else {
  1522             left = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * horzcandspacing);
  1523 
  1524             for (j = 0; j < i; ++j)
  1525                 left += candsizes[j].cx;
  1526 
  1527             top = listborder + listpadding + candmargin;
  1528             right = left + candsizes[i].cx + (candpadding * 2) + (candborder * 2);
  1529             bottom = size.cy - listborder - listpadding - candmargin;
  1530         }
  1531 
  1532         if (i == videodata->ime_candsel) {
  1533             SelectObject(hdc, selpen);
  1534             SelectObject(hdc, selbrush);
  1535             SetTextColor(hdc, seltextcolor);
  1536         }
  1537         else {
  1538             SelectObject(hdc, candpen);
  1539             SelectObject(hdc, candbrush);
  1540             SetTextColor(hdc, candtextcolor);
  1541         }
  1542 
  1543         DrawRect(hdc, left, top, right, bottom, candborder);
  1544         ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, (int)SDL_wcslen(s), NULL);
  1545     }
  1546     StopDrawToBitmap(hdc, &hbm);
  1547 
  1548     DeleteObject(listpen);
  1549     DeleteObject(listbrush);
  1550     DeleteObject(candpen);
  1551     DeleteObject(candbrush);
  1552     DeleteObject(selpen);
  1553     DeleteObject(selbrush);
  1554     DeleteObject(font);
  1555 
  1556     IME_PositionCandidateList(videodata, size);
  1557 }
  1558 
  1559 static void
  1560 IME_Render(SDL_VideoData *videodata)
  1561 {
  1562     HDC hdc = CreateCompatibleDC(NULL);
  1563 
  1564     if (videodata->ime_candlist)
  1565         IME_RenderCandidateList(videodata, hdc);
  1566 
  1567     DeleteDC(hdc);
  1568 
  1569     videodata->ime_dirty = SDL_FALSE;
  1570 }
  1571 
  1572 void IME_Present(SDL_VideoData *videodata)
  1573 {
  1574     if (videodata->ime_dirty)
  1575         IME_Render(videodata);
  1576 
  1577     /* FIXME: Need to show the IME bitmap */
  1578 }
  1579 
  1580 #endif /* SDL_DISABLE_WINDOWS_IME */
  1581 
  1582 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
  1583 
  1584 /* vi: set ts=4 sw=4 expandtab: */