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