Atlas - SDL_windowskeyboard.c

Home / ext / SDL / src / video / windows Lines: 1 | Size: 41687 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 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(void) 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_zeroa(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 LPVOID lpVerBuffer = 0; 603 LPVOID lpVerData = 0; 604 UINT cbVerData = 0; 605 char szTemp[256]; 606 HKL hkl = 0; 607 DWORD dwLang = 0; 608 SDL_assert(uIndex < SDL_arraysize(dwRet)); 609 610 hkl = videodata->ime_hkl; 611 if (hklprev == hkl) { 612 return dwRet[uIndex]; 613 } 614 hklprev = hkl; 615 616 SDL_assert(uIndex == 0); 617 dwLang = ((DWORD_PTR)hkl & 0xffff); 618 // FIXME: What does this do? 619 if (videodata->ime_internal_candidates && dwLang == LANG_CHT) { 620 dwRet[0] = IMEID_CHT_VER_VISTA; 621 dwRet[1] = 0; 622 return dwRet[0]; 623 } 624 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) { 625 dwRet[0] = dwRet[1] = 0; 626 return dwRet[0]; 627 } 628 if (!ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1)) { 629 dwRet[0] = dwRet[1] = 0; 630 return dwRet[0]; 631 } 632 if (!videodata->GetReadingString) { 633#define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) 634 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) { 635 dwRet[0] = dwRet[1] = 0; 636 return dwRet[0]; 637 } 638#undef LCID_INVARIANT 639 dwVerSize = GetFileVersionInfoSizeA(szTemp, NULL); 640 if (dwVerSize) { 641 lpVerBuffer = SDL_malloc(dwVerSize); 642 if (lpVerBuffer) { 643 if (GetFileVersionInfoA(szTemp, 0, dwVerSize, lpVerBuffer)) { 644 if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) { 645#define pVerFixedInfo ((VS_FIXEDFILEINFO FAR *)lpVerData) 646 DWORD dwVer = pVerFixedInfo->dwFileVersionMS; 647 dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16; 648 if ((videodata->GetReadingString) || 649 ((dwLang == LANG_CHT) && (dwVer == MAKEIMEVERSION(4, 2) || 650 dwVer == MAKEIMEVERSION(4, 3) || 651 dwVer == MAKEIMEVERSION(4, 4) || 652 dwVer == MAKEIMEVERSION(5, 0) || 653 dwVer == MAKEIMEVERSION(5, 1) || 654 dwVer == MAKEIMEVERSION(5, 2) || 655 dwVer == MAKEIMEVERSION(6, 0))) || 656 ((dwLang == LANG_CHS) && (dwVer == MAKEIMEVERSION(4, 1) || 657 dwVer == MAKEIMEVERSION(4, 2) || 658 dwVer == MAKEIMEVERSION(5, 3)))) { 659 dwRet[0] = dwVer | dwLang; 660 dwRet[1] = pVerFixedInfo->dwFileVersionLS; 661 SDL_free(lpVerBuffer); 662 return dwRet[0]; 663 } 664#undef pVerFixedInfo 665 } 666 } 667 } 668 SDL_free(lpVerBuffer); 669 } 670 } 671 dwRet[0] = dwRet[1] = 0; 672 return dwRet[0]; 673} 674 675static void IME_SetupAPI(SDL_VideoData *videodata) 676{ 677 char ime_file[MAX_PATH + 1]; 678 SDL_SharedObject *hime = 0; 679 HKL hkl = 0; 680 videodata->GetReadingString = NULL; 681 videodata->ShowReadingWindow = NULL; 682 683 hkl = videodata->ime_hkl; 684 if (!ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1)) { 685 return; 686 } 687 688 hime = SDL_LoadObject(ime_file); 689 if (!hime) { 690 return; 691 } 692 693 /* *INDENT-OFF* */ // clang-format off 694 videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT)) 695 SDL_LoadFunction(hime, "GetReadingString"); 696 videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL)) 697 SDL_LoadFunction(hime, "ShowReadingWindow"); 698 /* *INDENT-ON* */ // clang-format on 699 700 if (videodata->ShowReadingWindow) { 701 HIMC himc = ImmGetContext(videodata->ime_hwnd_current); 702 if (himc) { 703 videodata->ShowReadingWindow(himc, FALSE); 704 ImmReleaseContext(videodata->ime_hwnd_current, himc); 705 } 706 } 707} 708 709static void IME_SetWindow(SDL_VideoData *videodata, SDL_Window *window) 710{ 711 HWND hwnd = window->internal->hwnd; 712 713 if (hwnd != videodata->ime_hwnd_current) { 714 videodata->ime_hwnd_current = hwnd; 715 SDL_zero(videodata->ime_composition_area); 716 SDL_zero(videodata->ime_candidate_area); 717 } 718 719 IME_SetTextInputArea(videodata, hwnd, &window->text_input_rect, window->text_input_cursor); 720} 721 722#endif 723 724static void IME_SetTextInputArea(SDL_VideoData *videodata, HWND hwnd, const SDL_Rect *rect, int cursor) 725{ 726 HIMC himc; 727 728 himc = ImmGetContext(hwnd); 729 if (himc) { 730 COMPOSITIONFORM cof; 731 CANDIDATEFORM caf; 732 int font_height = rect->h; 733 734 LOGFONTW font; 735 if (ImmGetCompositionFontW(himc, &font)) { 736 font_height = font.lfHeight; 737 } 738 739 SDL_zero(cof); 740 cof.dwStyle = CFS_RECT; 741 cof.ptCurrentPos.x = rect->x + cursor; 742 cof.ptCurrentPos.y = rect->y + (rect->h - font_height) / 2; 743 cof.rcArea.left = rect->x; 744 cof.rcArea.right = (LONG)rect->x + rect->w; 745 cof.rcArea.top = rect->y; 746 cof.rcArea.bottom = (LONG)rect->y + rect->h; 747 if (SDL_memcmp(&cof, &videodata->ime_composition_area, sizeof(cof)) != 0) { 748 SDL_copyp(&videodata->ime_composition_area, &cof); 749 ImmSetCompositionWindow(himc, &cof); 750 } 751 752 SDL_zero(caf); 753 caf.dwIndex = 0; 754 caf.dwStyle = CFS_EXCLUDE; 755 caf.ptCurrentPos.x = rect->x + cursor; 756 caf.ptCurrentPos.y = rect->y; 757 caf.rcArea.left = rect->x; 758 caf.rcArea.right = (LONG)rect->x + rect->w; 759 caf.rcArea.top = rect->y; 760 caf.rcArea.bottom = (LONG)rect->y + rect->h; 761 if (SDL_memcmp(&caf, &videodata->ime_candidate_area, sizeof(caf)) != 0) { 762 SDL_copyp(&videodata->ime_candidate_area, &caf); 763 ImmSetCandidateWindow(himc, &caf); 764 } 765 766 ImmReleaseContext(hwnd, himc); 767 } 768} 769 770#ifndef SDL_DISABLE_WINDOWS_IME 771 772static void IME_UpdateInputLocale(SDL_VideoData *videodata) 773{ 774 HKL hklnext = GetKeyboardLayout(0); 775 776 if (hklnext == videodata->ime_hkl) { 777 return; 778 } 779 780 videodata->ime_hkl = hklnext; 781 videodata->ime_horizontal_candidates = (PRIMLANG() == LANG_KOREAN || LANG() == LANG_CHS); 782 videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1; 783} 784 785static void IME_ClearComposition(SDL_VideoData *videodata) 786{ 787 HIMC himc = 0; 788 if (!videodata->ime_initialized) { 789 return; 790 } 791 792 himc = ImmGetContext(videodata->ime_hwnd_current); 793 if (!himc) { 794 return; 795 } 796 797 ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); 798 ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR)); 799 800 ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0); 801 ImmReleaseContext(videodata->ime_hwnd_current, himc); 802 IME_SendClearComposition(videodata); 803} 804 805static void IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string) 806{ 807 LONG length; 808 DWORD dwLang = ((DWORD_PTR)videodata->ime_hkl & 0xffff); 809 810 videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0)); 811 videodata->ime_selected_start = 0; 812 videodata->ime_selected_length = 0; 813 SDL_DebugIMELog("Cursor = %d", videodata->ime_cursor); 814 815 length = ImmGetCompositionStringW(himc, string, NULL, 0); 816 if (length > 0 && videodata->ime_composition_length < length) { 817 SDL_free(videodata->ime_composition); 818 819 videodata->ime_composition = (WCHAR *)SDL_malloc(length + sizeof(WCHAR)); 820 videodata->ime_composition_length = length; 821 } 822 823 length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, videodata->ime_composition_length); 824 if (length < 0) { 825 length = 0; 826 } 827 length /= sizeof(WCHAR); 828 829 if ((dwLang == LANG_CHT || dwLang == LANG_CHS) && 830 videodata->ime_cursor > 0 && 831 videodata->ime_cursor < (int)(videodata->ime_composition_length / sizeof(WCHAR)) && 832 (videodata->ime_composition[0] == 0x3000 || videodata->ime_composition[0] == 0x0020)) { 833 // Traditional Chinese IMEs add a placeholder U+3000 834 // Simplified Chinese IMEs seem to add a placeholder U+0020 sometimes 835 for (int i = videodata->ime_cursor + 1; i < length; ++i) { 836 videodata->ime_composition[i - 1] = videodata->ime_composition[i]; 837 } 838 --length; 839 } 840 841 videodata->ime_composition[length] = 0; 842 843 length = ImmGetCompositionStringW(himc, GCS_COMPATTR, NULL, 0); 844 if (length > 0) { 845 Uint8 *attributes = (Uint8 *)SDL_malloc(length); 846 if (attributes) { 847 int start = 0; 848 int end = 0; 849 850 length = ImmGetCompositionString(himc, GCS_COMPATTR, attributes, length); 851 if (length < 0) { 852 length = 0; 853 } 854 855 for (LONG i = 0; i < length; ++i) { 856 SDL_DebugIMELog("attrib[%d] = %d", i, attributes[i]); 857 } 858 859 for (start = 0; start < length; ++start) { 860 if (attributes[start] == ATTR_TARGET_CONVERTED || attributes[start] == ATTR_TARGET_NOTCONVERTED) { 861 break; 862 } 863 } 864 865 for (end = start; end < length; ++end) { 866 if (attributes[end] != ATTR_TARGET_CONVERTED && attributes[end] != ATTR_TARGET_NOTCONVERTED) { 867 break; 868 } 869 } 870 871 if (end > start) { 872 videodata->ime_selected_start = start; 873 videodata->ime_selected_length = end - start; 874 } 875 876 SDL_free(attributes); 877 } 878 } 879} 880 881static void IME_SendInputEvent(SDL_VideoData *videodata) 882{ 883 char *s = 0; 884 s = WIN_StringToUTF8W(videodata->ime_composition); 885 SDL_SendKeyboardText(s); 886 SDL_free(s); 887 888 videodata->ime_composition[0] = 0; 889 videodata->ime_readingstring[0] = 0; 890 videodata->ime_cursor = 0; 891} 892 893static void IME_SendEditingEvent(SDL_VideoData *videodata) 894{ 895 char *s = NULL; 896 WCHAR *buffer = NULL; 897 size_t size = videodata->ime_composition_length; 898 if (videodata->ime_readingstring[0]) { 899 size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor); 900 901 size += sizeof(videodata->ime_readingstring); 902 buffer = (WCHAR *)SDL_malloc(size + sizeof(WCHAR)); 903 if (!buffer) { 904 return; 905 } 906 buffer[0] = 0; 907 908 SDL_wcslcpy(buffer, videodata->ime_composition, len + 1); 909 SDL_wcslcat(buffer, videodata->ime_readingstring, size); 910 SDL_wcslcat(buffer, &videodata->ime_composition[len], size); 911 } else { 912 buffer = (WCHAR *)SDL_malloc(size + sizeof(WCHAR)); 913 if (!buffer) { 914 return; 915 } 916 buffer[0] = 0; 917 SDL_wcslcpy(buffer, videodata->ime_composition, size); 918 } 919 920 s = WIN_StringToUTF8W(buffer); 921 if (s) { 922 if (videodata->ime_readingstring[0]) { 923 SDL_SendEditingText(s, videodata->ime_cursor, (int)SDL_wcslen(videodata->ime_readingstring)); 924 } else if (videodata->ime_cursor == videodata->ime_selected_start) { 925 SDL_SendEditingText(s, videodata->ime_selected_start, videodata->ime_selected_length); 926 } else { 927 SDL_SendEditingText(s, videodata->ime_cursor, 0); 928 } 929 if (*s) { 930 videodata->ime_needs_clear_composition = true; 931 } 932 SDL_free(s); 933 } 934 SDL_free(buffer); 935} 936 937static void IME_SendClearComposition(SDL_VideoData *videodata) 938{ 939 if (videodata->ime_needs_clear_composition) { 940 SDL_SendEditingText("", 0, 0); 941 videodata->ime_needs_clear_composition = false; 942 } 943} 944 945static bool IME_OpenCandidateList(SDL_VideoData *videodata) 946{ 947 videodata->ime_candidates_open = true; 948 videodata->ime_candcount = 0; 949 return true; 950} 951 952static void IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate) 953{ 954 if (videodata->ime_candidates[i]) { 955 SDL_free(videodata->ime_candidates[i]); 956 videodata->ime_candidates[i] = NULL; 957 } 958 959 SDL_COMPILE_TIME_ASSERT(IME_CANDIDATE_INDEXING_REQUIRES, MAX_CANDLIST == 10); 960 char *candidate_utf8 = WIN_StringToUTF8W(candidate); 961 SDL_asprintf(&videodata->ime_candidates[i], "%d %s", ((i + videodata->ime_candlistindexbase) % 10), candidate_utf8); 962 SDL_free(candidate_utf8); 963 964 videodata->ime_candcount = (i + 1); 965} 966 967static void IME_SendCandidateList(SDL_VideoData *videodata) 968{ 969 SDL_SendEditingTextCandidates(videodata->ime_candidates, videodata->ime_candcount, videodata->ime_candsel, videodata->ime_horizontal_candidates); 970} 971 972static void IME_CloseCandidateList(SDL_VideoData *videodata) 973{ 974 videodata->ime_candidates_open = false; 975 976 if (videodata->ime_candcount > 0) { 977 for (int i = 0; i < videodata->ime_candcount; ++i) { 978 SDL_free(videodata->ime_candidates[i]); 979 videodata->ime_candidates[i] = NULL; 980 } 981 videodata->ime_candcount = 0; 982 983 SDL_SendEditingTextCandidates(NULL, 0, -1, false); 984 } 985} 986 987static void IME_GetCandidateList(SDL_VideoData *videodata, HWND hwnd) 988{ 989 HIMC himc; 990 DWORD size; 991 LPCANDIDATELIST cand_list; 992 bool has_candidates = false; 993 994 himc = ImmGetContext(hwnd); 995 if (himc) { 996 size = ImmGetCandidateListW(himc, 0, NULL, 0); 997 if (size != 0) { 998 cand_list = (LPCANDIDATELIST)SDL_malloc(size); 999 if (cand_list != NULL) { 1000 size = ImmGetCandidateListW(himc, 0, cand_list, size); 1001 if (size != 0) { 1002 if (IME_OpenCandidateList(videodata)) { 1003 UINT i, j; 1004 UINT page_start = 0; 1005 UINT page_size = 0; 1006 1007 videodata->ime_candsel = cand_list->dwSelection; 1008 1009 if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) { 1010 const UINT maxcandchar = 18; 1011 size_t cchars = 0; 1012 1013 for (i = 0; i < cand_list->dwCount; ++i) { 1014 size_t len = SDL_wcslen((LPWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i])) + 1; 1015 if (len + cchars > maxcandchar) { 1016 if (i > cand_list->dwSelection) { 1017 break; 1018 } 1019 1020 page_start = i; 1021 cchars = len; 1022 } else { 1023 cchars += len; 1024 } 1025 } 1026 page_size = i - page_start; 1027 } else { 1028 page_size = SDL_min(cand_list->dwPageSize == 0 ? MAX_CANDLIST : cand_list->dwPageSize, MAX_CANDLIST); 1029 page_start = (cand_list->dwSelection / page_size) * page_size; 1030 } 1031 for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < page_size; i++, j++) { 1032 LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]); 1033 IME_AddCandidate(videodata, j, candidate); 1034 } 1035 1036 has_candidates = true; 1037 IME_SendCandidateList(videodata); 1038 } 1039 } 1040 SDL_free(cand_list); 1041 } 1042 } 1043 ImmReleaseContext(hwnd, himc); 1044 } 1045 1046 if (!has_candidates) { 1047 IME_CloseCandidateList(videodata); 1048 } 1049} 1050 1051bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata) 1052{ 1053 bool trap = false; 1054 HIMC himc = 0; 1055 1056 if (msg == WM_IME_SETCONTEXT) { 1057 SDL_DebugIMELog("WM_IME_SETCONTEXT"); 1058 1059 LPARAM element_mask; 1060 if (videodata->ime_internal_composition && videodata->ime_internal_candidates) { 1061 element_mask = 0; 1062 } else { 1063 element_mask = ISC_SHOWUIALL; 1064 if (videodata->ime_internal_composition) { 1065 element_mask &= ~ISC_SHOWUICOMPOSITIONWINDOW; 1066 } 1067 if (videodata->ime_internal_candidates) { 1068 element_mask &= ~ISC_SHOWUIALLCANDIDATEWINDOW; 1069 } 1070 } 1071 *lParam &= element_mask; 1072 1073 return false; 1074 } else if (msg == WM_IME_STARTCOMPOSITION) { 1075 SDL_DebugIMELog("WM_IME_STARTCOMPOSITION"); 1076 if (videodata->ime_internal_composition) { 1077 // Windows may still display a composition dialog even with 1078 // ISC_SHOWUICOMPOSITIONWINDOW cleared, so trap the message 1079 // here to prevent that (even when the IME is disabled). 1080 return true; 1081 } 1082 } 1083 1084 if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled) { 1085 return false; 1086 } 1087 1088 switch (msg) { 1089 case WM_KEYDOWN: 1090 if (wParam == VK_PROCESSKEY) { 1091 SDL_DebugIMELog("WM_KEYDOWN VK_PROCESSKEY"); 1092 trap = true; 1093 } else { 1094 SDL_DebugIMELog("WM_KEYDOWN normal"); 1095 } 1096 break; 1097 case WM_SYSKEYDOWN: 1098 if (wParam == VK_PROCESSKEY) { 1099 SDL_DebugIMELog("WM_SYSKEYDOWN VK_PROCESSKEY"); 1100 trap = true; 1101 } else { 1102 SDL_DebugIMELog("WM_SYSKEYDOWN normal"); 1103 } 1104 break; 1105 case WM_INPUTLANGCHANGE: 1106 SDL_DebugIMELog("WM_INPUTLANGCHANGE"); 1107 IME_InputLangChanged(videodata); 1108 break; 1109 case WM_IME_COMPOSITION: 1110 SDL_DebugIMELog("WM_IME_COMPOSITION %x", lParam); 1111 if (videodata->ime_internal_composition) { 1112 trap = true; 1113 himc = ImmGetContext(hwnd); 1114 if (*lParam & GCS_RESULTSTR) { 1115 SDL_DebugIMELog("GCS_RESULTSTR"); 1116 IME_GetCompositionString(videodata, himc, GCS_RESULTSTR); 1117 IME_SendClearComposition(videodata); 1118 IME_SendInputEvent(videodata); 1119 } 1120 if (*lParam & GCS_COMPSTR) { 1121 SDL_DebugIMELog("GCS_COMPSTR"); 1122 videodata->ime_readingstring[0] = 0; 1123 IME_GetCompositionString(videodata, himc, GCS_COMPSTR); 1124 IME_SendEditingEvent(videodata); 1125 } 1126 ImmReleaseContext(hwnd, himc); 1127 } 1128 break; 1129 case WM_IME_ENDCOMPOSITION: 1130 SDL_DebugIMELog("WM_IME_ENDCOMPOSITION"); 1131 if (videodata->ime_internal_composition) { 1132 trap = true; 1133 videodata->ime_composition[0] = 0; 1134 videodata->ime_readingstring[0] = 0; 1135 videodata->ime_cursor = 0; 1136 videodata->ime_selected_start = 0; 1137 videodata->ime_selected_length = 0; 1138 IME_SendClearComposition(videodata); 1139 } 1140 break; 1141 case WM_IME_NOTIFY: 1142 SDL_DebugIMELog("WM_IME_NOTIFY %x", wParam); 1143 switch (wParam) { 1144 case IMN_SETCOMPOSITIONWINDOW: 1145 SDL_DebugIMELog("IMN_SETCOMPOSITIONWINDOW"); 1146 break; 1147 case IMN_SETCOMPOSITIONFONT: 1148 SDL_DebugIMELog("IMN_SETCOMPOSITIONFONT"); 1149 break; 1150 case IMN_SETCANDIDATEPOS: 1151 SDL_DebugIMELog("IMN_SETCANDIDATEPOS"); 1152 break; 1153 case IMN_SETCONVERSIONMODE: 1154 case IMN_SETOPENSTATUS: 1155 SDL_DebugIMELog("%s", wParam == IMN_SETCONVERSIONMODE ? "IMN_SETCONVERSIONMODE" : "IMN_SETOPENSTATUS"); 1156 IME_UpdateInputLocale(videodata); 1157 break; 1158 case IMN_OPENCANDIDATE: 1159 case IMN_CHANGECANDIDATE: 1160 SDL_DebugIMELog("%s", wParam == IMN_OPENCANDIDATE ? "IMN_OPENCANDIDATE" : "IMN_CHANGECANDIDATE"); 1161 if (videodata->ime_internal_candidates) { 1162 trap = true; 1163 videodata->ime_update_candidates = true; 1164 } 1165 break; 1166 case IMN_CLOSECANDIDATE: 1167 SDL_DebugIMELog("IMN_CLOSECANDIDATE"); 1168 if (videodata->ime_internal_candidates) { 1169 trap = true; 1170 videodata->ime_update_candidates = false; 1171 IME_CloseCandidateList(videodata); 1172 } 1173 break; 1174 case IMN_PRIVATE: 1175 { 1176 DWORD dwId = IME_GetId(videodata, 0); 1177 SDL_DebugIMELog("IMN_PRIVATE %u", dwId); 1178 IME_GetReadingString(videodata, hwnd); 1179 switch (dwId) { 1180 case IMEID_CHT_VER42: 1181 case IMEID_CHT_VER43: 1182 case IMEID_CHT_VER44: 1183 case IMEID_CHS_VER41: 1184 case IMEID_CHS_VER42: 1185 if (*lParam == 1 || *lParam == 2) { 1186 trap = true; 1187 } 1188 1189 break; 1190 case IMEID_CHT_VER50: 1191 case IMEID_CHT_VER51: 1192 case IMEID_CHT_VER52: 1193 case IMEID_CHT_VER60: 1194 case IMEID_CHS_VER53: 1195 if (*lParam == 16 || *lParam == 17 || *lParam == 26 || *lParam == 27 || *lParam == 28) { 1196 trap = true; 1197 } 1198 break; 1199 } 1200 } break; 1201 default: 1202 trap = true; 1203 break; 1204 } 1205 break; 1206 } 1207 return trap; 1208} 1209 1210void WIN_UpdateIMECandidates(SDL_VideoDevice *_this) 1211{ 1212 SDL_VideoData *videodata = _this->internal; 1213 1214 if (videodata->ime_update_candidates) { 1215 IME_GetCandidateList(videodata, videodata->ime_hwnd_current); 1216 videodata->ime_update_candidates = false; 1217 } 1218} 1219 1220#endif // SDL_DISABLE_WINDOWS_IME 1221 1222#endif // SDL_VIDEO_DRIVER_WINDOWS 1223
[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.