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