src/video/windows/SDL_windowskeyboard.c
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Mon, 25 Feb 2013 16:52:51 -0800
changeset 6925 59fedfb8faaf
parent 6922 91d157d9f283
child 6926 166712f45009
permissions -rw-r--r--
sdl2
- more scancode goodness, removing now dead wparam translation code
- add scancode for SDL_SCANCODE_NONUSBACKSLASH
- don't translate sdl key values for numeric and grave key

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