Atlas - SDL_windowskeyboard.c
Home / ext / SDL / src / video / windows Lines: 1 | Size: 41553 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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 defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 24 25#include "SDL_windowsvideo.h" 26 27#include "../../events/SDL_keyboard_c.h" 28#include "../../events/scancodes_windows.h" 29 30#include <imm.h> 31#include <oleauto.h> 32 33#ifndef SDL_DISABLE_WINDOWS_IME 34#if 0 35#define SDL_DebugIMELog SDL_Log 36#else 37#define SDL_DebugIMELog(...) 38#endif 39static bool IME_Init(SDL_VideoData *videodata, SDL_Window *window); 40static void IME_Enable(SDL_VideoData *videodata, HWND hwnd); 41static void IME_Disable(SDL_VideoData *videodata, HWND hwnd); 42static void IME_SetTextInputArea(SDL_VideoData *videodata, HWND hwnd, const SDL_Rect *rect, int cursor); 43static void IME_ClearComposition(SDL_VideoData *videodata); 44static void IME_GetCandidateList(SDL_VideoData *videodata, HWND hwnd); 45static void IME_Quit(SDL_VideoData *videodata); 46#else 47static void IME_SetTextInputArea(SDL_VideoData *videodata, HWND hwnd, const SDL_Rect *rect, int cursor); 48#endif // !SDL_DISABLE_WINDOWS_IME 49 50#ifndef MAPVK_VK_TO_VSC 51#define MAPVK_VK_TO_VSC 0 52#endif 53#ifndef MAPVK_VSC_TO_VK 54#define MAPVK_VSC_TO_VK 1 55#endif 56 57/* Building keymaps is expensive, so keep a reasonably-sized LRU cache to 58 * enable fast switching between commonly used ones. 59 */ 60static struct WIN_KeymapCache 61{ 62 HKL keyboard_layout; 63 SDL_Keymap *keymap; 64} keymap_cache[4]; 65 66static int keymap_cache_size; 67 68static SDL_Keymap *WIN_GetCachedKeymap(HKL layout) 69{ 70 SDL_Keymap *keymap = NULL; 71 for (int i = 0; i < keymap_cache_size; ++i) { 72 if (keymap_cache[i].keyboard_layout == layout) { 73 keymap = keymap_cache[i].keymap; 74 75 // Move the map to the front of the list. 76 if (i) { 77 SDL_memmove(keymap_cache + 1, keymap_cache, sizeof(struct WIN_KeymapCache) * i); 78 keymap_cache[0].keyboard_layout = layout; 79 keymap_cache[0].keymap = keymap; 80 } 81 break; 82 } 83 } 84 return keymap; 85} 86 87static void WIN_CacheKeymap(HKL layout, SDL_Keymap *keymap) 88{ 89 // If the cache is full, evict the last keymap. 90 if (keymap_cache_size == SDL_arraysize(keymap_cache)) { 91 SDL_DestroyKeymap(keymap_cache[--keymap_cache_size].keymap); 92 } 93 94 // Move all elements down by one. 95 if (keymap_cache_size) { 96 SDL_memmove(keymap_cache + 1, keymap_cache, sizeof(struct WIN_KeymapCache) * keymap_cache_size); 97 } 98 99 keymap_cache[0].keyboard_layout = layout; 100 keymap_cache[0].keymap = keymap; 101 ++keymap_cache_size; 102} 103 104static SDL_Keymap *WIN_BuildKeymap() 105{ 106 SDL_Scancode scancode; 107 BYTE keyboardState[256] = { 0 }; 108 WCHAR buffer[16]; 109 const SDL_Keymod mods[] = { 110 SDL_KMOD_NONE, 111 SDL_KMOD_SHIFT, 112 SDL_KMOD_CAPS, 113 (SDL_KMOD_SHIFT | SDL_KMOD_CAPS), 114 SDL_KMOD_MODE, 115 (SDL_KMOD_MODE | SDL_KMOD_SHIFT), 116 (SDL_KMOD_MODE | SDL_KMOD_CAPS), 117 (SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS) 118 }; 119 120 WIN_ResetDeadKeys(); 121 122 SDL_Keymap *keymap = SDL_CreateKeymap(false); 123 if (!keymap) { 124 return NULL; 125 } 126 127 for (int m = 0; m < SDL_arraysize(mods); ++m) { 128 for (int i = 0; i < SDL_arraysize(windows_scancode_table); i++) { 129 int vk, sc, result; 130 Uint32 *ch = 0; 131 132 // Make sure this scancode is a valid character scancode 133 scancode = windows_scancode_table[i]; 134 if (scancode == SDL_SCANCODE_UNKNOWN || 135 scancode == SDL_SCANCODE_DELETE || 136 (SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & SDLK_SCANCODE_MASK)) { 137 138 // The Colemak mapping swaps Backspace and CapsLock 139 if (mods[m] == SDL_KMOD_NONE && 140 (scancode == SDL_SCANCODE_CAPSLOCK || 141 scancode == SDL_SCANCODE_BACKSPACE)) { 142 vk = LOBYTE(MapVirtualKey(i, MAPVK_VSC_TO_VK)); 143 if (vk == VK_CAPITAL) { 144 SDL_SetKeymapEntry(keymap, scancode, mods[m], SDLK_CAPSLOCK); 145 } else if (vk == VK_BACK) { 146 SDL_SetKeymapEntry(keymap, scancode, mods[m], SDLK_BACKSPACE); 147 } 148 } 149 continue; 150 } 151 152 // Unpack the single byte index to make the scan code. 153 sc = MAKEWORD(i & 0x7f, (i & 0x80) ? 0xe0 : 0x00); 154 vk = LOBYTE(MapVirtualKey(sc, MAPVK_VSC_TO_VK)); 155 if (!vk) { 156 continue; 157 } 158 159 // Update the keyboard state for the modifiers 160 keyboardState[VK_SHIFT] = (mods[m] & SDL_KMOD_SHIFT) ? 0x80 : 0x00; 161 keyboardState[VK_CAPITAL] = (mods[m] & SDL_KMOD_CAPS) ? 0x01 : 0x00; 162 keyboardState[VK_CONTROL] = (mods[m] & SDL_KMOD_MODE) ? 0x80 : 0x00; 163 keyboardState[VK_MENU] = (mods[m] & SDL_KMOD_MODE) ? 0x80 : 0x00; 164 165 result = ToUnicode(vk, sc, keyboardState, buffer, 16, 0); 166 buffer[SDL_abs(result)] = 0; 167 168 // Convert UTF-16 to UTF-32 code points 169 ch = (Uint32 *)SDL_iconv_string("UTF-32LE", "UTF-16LE", (const char *)buffer, (SDL_abs(result) + 1) * sizeof(WCHAR)); 170 if (ch) { 171 /* Windows keyboard layouts can emit several UTF-32 code points on a single key press. 172 * Use <U+FFFD REPLACEMENT CHARACTER> since we cannot fit into single SDL_Keycode value in SDL keymap. 173 * See https://kbdlayout.info/features/ligatures for a list of such keys. */ 174 SDL_SetKeymapEntry(keymap, scancode, mods[m], ch[1] == 0 ? ch[0] : 0xfffd); 175 SDL_free(ch); 176 } else { 177 // The default keymap doesn't have any SDL_KMOD_MODE entries, so we don't need to override them 178 if (!(mods[m] & SDL_KMOD_MODE)) { 179 SDL_SetKeymapEntry(keymap, scancode, mods[m], SDLK_UNKNOWN); 180 } 181 } 182 183 if (result < 0) { 184 WIN_ResetDeadKeys(); 185 } 186 } 187 } 188 189 return keymap; 190} 191 192void WIN_UpdateKeymap(bool send_event) 193{ 194 HKL layout = GetKeyboardLayout(0); 195 SDL_Keymap *keymap = WIN_GetCachedKeymap(layout); 196 if (!keymap) { 197 keymap = WIN_BuildKeymap(); 198 if (keymap) { 199 WIN_CacheKeymap(layout, keymap); 200 } 201 } 202 203 SDL_SetKeymap(keymap, send_event); 204} 205 206// Alphabetic scancodes for PC keyboards 207void WIN_InitKeyboard(SDL_VideoDevice *_this) 208{ 209#ifndef SDL_DISABLE_WINDOWS_IME 210 SDL_VideoData *data = _this->internal; 211 212 data->ime_candlistindexbase = 1; 213 data->ime_composition_length = 32 * sizeof(WCHAR); 214 data->ime_composition = (WCHAR *)SDL_calloc(data->ime_composition_length, sizeof(WCHAR)); 215#endif // !SDL_DISABLE_WINDOWS_IME 216 217 // Build and bind the current keymap. 218 WIN_UpdateKeymap(false); 219 220 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); 221 SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows"); 222 SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows"); 223 224 // Are system caps/num/scroll lock active? Set our state to match. 225 SDL_ToggleModState(SDL_KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) ? true : false); 226 SDL_ToggleModState(SDL_KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) ? true : false); 227 SDL_ToggleModState(SDL_KMOD_SCROLL, (GetKeyState(VK_SCROLL) & 0x0001) ? true : false); 228} 229 230void WIN_QuitKeyboard(SDL_VideoDevice *_this) 231{ 232#ifndef SDL_DISABLE_WINDOWS_IME 233 SDL_VideoData *data = _this->internal; 234 235 IME_Quit(data); 236 237 if (data->ime_composition) { 238 SDL_free(data->ime_composition); 239 data->ime_composition = NULL; 240 } 241#endif // !SDL_DISABLE_WINDOWS_IME 242 243 for (int i = 0; i < keymap_cache_size; ++i) { 244 SDL_DestroyKeymap(keymap_cache[i].keymap); 245 } 246 SDL_memset(keymap_cache, 0, sizeof(keymap_cache)); 247 keymap_cache_size = 0; 248} 249 250void WIN_ResetDeadKeys(void) 251{ 252 /* 253 if a deadkey has been typed, but not the next character (which the deadkey might modify), 254 this tries to undo the effect pressing the deadkey. 255 see: http://archives.miloush.net/michkap/archive/2006/09/10/748775.html 256 */ 257 BYTE keyboardState[256]; 258 WCHAR buffer[16]; 259 int vk, sc, result, i; 260 261 if (!GetKeyboardState(keyboardState)) { 262 return; 263 } 264 265 vk = VK_SPACE; 266 sc = MapVirtualKey(vk, MAPVK_VK_TO_VSC); 267 if (sc == 0) { 268 // the keyboard doesn't have this key 269 return; 270 } 271 272 for (i = 0; i < 5; i++) { 273 result = ToUnicode(vk, sc, keyboardState, buffer, 16, 0); 274 if (result > 0) { 275 // success 276 return; 277 } 278 } 279} 280 281bool WIN_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) 282{ 283 WIN_ResetDeadKeys(); 284 285#ifndef SDL_DISABLE_WINDOWS_IME 286 HWND hwnd = window->internal->hwnd; 287 SDL_VideoData *videodata = _this->internal; 288 IME_Init(videodata, window); 289 IME_Enable(videodata, hwnd); 290 291 WIN_UpdateTextInputArea(_this, window); 292#endif // !SDL_DISABLE_WINDOWS_IME 293 294 return true; 295} 296 297bool WIN_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) 298{ 299 WIN_ResetDeadKeys(); 300 301#ifndef SDL_DISABLE_WINDOWS_IME 302 HWND hwnd = window->internal->hwnd; 303 SDL_VideoData *videodata = _this->internal; 304 IME_Init(videodata, window); 305 IME_Disable(videodata, hwnd); 306#endif // !SDL_DISABLE_WINDOWS_IME 307 308 return true; 309} 310 311bool WIN_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window) 312{ 313 SDL_VideoData *videodata = _this->internal; 314 SDL_WindowData *data = window->internal; 315 316 IME_SetTextInputArea(videodata, data->hwnd, &window->text_input_rect, window->text_input_cursor); 317 return true; 318} 319 320bool WIN_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window) 321{ 322#ifndef SDL_DISABLE_WINDOWS_IME 323 SDL_VideoData *videodata = _this->internal; 324 325 IME_ClearComposition(videodata); 326#endif 327 return true; 328} 329 330#ifdef SDL_DISABLE_WINDOWS_IME 331 332bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata) 333{ 334 return false; 335} 336 337void WIN_UpdateIMECandidates(SDL_VideoDevice *_this) 338{ 339 return; 340} 341 342#else 343 344#define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) 345#define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) 346 347#define MAKEIMEVERSION(major, minor) ((DWORD)(((BYTE)(major) << 24) | ((BYTE)(minor) << 16))) 348#define IMEID_VER(id) ((id)&0xffff0000) 349#define IMEID_LANG(id) ((id)&0x0000ffff) 350 351#define CHT_HKL_DAYI ((HKL)(UINT_PTR)0xE0060404) 352#define CHT_HKL_NEW_PHONETIC ((HKL)(UINT_PTR)0xE0080404) 353#define CHT_HKL_NEW_CHANG_JIE ((HKL)(UINT_PTR)0xE0090404) 354#define CHT_HKL_NEW_QUICK ((HKL)(UINT_PTR)0xE00A0404) 355#define CHT_HKL_HK_CANTONESE ((HKL)(UINT_PTR)0xE00B0404) 356#define CHT_IMEFILENAME1 "TINTLGNT.IME" 357#define CHT_IMEFILENAME2 "CINTLGNT.IME" 358#define CHT_IMEFILENAME3 "MSTCIPHA.IME" 359#define IMEID_CHT_VER42 (LANG_CHT | MAKEIMEVERSION(4, 2)) 360#define IMEID_CHT_VER43 (LANG_CHT | MAKEIMEVERSION(4, 3)) 361#define IMEID_CHT_VER44 (LANG_CHT | MAKEIMEVERSION(4, 4)) 362#define IMEID_CHT_VER50 (LANG_CHT | MAKEIMEVERSION(5, 0)) 363#define IMEID_CHT_VER51 (LANG_CHT | MAKEIMEVERSION(5, 1)) 364#define IMEID_CHT_VER52 (LANG_CHT | MAKEIMEVERSION(5, 2)) 365#define IMEID_CHT_VER60 (LANG_CHT | MAKEIMEVERSION(6, 0)) 366#define IMEID_CHT_VER_VISTA (LANG_CHT | MAKEIMEVERSION(7, 0)) 367 368#define CHS_HKL ((HKL)(UINT_PTR)0xE00E0804) 369#define CHS_IMEFILENAME1 "PINTLGNT.IME" 370#define CHS_IMEFILENAME2 "MSSCIPYA.IME" 371#define IMEID_CHS_VER41 (LANG_CHS | MAKEIMEVERSION(4, 1)) 372#define IMEID_CHS_VER42 (LANG_CHS | MAKEIMEVERSION(4, 2)) 373#define IMEID_CHS_VER53 (LANG_CHS | MAKEIMEVERSION(5, 3)) 374 375#define LANG() LOWORD((videodata->ime_hkl)) 376#define PRIMLANG() ((WORD)PRIMARYLANGID(LANG())) 377#define SUBLANG() SUBLANGID(LANG()) 378 379static void IME_UpdateInputLocale(SDL_VideoData *videodata); 380static void IME_SetWindow(SDL_VideoData *videodata, SDL_Window *window); 381static void IME_SetupAPI(SDL_VideoData *videodata); 382static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex); 383static void IME_SendEditingEvent(SDL_VideoData *videodata); 384static void IME_SendClearComposition(SDL_VideoData *videodata); 385 386static bool IME_Init(SDL_VideoData *videodata, SDL_Window *window) 387{ 388 HWND hwnd = window->internal->hwnd; 389 390 if (videodata->ime_initialized) { 391 return true; 392 } 393 394 const char *hint = SDL_GetHint(SDL_HINT_IME_IMPLEMENTED_UI); 395 if (hint && SDL_strstr(hint, "composition")) { 396 videodata->ime_internal_composition = true; 397 } 398 if (hint && SDL_strstr(hint, "candidates")) { 399 videodata->ime_internal_candidates = true; 400 } 401 402 videodata->ime_hwnd_main = hwnd; 403 videodata->ime_initialized = true; 404 videodata->ime_himm32 = SDL_LoadObject("imm32.dll"); 405 if (!videodata->ime_himm32) { 406 videodata->ime_available = false; 407 SDL_ClearError(); 408 return true; 409 } 410 /* *INDENT-OFF* */ // clang-format off 411 videodata->ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMC"); 412 videodata->ImmUnlockIMC = (BOOL (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMC"); 413 videodata->ImmLockIMCC = (LPVOID (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMCC"); 414 videodata->ImmUnlockIMCC = (BOOL (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMCC"); 415 /* *INDENT-ON* */ // clang-format on 416 417 IME_SetWindow(videodata, window); 418 videodata->ime_himc = ImmGetContext(hwnd); 419 ImmReleaseContext(hwnd, videodata->ime_himc); 420 if (!videodata->ime_himc) { 421 videodata->ime_available = false; 422 IME_Disable(videodata, hwnd); 423 return true; 424 } 425 videodata->ime_available = true; 426 IME_UpdateInputLocale(videodata); 427 IME_SetupAPI(videodata); 428 IME_UpdateInputLocale(videodata); 429 IME_Disable(videodata, hwnd); 430 return true; 431} 432 433static void IME_Enable(SDL_VideoData *videodata, HWND hwnd) 434{ 435 if (!videodata->ime_initialized || !videodata->ime_hwnd_current) { 436 return; 437 } 438 439 if (!videodata->ime_available) { 440 IME_Disable(videodata, hwnd); 441 return; 442 } 443 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main) { 444 ImmAssociateContext(videodata->ime_hwnd_current, videodata->ime_himc); 445 } 446 447 videodata->ime_enabled = true; 448 IME_UpdateInputLocale(videodata); 449} 450 451static void IME_Disable(SDL_VideoData *videodata, HWND hwnd) 452{ 453 if (!videodata->ime_initialized || !videodata->ime_hwnd_current) { 454 return; 455 } 456 457 IME_ClearComposition(videodata); 458 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main) { 459 ImmAssociateContext(videodata->ime_hwnd_current, (HIMC)0); 460 } 461 462 videodata->ime_enabled = false; 463} 464 465static void IME_Quit(SDL_VideoData *videodata) 466{ 467 if (!videodata->ime_initialized) { 468 return; 469 } 470 471 if (videodata->ime_hwnd_main) { 472 ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc); 473 } 474 475 videodata->ime_hwnd_main = 0; 476 videodata->ime_himc = 0; 477 if (videodata->ime_himm32) { 478 SDL_UnloadObject(videodata->ime_himm32); 479 videodata->ime_himm32 = 0; 480 } 481 for (int i = 0; i < videodata->ime_candcount; ++i) { 482 SDL_free(videodata->ime_candidates[i]); 483 videodata->ime_candidates[i] = NULL; 484 } 485 videodata->ime_initialized = false; 486} 487 488static void IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd) 489{ 490 DWORD id = 0; 491 HIMC himc = 0; 492 WCHAR buffer[16]; 493 WCHAR *s = buffer; 494 DWORD len = 0; 495 INT err = 0; 496 BOOL vertical = FALSE; 497 UINT maxuilen = 0; 498 499 videodata->ime_readingstring[0] = 0; 500 501 id = IME_GetId(videodata, 0); 502 if (!id) { 503 return; 504 } 505 506 himc = ImmGetContext(hwnd); 507 if (!himc) { 508 return; 509 } 510 511 if (videodata->GetReadingString) { 512 len = videodata->GetReadingString(himc, 0, 0, &err, &vertical, &maxuilen); 513 if (len) { 514 if (len > SDL_arraysize(buffer)) { 515 len = SDL_arraysize(buffer); 516 } 517 518 len = videodata->GetReadingString(himc, len, s, &err, &vertical, &maxuilen); 519 } 520 SDL_wcslcpy(videodata->ime_readingstring, s, len); 521 } else { 522 LPINPUTCONTEXT2 lpimc = videodata->ImmLockIMC(himc); 523 LPBYTE p = 0; 524 s = 0; 525 switch (id) { 526 case IMEID_CHT_VER42: 527 case IMEID_CHT_VER43: 528 case IMEID_CHT_VER44: 529 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 24); 530 if (!p) { 531 break; 532 } 533 534 len = *(DWORD *)(p + 7 * 4 + 32 * 4); 535 s = (WCHAR *)(p + 56); 536 break; 537 case IMEID_CHT_VER51: 538 case IMEID_CHT_VER52: 539 case IMEID_CHS_VER53: 540 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 4); 541 if (!p) { 542 break; 543 } 544 545 p = *(LPBYTE *)(p + 1 * 4 + 5 * 4); 546 if (!p) { 547 break; 548 } 549 550 len = *(DWORD *)(p + 1 * 4 + (16 * 2 + 2 * 4) + 5 * 4 + 16 * 2); 551 s = (WCHAR *)(p + 1 * 4 + (16 * 2 + 2 * 4) + 5 * 4); 552 break; 553 case IMEID_CHS_VER41: 554 { 555 int offset = (IME_GetId(videodata, 1) >= 0x00000002) ? 8 : 7; 556 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + offset * 4); 557 if (!p) { 558 break; 559 } 560 561 len = *(DWORD *)(p + 7 * 4 + 16 * 2 * 4); 562 s = (WCHAR *)(p + 6 * 4 + 16 * 2 * 1); 563 } break; 564 case IMEID_CHS_VER42: 565 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 1 * 4 + 1 * 4 + 6 * 4); 566 if (!p) { 567 break; 568 } 569 570 len = *(DWORD *)(p + 1 * 4 + (16 * 2 + 2 * 4) + 5 * 4 + 16 * 2); 571 s = (WCHAR *)(p + 1 * 4 + (16 * 2 + 2 * 4) + 5 * 4); 572 break; 573 } 574 if (s) { 575 size_t size = SDL_min((size_t)(len + 1), SDL_arraysize(videodata->ime_readingstring)); 576 SDL_wcslcpy(videodata->ime_readingstring, s, size); 577 } 578 579 videodata->ImmUnlockIMCC(lpimc->hPrivate); 580 videodata->ImmUnlockIMC(himc); 581 } 582 ImmReleaseContext(hwnd, himc); 583 IME_SendEditingEvent(videodata); 584} 585 586static void IME_InputLangChanged(SDL_VideoData *videodata) 587{ 588 UINT lang = PRIMLANG(); 589 IME_UpdateInputLocale(videodata); 590 591 IME_SetupAPI(videodata); 592 if (lang != PRIMLANG()) { 593 IME_ClearComposition(videodata); 594 } 595} 596 597static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex) 598{ 599 static HKL hklprev = 0; 600 static DWORD dwRet[2] = { 0 }; 601 DWORD dwVerSize = 0; 602 DWORD dwVerHandle = 0; 603 LPVOID lpVerBuffer = 0; 604 LPVOID lpVerData = 0; 605 UINT cbVerData = 0; 606 char szTemp[256]; 607 HKL hkl = 0; 608 DWORD dwLang = 0; 609 SDL_assert(uIndex < sizeof(dwRet) / sizeof(dwRet[0])); 610 611 hkl = videodata->ime_hkl; 612 if (hklprev == hkl) { 613 return dwRet[uIndex]; 614 } 615 hklprev = hkl; 616 617 SDL_assert(uIndex == 0); 618 dwLang = ((DWORD_PTR)hkl & 0xffff); 619 // FIXME: What does this do? 620 if (videodata->ime_internal_candidates && dwLang == LANG_CHT) { 621 dwRet[0] = IMEID_CHT_VER_VISTA; 622 dwRet[1] = 0; 623 return dwRet[0]; 624 } 625 if (hkl != CHT_HKL_NEW_PHONETIC && hkl != CHT_HKL_NEW_CHANG_JIE && hkl != CHT_HKL_NEW_QUICK && hkl != CHT_HKL_HK_CANTONESE && hkl != CHS_HKL) { 626 dwRet[0] = dwRet[1] = 0; 627 return dwRet[0]; 628 } 629 if (!ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1)) { 630 dwRet[0] = dwRet[1] = 0; 631 return dwRet[0]; 632 } 633 if (!videodata->GetReadingString) { 634#define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) 635 if (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME1, -1) != 2 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME2, -1) != 2 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME3, -1) != 2 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME1, -1) != 2 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME2, -1) != 2) { 636 dwRet[0] = dwRet[1] = 0; 637 return dwRet[0]; 638 } 639#undef LCID_INVARIANT 640 dwVerSize = GetFileVersionInfoSizeA(szTemp, &dwVerHandle); 641 if (dwVerSize) { 642 lpVerBuffer = SDL_malloc(dwVerSize); 643 if (lpVerBuffer) { 644 if (GetFileVersionInfoA(szTemp, dwVerHandle, dwVerSize, lpVerBuffer)) { 645 if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) { 646#define pVerFixedInfo ((VS_FIXEDFILEINFO FAR *)lpVerData) 647 DWORD dwVer = pVerFixedInfo->dwFileVersionMS; 648 dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16; 649 if ((videodata->GetReadingString) || 650 ((dwLang == LANG_CHT) && (dwVer == MAKEIMEVERSION(4, 2) || 651 dwVer == MAKEIMEVERSION(4, 3) || 652 dwVer == MAKEIMEVERSION(4, 4) || 653 dwVer == MAKEIMEVERSION(5, 0) || 654 dwVer == MAKEIMEVERSION(5, 1) || 655 dwVer == MAKEIMEVERSION(5, 2) || 656 dwVer == MAKEIMEVERSION(6, 0))) || 657 ((dwLang == LANG_CHS) && (dwVer == MAKEIMEVERSION(4, 1) || 658 dwVer == MAKEIMEVERSION(4, 2) || 659 dwVer == MAKEIMEVERSION(5, 3)))) { 660 dwRet[0] = dwVer | dwLang; 661 dwRet[1] = pVerFixedInfo->dwFileVersionLS; 662 SDL_free(lpVerBuffer); 663 return dwRet[0]; 664 } 665#undef pVerFixedInfo 666 } 667 } 668 } 669 SDL_free(lpVerBuffer); 670 } 671 } 672 dwRet[0] = dwRet[1] = 0; 673 return dwRet[0]; 674} 675 676static void IME_SetupAPI(SDL_VideoData *videodata) 677{ 678 char ime_file[MAX_PATH + 1]; 679 SDL_SharedObject *hime = 0; 680 HKL hkl = 0; 681 videodata->GetReadingString = NULL; 682 videodata->ShowReadingWindow = NULL; 683 684 hkl = videodata->ime_hkl; 685 if (!ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1)) { 686 return; 687 } 688 689 hime = SDL_LoadObject(ime_file); 690 if (!hime) { 691 return; 692 } 693 694 /* *INDENT-OFF* */ // clang-format off 695 videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT)) 696 SDL_LoadFunction(hime, "GetReadingString"); 697 videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL)) 698 SDL_LoadFunction(hime, "ShowReadingWindow"); 699 /* *INDENT-ON* */ // clang-format on 700 701 if (videodata->ShowReadingWindow) { 702 HIMC himc = ImmGetContext(videodata->ime_hwnd_current); 703 if (himc) { 704 videodata->ShowReadingWindow(himc, FALSE); 705 ImmReleaseContext(videodata->ime_hwnd_current, himc); 706 } 707 } 708} 709 710static void IME_SetWindow(SDL_VideoData *videodata, SDL_Window *window) 711{ 712 HWND hwnd = window->internal->hwnd; 713 714 if (hwnd != videodata->ime_hwnd_current) { 715 videodata->ime_hwnd_current = hwnd; 716 SDL_zero(videodata->ime_composition_area); 717 SDL_zero(videodata->ime_candidate_area); 718 } 719 720 IME_SetTextInputArea(videodata, hwnd, &window->text_input_rect, window->text_input_cursor); 721} 722 723#endif 724 725static void IME_SetTextInputArea(SDL_VideoData *videodata, HWND hwnd, const SDL_Rect *rect, int cursor) 726{ 727 HIMC himc; 728 729 himc = ImmGetContext(hwnd); 730 if (himc) { 731 COMPOSITIONFORM cof; 732 CANDIDATEFORM caf; 733 int font_height = rect->h; 734 735 LOGFONTW font; 736 if (ImmGetCompositionFontW(himc, &font)) { 737 font_height = font.lfHeight; 738 } 739 740 SDL_zero(cof); 741 cof.dwStyle = CFS_RECT; 742 cof.ptCurrentPos.x = rect->x + cursor; 743 cof.ptCurrentPos.y = rect->y + (rect->h - font_height) / 2; 744 cof.rcArea.left = rect->x; 745 cof.rcArea.right = (LONG)rect->x + rect->w; 746 cof.rcArea.top = rect->y; 747 cof.rcArea.bottom = (LONG)rect->y + rect->h; 748 if (SDL_memcmp(&cof, &videodata->ime_composition_area, sizeof(cof)) != 0) { 749 SDL_copyp(&videodata->ime_composition_area, &cof); 750 ImmSetCompositionWindow(himc, &cof); 751 } 752 753 SDL_zero(caf); 754 caf.dwIndex = 0; 755 caf.dwStyle = CFS_EXCLUDE; 756 caf.ptCurrentPos.x = rect->x + cursor; 757 caf.ptCurrentPos.y = rect->y; 758 caf.rcArea.left = rect->x; 759 caf.rcArea.right = (LONG)rect->x + rect->w; 760 caf.rcArea.top = rect->y; 761 caf.rcArea.bottom = (LONG)rect->y + rect->h; 762 if (SDL_memcmp(&caf, &videodata->ime_candidate_area, sizeof(caf)) != 0) { 763 SDL_copyp(&videodata->ime_candidate_area, &caf); 764 ImmSetCandidateWindow(himc, &caf); 765 } 766 767 ImmReleaseContext(hwnd, himc); 768 } 769} 770 771#ifndef SDL_DISABLE_WINDOWS_IME 772 773static void IME_UpdateInputLocale(SDL_VideoData *videodata) 774{ 775 HKL hklnext = GetKeyboardLayout(0); 776 777 if (hklnext == videodata->ime_hkl) { 778 return; 779 } 780 781 videodata->ime_hkl = hklnext; 782 videodata->ime_horizontal_candidates = (PRIMLANG() == LANG_KOREAN || LANG() == LANG_CHS); 783 videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1; 784} 785 786static void IME_ClearComposition(SDL_VideoData *videodata) 787{ 788 HIMC himc = 0; 789 if (!videodata->ime_initialized) { 790 return; 791 } 792 793 himc = ImmGetContext(videodata->ime_hwnd_current); 794 if (!himc) { 795 return; 796 } 797 798 ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); 799 ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR)); 800 801 ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0); 802 ImmReleaseContext(videodata->ime_hwnd_current, himc); 803 IME_SendClearComposition(videodata); 804} 805 806static void IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string) 807{ 808 LONG length; 809 DWORD dwLang = ((DWORD_PTR)videodata->ime_hkl & 0xffff); 810 811 videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0)); 812 videodata->ime_selected_start = 0; 813 videodata->ime_selected_length = 0; 814 SDL_DebugIMELog("Cursor = %d", videodata->ime_cursor); 815 816 length = ImmGetCompositionStringW(himc, string, NULL, 0); 817 if (length > 0 && videodata->ime_composition_length < length) { 818 SDL_free(videodata->ime_composition); 819 820 videodata->ime_composition = (WCHAR *)SDL_malloc(length + sizeof(WCHAR)); 821 videodata->ime_composition_length = length; 822 } 823 824 length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, videodata->ime_composition_length); 825 if (length < 0) { 826 length = 0; 827 } 828 length /= sizeof(WCHAR); 829 830 if ((dwLang == LANG_CHT || dwLang == LANG_CHS) && 831 videodata->ime_cursor > 0 && 832 videodata->ime_cursor < (int)(videodata->ime_composition_length / sizeof(WCHAR)) && 833 (videodata->ime_composition[0] == 0x3000 || videodata->ime_composition[0] == 0x0020)) { 834 // Traditional Chinese IMEs add a placeholder U+3000 835 // Simplified Chinese IMEs seem to add a placeholder U+0020 sometimes 836 for (int i = videodata->ime_cursor + 1; i < length; ++i) { 837 videodata->ime_composition[i - 1] = videodata->ime_composition[i]; 838 } 839 --length; 840 } 841 842 videodata->ime_composition[length] = 0; 843 844 length = ImmGetCompositionStringW(himc, GCS_COMPATTR, NULL, 0); 845 if (length > 0) { 846 Uint8 *attributes = (Uint8 *)SDL_malloc(length); 847 if (attributes) { 848 int start = 0; 849 int end = 0; 850 851 length = ImmGetCompositionString(himc, GCS_COMPATTR, attributes, length); 852 if (length < 0) { 853 length = 0; 854 } 855 856 for (LONG i = 0; i < length; ++i) { 857 SDL_DebugIMELog("attrib[%d] = %d", i, attributes[i]); 858 } 859 860 for (start = 0; start < length; ++start) { 861 if (attributes[start] == ATTR_TARGET_CONVERTED || attributes[start] == ATTR_TARGET_NOTCONVERTED) { 862 break; 863 } 864 } 865 866 for (end = start; end < length; ++end) { 867 if (attributes[end] != ATTR_TARGET_CONVERTED && attributes[end] != ATTR_TARGET_NOTCONVERTED) { 868 break; 869 } 870 } 871 872 if (end > start) { 873 videodata->ime_selected_start = start; 874 videodata->ime_selected_length = end - start; 875 } 876 877 SDL_free(attributes); 878 } 879 } 880} 881 882static void IME_SendInputEvent(SDL_VideoData *videodata) 883{ 884 char *s = 0; 885 s = WIN_StringToUTF8W(videodata->ime_composition); 886 SDL_SendKeyboardText(s); 887 SDL_free(s); 888 889 videodata->ime_composition[0] = 0; 890 videodata->ime_readingstring[0] = 0; 891 videodata->ime_cursor = 0; 892} 893 894static void IME_SendEditingEvent(SDL_VideoData *videodata) 895{ 896 char *s = NULL; 897 WCHAR *buffer = NULL; 898 size_t size = videodata->ime_composition_length; 899 if (videodata->ime_readingstring[0]) { 900 size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor); 901 902 size += sizeof(videodata->ime_readingstring); 903 buffer = (WCHAR *)SDL_malloc(size + sizeof(WCHAR)); 904 if (!buffer) { 905 return; 906 } 907 buffer[0] = 0; 908 909 SDL_wcslcpy(buffer, videodata->ime_composition, len + 1); 910 SDL_wcslcat(buffer, videodata->ime_readingstring, size); 911 SDL_wcslcat(buffer, &videodata->ime_composition[len], size); 912 } else { 913 buffer = (WCHAR *)SDL_malloc(size + sizeof(WCHAR)); 914 if (!buffer) { 915 return; 916 } 917 buffer[0] = 0; 918 SDL_wcslcpy(buffer, videodata->ime_composition, size); 919 } 920 921 s = WIN_StringToUTF8W(buffer); 922 if (s) { 923 if (videodata->ime_readingstring[0]) { 924 SDL_SendEditingText(s, videodata->ime_cursor, (int)SDL_wcslen(videodata->ime_readingstring)); 925 } else if (videodata->ime_cursor == videodata->ime_selected_start) { 926 SDL_SendEditingText(s, videodata->ime_selected_start, videodata->ime_selected_length); 927 } else { 928 SDL_SendEditingText(s, videodata->ime_cursor, 0); 929 } 930 if (*s) { 931 videodata->ime_needs_clear_composition = true; 932 } 933 SDL_free(s); 934 } 935 SDL_free(buffer); 936} 937 938static void IME_SendClearComposition(SDL_VideoData *videodata) 939{ 940 if (videodata->ime_needs_clear_composition) { 941 SDL_SendEditingText("", 0, 0); 942 videodata->ime_needs_clear_composition = false; 943 } 944} 945 946static bool IME_OpenCandidateList(SDL_VideoData *videodata) 947{ 948 videodata->ime_candidates_open = true; 949 videodata->ime_candcount = 0; 950 return true; 951} 952 953static void IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate) 954{ 955 if (videodata->ime_candidates[i]) { 956 SDL_free(videodata->ime_candidates[i]); 957 videodata->ime_candidates[i] = NULL; 958 } 959 960 SDL_COMPILE_TIME_ASSERT(IME_CANDIDATE_INDEXING_REQUIRES, MAX_CANDLIST == 10); 961 char *candidate_utf8 = WIN_StringToUTF8W(candidate); 962 SDL_asprintf(&videodata->ime_candidates[i], "%d %s", ((i + videodata->ime_candlistindexbase) % 10), candidate_utf8); 963 SDL_free(candidate_utf8); 964 965 videodata->ime_candcount = (i + 1); 966} 967 968static void IME_SendCandidateList(SDL_VideoData *videodata) 969{ 970 SDL_SendEditingTextCandidates(videodata->ime_candidates, videodata->ime_candcount, videodata->ime_candsel, videodata->ime_horizontal_candidates); 971} 972 973static void IME_CloseCandidateList(SDL_VideoData *videodata) 974{ 975 videodata->ime_candidates_open = false; 976 977 if (videodata->ime_candcount > 0) { 978 for (int i = 0; i < videodata->ime_candcount; ++i) { 979 SDL_free(videodata->ime_candidates[i]); 980 videodata->ime_candidates[i] = NULL; 981 } 982 videodata->ime_candcount = 0; 983 984 SDL_SendEditingTextCandidates(NULL, 0, -1, false); 985 } 986} 987 988static void IME_GetCandidateList(SDL_VideoData *videodata, HWND hwnd) 989{ 990 HIMC himc; 991 DWORD size; 992 LPCANDIDATELIST cand_list; 993 bool has_candidates = false; 994 995 himc = ImmGetContext(hwnd); 996 if (himc) { 997 size = ImmGetCandidateListW(himc, 0, NULL, 0); 998 if (size != 0) { 999 cand_list = (LPCANDIDATELIST)SDL_malloc(size); 1000 if (cand_list != NULL) { 1001 size = ImmGetCandidateListW(himc, 0, cand_list, size); 1002 if (size != 0) { 1003 if (IME_OpenCandidateList(videodata)) { 1004 UINT i, j; 1005 UINT page_start = 0; 1006 UINT page_size = 0; 1007 1008 videodata->ime_candsel = cand_list->dwSelection; 1009 1010 if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) { 1011 const UINT maxcandchar = 18; 1012 size_t cchars = 0; 1013 1014 for (i = 0; i < cand_list->dwCount; ++i) { 1015 size_t len = SDL_wcslen((LPWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i])) + 1; 1016 if (len + cchars > maxcandchar) { 1017 if (i > cand_list->dwSelection) { 1018 break; 1019 } 1020 1021 page_start = i; 1022 cchars = len; 1023 } else { 1024 cchars += len; 1025 } 1026 } 1027 page_size = i - page_start; 1028 } else { 1029 page_size = SDL_min(cand_list->dwPageSize == 0 ? MAX_CANDLIST : cand_list->dwPageSize, MAX_CANDLIST); 1030 page_start = (cand_list->dwSelection / page_size) * page_size; 1031 } 1032 for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < page_size; i++, j++) { 1033 LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]); 1034 IME_AddCandidate(videodata, j, candidate); 1035 } 1036 1037 has_candidates = true; 1038 IME_SendCandidateList(videodata); 1039 } 1040 } 1041 SDL_free(cand_list); 1042 } 1043 } 1044 ImmReleaseContext(hwnd, himc); 1045 } 1046 1047 if (!has_candidates) { 1048 IME_CloseCandidateList(videodata); 1049 } 1050} 1051 1052bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata) 1053{ 1054 bool trap = false; 1055 HIMC himc = 0; 1056 1057 if (msg == WM_IME_SETCONTEXT) { 1058 SDL_DebugIMELog("WM_IME_SETCONTEXT"); 1059 1060 LPARAM element_mask; 1061 if (videodata->ime_internal_composition && videodata->ime_internal_candidates) { 1062 element_mask = 0; 1063 } else { 1064 element_mask = ISC_SHOWUIALL; 1065 if (videodata->ime_internal_composition) { 1066 element_mask &= ~ISC_SHOWUICOMPOSITIONWINDOW; 1067 } 1068 if (videodata->ime_internal_candidates) { 1069 element_mask &= ~ISC_SHOWUIALLCANDIDATEWINDOW; 1070 } 1071 } 1072 *lParam &= element_mask; 1073 1074 return false; 1075 } 1076 1077 if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled) { 1078 return false; 1079 } 1080 1081 switch (msg) { 1082 case WM_KEYDOWN: 1083 if (wParam == VK_PROCESSKEY) { 1084 SDL_DebugIMELog("WM_KEYDOWN VK_PROCESSKEY"); 1085 trap = true; 1086 } else { 1087 SDL_DebugIMELog("WM_KEYDOWN normal"); 1088 } 1089 break; 1090 case WM_SYSKEYDOWN: 1091 if (wParam == VK_PROCESSKEY) { 1092 SDL_DebugIMELog("WM_SYSKEYDOWN VK_PROCESSKEY"); 1093 trap = true; 1094 } else { 1095 SDL_DebugIMELog("WM_SYSKEYDOWN normal"); 1096 } 1097 break; 1098 case WM_INPUTLANGCHANGE: 1099 SDL_DebugIMELog("WM_INPUTLANGCHANGE"); 1100 IME_InputLangChanged(videodata); 1101 break; 1102 case WM_IME_STARTCOMPOSITION: 1103 SDL_DebugIMELog("WM_IME_STARTCOMPOSITION"); 1104 if (videodata->ime_internal_composition) { 1105 trap = true; 1106 } 1107 break; 1108 case WM_IME_COMPOSITION: 1109 SDL_DebugIMELog("WM_IME_COMPOSITION %x", lParam); 1110 if (videodata->ime_internal_composition) { 1111 trap = true; 1112 himc = ImmGetContext(hwnd); 1113 if (*lParam & GCS_RESULTSTR) { 1114 SDL_DebugIMELog("GCS_RESULTSTR"); 1115 IME_GetCompositionString(videodata, himc, GCS_RESULTSTR); 1116 IME_SendClearComposition(videodata); 1117 IME_SendInputEvent(videodata); 1118 } 1119 if (*lParam & GCS_COMPSTR) { 1120 SDL_DebugIMELog("GCS_COMPSTR"); 1121 videodata->ime_readingstring[0] = 0; 1122 IME_GetCompositionString(videodata, himc, GCS_COMPSTR); 1123 IME_SendEditingEvent(videodata); 1124 } 1125 ImmReleaseContext(hwnd, himc); 1126 } 1127 break; 1128 case WM_IME_ENDCOMPOSITION: 1129 SDL_DebugIMELog("WM_IME_ENDCOMPOSITION"); 1130 if (videodata->ime_internal_composition) { 1131 trap = true; 1132 videodata->ime_composition[0] = 0; 1133 videodata->ime_readingstring[0] = 0; 1134 videodata->ime_cursor = 0; 1135 videodata->ime_selected_start = 0; 1136 videodata->ime_selected_length = 0; 1137 IME_SendClearComposition(videodata); 1138 } 1139 break; 1140 case WM_IME_NOTIFY: 1141 SDL_DebugIMELog("WM_IME_NOTIFY %x", wParam); 1142 switch (wParam) { 1143 case IMN_SETCOMPOSITIONWINDOW: 1144 SDL_DebugIMELog("IMN_SETCOMPOSITIONWINDOW"); 1145 break; 1146 case IMN_SETCOMPOSITIONFONT: 1147 SDL_DebugIMELog("IMN_SETCOMPOSITIONFONT"); 1148 break; 1149 case IMN_SETCANDIDATEPOS: 1150 SDL_DebugIMELog("IMN_SETCANDIDATEPOS"); 1151 break; 1152 case IMN_SETCONVERSIONMODE: 1153 case IMN_SETOPENSTATUS: 1154 SDL_DebugIMELog("%s", wParam == IMN_SETCONVERSIONMODE ? "IMN_SETCONVERSIONMODE" : "IMN_SETOPENSTATUS"); 1155 IME_UpdateInputLocale(videodata); 1156 break; 1157 case IMN_OPENCANDIDATE: 1158 case IMN_CHANGECANDIDATE: 1159 SDL_DebugIMELog("%s", wParam == IMN_OPENCANDIDATE ? "IMN_OPENCANDIDATE" : "IMN_CHANGECANDIDATE"); 1160 if (videodata->ime_internal_candidates) { 1161 trap = true; 1162 videodata->ime_update_candidates = true; 1163 } 1164 break; 1165 case IMN_CLOSECANDIDATE: 1166 SDL_DebugIMELog("IMN_CLOSECANDIDATE"); 1167 if (videodata->ime_internal_candidates) { 1168 trap = true; 1169 videodata->ime_update_candidates = false; 1170 IME_CloseCandidateList(videodata); 1171 } 1172 break; 1173 case IMN_PRIVATE: 1174 { 1175 DWORD dwId = IME_GetId(videodata, 0); 1176 SDL_DebugIMELog("IMN_PRIVATE %u", dwId); 1177 IME_GetReadingString(videodata, hwnd); 1178 switch (dwId) { 1179 case IMEID_CHT_VER42: 1180 case IMEID_CHT_VER43: 1181 case IMEID_CHT_VER44: 1182 case IMEID_CHS_VER41: 1183 case IMEID_CHS_VER42: 1184 if (*lParam == 1 || *lParam == 2) { 1185 trap = true; 1186 } 1187 1188 break; 1189 case IMEID_CHT_VER50: 1190 case IMEID_CHT_VER51: 1191 case IMEID_CHT_VER52: 1192 case IMEID_CHT_VER60: 1193 case IMEID_CHS_VER53: 1194 if (*lParam == 16 || *lParam == 17 || *lParam == 26 || *lParam == 27 || *lParam == 28) { 1195 trap = true; 1196 } 1197 break; 1198 } 1199 } break; 1200 default: 1201 trap = true; 1202 break; 1203 } 1204 break; 1205 } 1206 return trap; 1207} 1208 1209void WIN_UpdateIMECandidates(SDL_VideoDevice *_this) 1210{ 1211 SDL_VideoData *videodata = _this->internal; 1212 1213 if (videodata->ime_update_candidates) { 1214 IME_GetCandidateList(videodata, videodata->ime_hwnd_current); 1215 videodata->ime_update_candidates = false; 1216 } 1217} 1218 1219#endif // SDL_DISABLE_WINDOWS_IME 1220 1221#endif // SDL_VIDEO_DRIVER_WINDOWS 1222[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.