Atlas - SDL_windowskeyboard.c

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