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