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