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