1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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)0xE0060404)
299 #define CHT_HKL_NEW_PHONETIC ((HKL)0xE0080404)
300 #define CHT_HKL_NEW_CHANG_JIE ((HKL)0xE0090404)
301 #define CHT_HKL_NEW_QUICK ((HKL)0xE00A0404)
302 #define CHT_HKL_HK_CANTONESE ((HKL)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)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 #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
335 #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
336
337 static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata);
338 static void UILess_ReleaseSinks(SDL_VideoData *videodata);
339 static void UILess_EnableUIUpdates(SDL_VideoData *videodata);
340 static void UILess_DisableUIUpdates(SDL_VideoData *videodata);
341
342 static void
IME_Init(SDL_VideoData * videodata,HWND hwnd)343 IME_Init(SDL_VideoData *videodata, HWND hwnd)
344 {
345 if (videodata->ime_initialized)
346 return;
347
348 videodata->ime_hwnd_main = hwnd;
349 if (SUCCEEDED(WIN_CoInitialize())) {
350 videodata->ime_com_initialized = SDL_TRUE;
351 CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (LPVOID *)&videodata->ime_threadmgr);
352 }
353 videodata->ime_initialized = SDL_TRUE;
354 videodata->ime_himm32 = SDL_LoadObject("imm32.dll");
355 if (!videodata->ime_himm32) {
356 videodata->ime_available = SDL_FALSE;
357 return;
358 }
359 videodata->ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMC");
360 videodata->ImmUnlockIMC = (BOOL (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMC");
361 videodata->ImmLockIMCC = (LPVOID (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMCC");
362 videodata->ImmUnlockIMCC = (BOOL (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMCC");
363
364 IME_SetWindow(videodata, hwnd);
365 videodata->ime_himc = ImmGetContext(hwnd);
366 ImmReleaseContext(hwnd, videodata->ime_himc);
367 if (!videodata->ime_himc) {
368 videodata->ime_available = SDL_FALSE;
369 IME_Disable(videodata, hwnd);
370 return;
371 }
372 videodata->ime_available = SDL_TRUE;
373 IME_UpdateInputLocale(videodata);
374 IME_SetupAPI(videodata);
375 videodata->ime_uiless = UILess_SetupSinks(videodata);
376 IME_UpdateInputLocale(videodata);
377 IME_Disable(videodata, hwnd);
378 }
379
380 static void
IME_Enable(SDL_VideoData * videodata,HWND hwnd)381 IME_Enable(SDL_VideoData *videodata, HWND hwnd)
382 {
383 if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
384 return;
385
386 if (!videodata->ime_available) {
387 IME_Disable(videodata, hwnd);
388 return;
389 }
390 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
391 ImmAssociateContext(videodata->ime_hwnd_current, videodata->ime_himc);
392
393 videodata->ime_enabled = SDL_TRUE;
394 IME_UpdateInputLocale(videodata);
395 UILess_EnableUIUpdates(videodata);
396 }
397
398 static void
IME_Disable(SDL_VideoData * videodata,HWND hwnd)399 IME_Disable(SDL_VideoData *videodata, HWND hwnd)
400 {
401 if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
402 return;
403
404 IME_ClearComposition(videodata);
405 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
406 ImmAssociateContext(videodata->ime_hwnd_current, (HIMC)0);
407
408 videodata->ime_enabled = SDL_FALSE;
409 UILess_DisableUIUpdates(videodata);
410 }
411
412 static void
IME_Quit(SDL_VideoData * videodata)413 IME_Quit(SDL_VideoData *videodata)
414 {
415 if (!videodata->ime_initialized)
416 return;
417
418 UILess_ReleaseSinks(videodata);
419 if (videodata->ime_hwnd_main)
420 ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc);
421
422 videodata->ime_hwnd_main = 0;
423 videodata->ime_himc = 0;
424 if (videodata->ime_himm32) {
425 SDL_UnloadObject(videodata->ime_himm32);
426 videodata->ime_himm32 = 0;
427 }
428 if (videodata->ime_threadmgr) {
429 videodata->ime_threadmgr->lpVtbl->Release(videodata->ime_threadmgr);
430 videodata->ime_threadmgr = 0;
431 }
432 if (videodata->ime_com_initialized) {
433 WIN_CoUninitialize();
434 videodata->ime_com_initialized = SDL_FALSE;
435 }
436 IME_DestroyTextures(videodata);
437 videodata->ime_initialized = SDL_FALSE;
438 }
439
440 static void
IME_GetReadingString(SDL_VideoData * videodata,HWND hwnd)441 IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd)
442 {
443 DWORD id = 0;
444 HIMC himc = 0;
445 WCHAR buffer[16];
446 WCHAR *s = buffer;
447 DWORD len = 0;
448 INT err = 0;
449 BOOL vertical = FALSE;
450 UINT maxuilen = 0;
451 static OSVERSIONINFOA osversion;
452
453 if (videodata->ime_uiless)
454 return;
455
456 videodata->ime_readingstring[0] = 0;
457 if (!osversion.dwOSVersionInfoSize) {
458 osversion.dwOSVersionInfoSize = sizeof(osversion);
459 GetVersionExA(&osversion);
460 }
461 id = IME_GetId(videodata, 0);
462 if (!id)
463 return;
464
465 himc = ImmGetContext(hwnd);
466 if (!himc)
467 return;
468
469 if (videodata->GetReadingString) {
470 len = videodata->GetReadingString(himc, 0, 0, &err, &vertical, &maxuilen);
471 if (len) {
472 if (len > SDL_arraysize(buffer))
473 len = SDL_arraysize(buffer);
474
475 len = videodata->GetReadingString(himc, len, s, &err, &vertical, &maxuilen);
476 }
477 SDL_wcslcpy(videodata->ime_readingstring, s, len);
478 }
479 else {
480 LPINPUTCONTEXT2 lpimc = videodata->ImmLockIMC(himc);
481 LPBYTE p = 0;
482 s = 0;
483 switch (id)
484 {
485 case IMEID_CHT_VER42:
486 case IMEID_CHT_VER43:
487 case IMEID_CHT_VER44:
488 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 24);
489 if (!p)
490 break;
491
492 len = *(DWORD *)(p + 7*4 + 32*4);
493 s = (WCHAR *)(p + 56);
494 break;
495 case IMEID_CHT_VER51:
496 case IMEID_CHT_VER52:
497 case IMEID_CHS_VER53:
498 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 4);
499 if (!p)
500 break;
501
502 p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);
503 if (!p)
504 break;
505
506 len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
507 s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
508 break;
509 case IMEID_CHS_VER41:
510 {
511 int offset = (IME_GetId(videodata, 1) >= 0x00000002) ? 8 : 7;
512 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + offset * 4);
513 if (!p)
514 break;
515
516 len = *(DWORD *)(p + 7*4 + 16*2*4);
517 s = (WCHAR *)(p + 6*4 + 16*2*1);
518 }
519 break;
520 case IMEID_CHS_VER42:
521 if (osversion.dwPlatformId != VER_PLATFORM_WIN32_NT)
522 break;
523
524 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 1*4 + 1*4 + 6*4);
525 if (!p)
526 break;
527
528 len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
529 s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
530 break;
531 }
532 if (s) {
533 size_t size = SDL_min((size_t)(len + 1), SDL_arraysize(videodata->ime_readingstring));
534 SDL_wcslcpy(videodata->ime_readingstring, s, size);
535 }
536
537 videodata->ImmUnlockIMCC(lpimc->hPrivate);
538 videodata->ImmUnlockIMC(himc);
539 }
540 ImmReleaseContext(hwnd, himc);
541 IME_SendEditingEvent(videodata);
542 }
543
544 static void
IME_InputLangChanged(SDL_VideoData * videodata)545 IME_InputLangChanged(SDL_VideoData *videodata)
546 {
547 UINT lang = PRIMLANG();
548 IME_UpdateInputLocale(videodata);
549 if (!videodata->ime_uiless)
550 videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
551
552 IME_SetupAPI(videodata);
553 if (lang != PRIMLANG()) {
554 IME_ClearComposition(videodata);
555 }
556 }
557
558 static DWORD
IME_GetId(SDL_VideoData * videodata,UINT uIndex)559 IME_GetId(SDL_VideoData *videodata, UINT uIndex)
560 {
561 static HKL hklprev = 0;
562 static DWORD dwRet[2] = {0};
563 DWORD dwVerSize = 0;
564 DWORD dwVerHandle = 0;
565 LPVOID lpVerBuffer = 0;
566 LPVOID lpVerData = 0;
567 UINT cbVerData = 0;
568 char szTemp[256];
569 HKL hkl = 0;
570 DWORD dwLang = 0;
571 if (uIndex >= sizeof(dwRet) / sizeof(dwRet[0]))
572 return 0;
573
574 hkl = videodata->ime_hkl;
575 if (hklprev == hkl)
576 return dwRet[uIndex];
577
578 hklprev = hkl;
579 dwLang = ((DWORD_PTR)hkl & 0xffff);
580 if (videodata->ime_uiless && LANG() == LANG_CHT) {
581 dwRet[0] = IMEID_CHT_VER_VISTA;
582 dwRet[1] = 0;
583 return dwRet[0];
584 }
585 if (hkl != CHT_HKL_NEW_PHONETIC
586 && hkl != CHT_HKL_NEW_CHANG_JIE
587 && hkl != CHT_HKL_NEW_QUICK
588 && hkl != CHT_HKL_HK_CANTONESE
589 && hkl != CHS_HKL) {
590 dwRet[0] = dwRet[1] = 0;
591 return dwRet[uIndex];
592 }
593 if (ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1) <= 0) {
594 dwRet[0] = dwRet[1] = 0;
595 return dwRet[uIndex];
596 }
597 if (!videodata->GetReadingString) {
598 #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
599 if (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME1, -1) != 2
600 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME2, -1) != 2
601 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME3, -1) != 2
602 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME1, -1) != 2
603 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME2, -1) != 2) {
604 dwRet[0] = dwRet[1] = 0;
605 return dwRet[uIndex];
606 }
607 #undef LCID_INVARIANT
608 dwVerSize = GetFileVersionInfoSizeA(szTemp, &dwVerHandle);
609 if (dwVerSize) {
610 lpVerBuffer = SDL_malloc(dwVerSize);
611 if (lpVerBuffer) {
612 if (GetFileVersionInfoA(szTemp, dwVerHandle, dwVerSize, lpVerBuffer)) {
613 if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) {
614 #define pVerFixedInfo ((VS_FIXEDFILEINFO FAR*)lpVerData)
615 DWORD dwVer = pVerFixedInfo->dwFileVersionMS;
616 dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16;
617 if ((videodata->GetReadingString) ||
618 ((dwLang == LANG_CHT) && (
619 dwVer == MAKEIMEVERSION(4, 2) ||
620 dwVer == MAKEIMEVERSION(4, 3) ||
621 dwVer == MAKEIMEVERSION(4, 4) ||
622 dwVer == MAKEIMEVERSION(5, 0) ||
623 dwVer == MAKEIMEVERSION(5, 1) ||
624 dwVer == MAKEIMEVERSION(5, 2) ||
625 dwVer == MAKEIMEVERSION(6, 0)))
626 ||
627 ((dwLang == LANG_CHS) && (
628 dwVer == MAKEIMEVERSION(4, 1) ||
629 dwVer == MAKEIMEVERSION(4, 2) ||
630 dwVer == MAKEIMEVERSION(5, 3)))) {
631 dwRet[0] = dwVer | dwLang;
632 dwRet[1] = pVerFixedInfo->dwFileVersionLS;
633 SDL_free(lpVerBuffer);
634 return dwRet[0];
635 }
636 #undef pVerFixedInfo
637 }
638 }
639 }
640 SDL_free(lpVerBuffer);
641 }
642 }
643 dwRet[0] = dwRet[1] = 0;
644 return dwRet[uIndex];
645 }
646
647 static void
IME_SetupAPI(SDL_VideoData * videodata)648 IME_SetupAPI(SDL_VideoData *videodata)
649 {
650 char ime_file[MAX_PATH + 1];
651 void* hime = 0;
652 HKL hkl = 0;
653 videodata->GetReadingString = 0;
654 videodata->ShowReadingWindow = 0;
655 if (videodata->ime_uiless)
656 return;
657
658 hkl = videodata->ime_hkl;
659 if (ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1) <= 0)
660 return;
661
662 hime = SDL_LoadObject(ime_file);
663 if (!hime)
664 return;
665
666 videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT))
667 SDL_LoadFunction(hime, "GetReadingString");
668 videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL))
669 SDL_LoadFunction(hime, "ShowReadingWindow");
670
671 if (videodata->ShowReadingWindow) {
672 HIMC himc = ImmGetContext(videodata->ime_hwnd_current);
673 if (himc) {
674 videodata->ShowReadingWindow(himc, FALSE);
675 ImmReleaseContext(videodata->ime_hwnd_current, himc);
676 }
677 }
678 }
679
680 static void
IME_SetWindow(SDL_VideoData * videodata,HWND hwnd)681 IME_SetWindow(SDL_VideoData* videodata, HWND hwnd)
682 {
683 videodata->ime_hwnd_current = hwnd;
684 if (videodata->ime_threadmgr) {
685 struct ITfDocumentMgr *document_mgr = 0;
686 if (SUCCEEDED(videodata->ime_threadmgr->lpVtbl->AssociateFocus(videodata->ime_threadmgr, hwnd, NULL, &document_mgr))) {
687 if (document_mgr)
688 document_mgr->lpVtbl->Release(document_mgr);
689 }
690 }
691 }
692
693 static void
IME_UpdateInputLocale(SDL_VideoData * videodata)694 IME_UpdateInputLocale(SDL_VideoData *videodata)
695 {
696 static HKL hklprev = 0;
697 videodata->ime_hkl = GetKeyboardLayout(0);
698 if (hklprev == videodata->ime_hkl)
699 return;
700
701 hklprev = videodata->ime_hkl;
702 switch (PRIMLANG()) {
703 case LANG_CHINESE:
704 videodata->ime_candvertical = SDL_TRUE;
705 if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
706 videodata->ime_candvertical = SDL_FALSE;
707
708 break;
709 case LANG_JAPANESE:
710 videodata->ime_candvertical = SDL_TRUE;
711 break;
712 case LANG_KOREAN:
713 videodata->ime_candvertical = SDL_FALSE;
714 break;
715 }
716 }
717
718 static void
IME_ClearComposition(SDL_VideoData * videodata)719 IME_ClearComposition(SDL_VideoData *videodata)
720 {
721 HIMC himc = 0;
722 if (!videodata->ime_initialized)
723 return;
724
725 himc = ImmGetContext(videodata->ime_hwnd_current);
726 if (!himc)
727 return;
728
729 ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
730 if (videodata->ime_uiless)
731 ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
732
733 ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0);
734 ImmReleaseContext(videodata->ime_hwnd_current, himc);
735 SDL_SendEditingText("", 0, 0);
736 }
737
738 static void
IME_GetCompositionString(SDL_VideoData * videodata,HIMC himc,DWORD string)739 IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
740 {
741 LONG length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, sizeof(videodata->ime_composition) - sizeof(videodata->ime_composition[0]));
742 if (length < 0)
743 length = 0;
744
745 length /= sizeof(videodata->ime_composition[0]);
746 videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0));
747 if (videodata->ime_cursor < SDL_arraysize(videodata->ime_composition) && videodata->ime_composition[videodata->ime_cursor] == 0x3000) {
748 int i;
749 for (i = videodata->ime_cursor + 1; i < length; ++i)
750 videodata->ime_composition[i - 1] = videodata->ime_composition[i];
751
752 --length;
753 }
754 videodata->ime_composition[length] = 0;
755 }
756
757 static void
IME_SendInputEvent(SDL_VideoData * videodata)758 IME_SendInputEvent(SDL_VideoData *videodata)
759 {
760 char *s = 0;
761 s = WIN_StringToUTF8(videodata->ime_composition);
762 SDL_SendKeyboardText(s);
763 SDL_free(s);
764
765 videodata->ime_composition[0] = 0;
766 videodata->ime_readingstring[0] = 0;
767 videodata->ime_cursor = 0;
768 }
769
770 static void
IME_SendEditingEvent(SDL_VideoData * videodata)771 IME_SendEditingEvent(SDL_VideoData *videodata)
772 {
773 char *s = 0;
774 WCHAR buffer[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
775 const size_t size = SDL_arraysize(buffer);
776 buffer[0] = 0;
777 if (videodata->ime_readingstring[0]) {
778 size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor);
779 SDL_wcslcpy(buffer, videodata->ime_composition, len + 1);
780 SDL_wcslcat(buffer, videodata->ime_readingstring, size);
781 SDL_wcslcat(buffer, &videodata->ime_composition[len], size);
782 }
783 else {
784 SDL_wcslcpy(buffer, videodata->ime_composition, size);
785 }
786 s = WIN_StringToUTF8(buffer);
787 SDL_SendEditingText(s, videodata->ime_cursor + (int)SDL_wcslen(videodata->ime_readingstring), 0);
788 SDL_free(s);
789 }
790
791 static void
IME_AddCandidate(SDL_VideoData * videodata,UINT i,LPCWSTR candidate)792 IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
793 {
794 LPWSTR dst = videodata->ime_candidates[i];
795 *dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
796 if (videodata->ime_candvertical)
797 *dst++ = TEXT(' ');
798
799 while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
800 *dst++ = *candidate++;
801
802 *dst = (WCHAR)'\0';
803 }
804
805 static void
IME_GetCandidateList(HIMC himc,SDL_VideoData * videodata)806 IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
807 {
808 LPCANDIDATELIST cand_list = 0;
809 DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
810 if (size) {
811 cand_list = (LPCANDIDATELIST)SDL_malloc(size);
812 if (cand_list) {
813 size = ImmGetCandidateListW(himc, 0, cand_list, size);
814 if (size) {
815 UINT i, j;
816 UINT page_start = 0;
817 videodata->ime_candsel = cand_list->dwSelection;
818 videodata->ime_candcount = cand_list->dwCount;
819
820 if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) {
821 const UINT maxcandchar = 18;
822 size_t cchars = 0;
823
824 for (i = 0; i < videodata->ime_candcount; ++i) {
825 size_t len = SDL_wcslen((LPWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i])) + 1;
826 if (len + cchars > maxcandchar) {
827 if (i > cand_list->dwSelection)
828 break;
829
830 page_start = i;
831 cchars = len;
832 }
833 else {
834 cchars += len;
835 }
836 }
837 videodata->ime_candpgsize = i - page_start;
838 } else {
839 videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
840 page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
841 }
842 SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
843 for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++) {
844 LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]);
845 IME_AddCandidate(videodata, j, candidate);
846 }
847 if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
848 videodata->ime_candsel = -1;
849
850 }
851 SDL_free(cand_list);
852 }
853 }
854 }
855
856 static void
IME_ShowCandidateList(SDL_VideoData * videodata)857 IME_ShowCandidateList(SDL_VideoData *videodata)
858 {
859 videodata->ime_dirty = SDL_TRUE;
860 videodata->ime_candlist = SDL_TRUE;
861 IME_DestroyTextures(videodata);
862 IME_SendEditingEvent(videodata);
863 }
864
865 static void
IME_HideCandidateList(SDL_VideoData * videodata)866 IME_HideCandidateList(SDL_VideoData *videodata)
867 {
868 videodata->ime_dirty = SDL_FALSE;
869 videodata->ime_candlist = SDL_FALSE;
870 IME_DestroyTextures(videodata);
871 IME_SendEditingEvent(videodata);
872 }
873
874 SDL_bool
IME_HandleMessage(HWND hwnd,UINT msg,WPARAM wParam,LPARAM * lParam,SDL_VideoData * videodata)875 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
876 {
877 SDL_bool trap = SDL_FALSE;
878 HIMC himc = 0;
879 if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled)
880 return SDL_FALSE;
881
882 switch (msg) {
883 case WM_INPUTLANGCHANGE:
884 IME_InputLangChanged(videodata);
885 break;
886 case WM_IME_SETCONTEXT:
887 *lParam = 0;
888 break;
889 case WM_IME_STARTCOMPOSITION:
890 trap = SDL_TRUE;
891 break;
892 case WM_IME_COMPOSITION:
893 trap = SDL_TRUE;
894 himc = ImmGetContext(hwnd);
895 if (*lParam & GCS_RESULTSTR) {
896 IME_GetCompositionString(videodata, himc, GCS_RESULTSTR);
897 IME_SendInputEvent(videodata);
898 }
899 if (*lParam & GCS_COMPSTR) {
900 if (!videodata->ime_uiless)
901 videodata->ime_readingstring[0] = 0;
902
903 IME_GetCompositionString(videodata, himc, GCS_COMPSTR);
904 IME_SendEditingEvent(videodata);
905 }
906 ImmReleaseContext(hwnd, himc);
907 break;
908 case WM_IME_ENDCOMPOSITION:
909 videodata->ime_composition[0] = 0;
910 videodata->ime_readingstring[0] = 0;
911 videodata->ime_cursor = 0;
912 SDL_SendEditingText("", 0, 0);
913 break;
914 case WM_IME_NOTIFY:
915 switch (wParam) {
916 case IMN_SETCONVERSIONMODE:
917 case IMN_SETOPENSTATUS:
918 IME_UpdateInputLocale(videodata);
919 break;
920 case IMN_OPENCANDIDATE:
921 case IMN_CHANGECANDIDATE:
922 if (videodata->ime_uiless)
923 break;
924
925 trap = SDL_TRUE;
926 IME_ShowCandidateList(videodata);
927 himc = ImmGetContext(hwnd);
928 if (!himc)
929 break;
930
931 IME_GetCandidateList(himc, videodata);
932 ImmReleaseContext(hwnd, himc);
933 break;
934 case IMN_CLOSECANDIDATE:
935 trap = SDL_TRUE;
936 IME_HideCandidateList(videodata);
937 break;
938 case IMN_PRIVATE:
939 {
940 DWORD dwId = IME_GetId(videodata, 0);
941 IME_GetReadingString(videodata, hwnd);
942 switch (dwId)
943 {
944 case IMEID_CHT_VER42:
945 case IMEID_CHT_VER43:
946 case IMEID_CHT_VER44:
947 case IMEID_CHS_VER41:
948 case IMEID_CHS_VER42:
949 if (*lParam == 1 || *lParam == 2)
950 trap = SDL_TRUE;
951
952 break;
953 case IMEID_CHT_VER50:
954 case IMEID_CHT_VER51:
955 case IMEID_CHT_VER52:
956 case IMEID_CHT_VER60:
957 case IMEID_CHS_VER53:
958 if (*lParam == 16
959 || *lParam == 17
960 || *lParam == 26
961 || *lParam == 27
962 || *lParam == 28)
963 trap = SDL_TRUE;
964 break;
965 }
966 }
967 break;
968 default:
969 trap = SDL_TRUE;
970 break;
971 }
972 break;
973 }
974 return trap;
975 }
976
977 static void
IME_CloseCandidateList(SDL_VideoData * videodata)978 IME_CloseCandidateList(SDL_VideoData *videodata)
979 {
980 IME_HideCandidateList(videodata);
981 videodata->ime_candcount = 0;
982 SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
983 }
984
985 static void
UILess_GetCandidateList(SDL_VideoData * videodata,ITfCandidateListUIElement * pcandlist)986 UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
987 {
988 UINT selection = 0;
989 UINT count = 0;
990 UINT page = 0;
991 UINT pgcount = 0;
992 DWORD pgstart = 0;
993 DWORD pgsize = 0;
994 UINT i, j;
995 pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
996 pcandlist->lpVtbl->GetCount(pcandlist, &count);
997 pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
998
999 videodata->ime_candsel = selection;
1000 videodata->ime_candcount = count;
1001 IME_ShowCandidateList(videodata);
1002
1003 pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
1004 if (pgcount > 0) {
1005 UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
1006 if (idxlist) {
1007 pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
1008 pgstart = idxlist[page];
1009 if (page < pgcount - 1)
1010 pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
1011 else
1012 pgsize = count - pgstart;
1013
1014 SDL_free(idxlist);
1015 }
1016 }
1017 videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
1018 videodata->ime_candsel = videodata->ime_candsel - pgstart;
1019
1020 SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
1021 for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++) {
1022 BSTR bstr;
1023 if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr))) {
1024 if (bstr) {
1025 IME_AddCandidate(videodata, j, bstr);
1026 SysFreeString(bstr);
1027 }
1028 }
1029 }
1030 if (PRIMLANG() == LANG_KOREAN)
1031 videodata->ime_candsel = -1;
1032 }
1033
TSFSink_AddRef(TSFSink * sink)1034 STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
1035 {
1036 return ++sink->refcount;
1037 }
1038
TSFSink_Release(TSFSink * sink)1039 STDMETHODIMP_(ULONG)TSFSink_Release(TSFSink *sink)
1040 {
1041 --sink->refcount;
1042 if (sink->refcount == 0) {
1043 SDL_free(sink);
1044 return 0;
1045 }
1046 return sink->refcount;
1047 }
1048
UIElementSink_QueryInterface(TSFSink * sink,REFIID riid,PVOID * ppv)1049 STDMETHODIMP UIElementSink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
1050 {
1051 if (!ppv)
1052 return E_INVALIDARG;
1053
1054 *ppv = 0;
1055 if (SDL_IsEqualIID(riid, &IID_IUnknown))
1056 *ppv = (IUnknown *)sink;
1057 else if (SDL_IsEqualIID(riid, &IID_ITfUIElementSink))
1058 *ppv = (ITfUIElementSink *)sink;
1059
1060 if (*ppv) {
1061 TSFSink_AddRef(sink);
1062 return S_OK;
1063 }
1064 return E_NOINTERFACE;
1065 }
1066
UILess_GetUIElement(SDL_VideoData * videodata,DWORD dwUIElementId)1067 ITfUIElement *UILess_GetUIElement(SDL_VideoData *videodata, DWORD dwUIElementId)
1068 {
1069 ITfUIElementMgr *puiem = 0;
1070 ITfUIElement *pelem = 0;
1071 ITfThreadMgrEx *threadmgrex = videodata->ime_threadmgrex;
1072
1073 if (SUCCEEDED(threadmgrex->lpVtbl->QueryInterface(threadmgrex, &IID_ITfUIElementMgr, (LPVOID *)&puiem))) {
1074 puiem->lpVtbl->GetUIElement(puiem, dwUIElementId, &pelem);
1075 puiem->lpVtbl->Release(puiem);
1076 }
1077 return pelem;
1078 }
1079
UIElementSink_BeginUIElement(TSFSink * sink,DWORD dwUIElementId,BOOL * pbShow)1080 STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow)
1081 {
1082 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
1083 ITfReadingInformationUIElement *preading = 0;
1084 ITfCandidateListUIElement *pcandlist = 0;
1085 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1086 if (!element)
1087 return E_INVALIDARG;
1088
1089 *pbShow = FALSE;
1090 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
1091 BSTR bstr;
1092 if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
1093 SysFreeString(bstr);
1094 }
1095 preading->lpVtbl->Release(preading);
1096 }
1097 else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1098 videodata->ime_candref++;
1099 UILess_GetCandidateList(videodata, pcandlist);
1100 pcandlist->lpVtbl->Release(pcandlist);
1101 }
1102 return S_OK;
1103 }
1104
UIElementSink_UpdateUIElement(TSFSink * sink,DWORD dwUIElementId)1105 STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
1106 {
1107 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
1108 ITfReadingInformationUIElement *preading = 0;
1109 ITfCandidateListUIElement *pcandlist = 0;
1110 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1111 if (!element)
1112 return E_INVALIDARG;
1113
1114 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
1115 BSTR bstr;
1116 if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
1117 WCHAR *s = (WCHAR *)bstr;
1118 SDL_wcslcpy(videodata->ime_readingstring, s, SDL_arraysize(videodata->ime_readingstring));
1119 IME_SendEditingEvent(videodata);
1120 SysFreeString(bstr);
1121 }
1122 preading->lpVtbl->Release(preading);
1123 }
1124 else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1125 UILess_GetCandidateList(videodata, pcandlist);
1126 pcandlist->lpVtbl->Release(pcandlist);
1127 }
1128 return S_OK;
1129 }
1130
UIElementSink_EndUIElement(TSFSink * sink,DWORD dwUIElementId)1131 STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
1132 {
1133 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
1134 ITfReadingInformationUIElement *preading = 0;
1135 ITfCandidateListUIElement *pcandlist = 0;
1136 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1137 if (!element)
1138 return E_INVALIDARG;
1139
1140 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
1141 videodata->ime_readingstring[0] = 0;
1142 IME_SendEditingEvent(videodata);
1143 preading->lpVtbl->Release(preading);
1144 }
1145 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1146 videodata->ime_candref--;
1147 if (videodata->ime_candref == 0)
1148 IME_CloseCandidateList(videodata);
1149
1150 pcandlist->lpVtbl->Release(pcandlist);
1151 }
1152 return S_OK;
1153 }
1154
IPPASink_QueryInterface(TSFSink * sink,REFIID riid,PVOID * ppv)1155 STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
1156 {
1157 if (!ppv)
1158 return E_INVALIDARG;
1159
1160 *ppv = 0;
1161 if (SDL_IsEqualIID(riid, &IID_IUnknown))
1162 *ppv = (IUnknown *)sink;
1163 else if (SDL_IsEqualIID(riid, &IID_ITfInputProcessorProfileActivationSink))
1164 *ppv = (ITfInputProcessorProfileActivationSink *)sink;
1165
1166 if (*ppv) {
1167 TSFSink_AddRef(sink);
1168 return S_OK;
1169 }
1170 return E_NOINTERFACE;
1171 }
1172
IPPASink_OnActivated(TSFSink * sink,DWORD dwProfileType,LANGID langid,REFCLSID clsid,REFGUID catid,REFGUID guidProfile,HKL hkl,DWORD dwFlags)1173 STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
1174 {
1175 static const GUID TF_PROFILE_DAYI = { 0x037B2C25, 0x480C, 0x4D7F, { 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A } };
1176 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1177 videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
1178 if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
1179 IME_InputLangChanged((SDL_VideoData *)sink->data);
1180
1181 IME_HideCandidateList(videodata);
1182 return S_OK;
1183 }
1184
1185 static void *vtUIElementSink[] = {
1186 (void *)(UIElementSink_QueryInterface),
1187 (void *)(TSFSink_AddRef),
1188 (void *)(TSFSink_Release),
1189 (void *)(UIElementSink_BeginUIElement),
1190 (void *)(UIElementSink_UpdateUIElement),
1191 (void *)(UIElementSink_EndUIElement)
1192 };
1193
1194 static void *vtIPPASink[] = {
1195 (void *)(IPPASink_QueryInterface),
1196 (void *)(TSFSink_AddRef),
1197 (void *)(TSFSink_Release),
1198 (void *)(IPPASink_OnActivated)
1199 };
1200
1201 static void
UILess_EnableUIUpdates(SDL_VideoData * videodata)1202 UILess_EnableUIUpdates(SDL_VideoData *videodata)
1203 {
1204 ITfSource *source = 0;
1205 if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie != TF_INVALID_COOKIE)
1206 return;
1207
1208 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1209 source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie);
1210 source->lpVtbl->Release(source);
1211 }
1212 }
1213
1214 static void
UILess_DisableUIUpdates(SDL_VideoData * videodata)1215 UILess_DisableUIUpdates(SDL_VideoData *videodata)
1216 {
1217 ITfSource *source = 0;
1218 if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie == TF_INVALID_COOKIE)
1219 return;
1220
1221 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1222 source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
1223 videodata->ime_uielemsinkcookie = TF_INVALID_COOKIE;
1224 source->lpVtbl->Release(source);
1225 }
1226 }
1227
1228 static SDL_bool
UILess_SetupSinks(SDL_VideoData * videodata)1229 UILess_SetupSinks(SDL_VideoData *videodata)
1230 {
1231 TfClientId clientid = 0;
1232 SDL_bool result = SDL_FALSE;
1233 ITfSource *source = 0;
1234 if (FAILED(CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, (LPVOID *)&videodata->ime_threadmgrex)))
1235 return SDL_FALSE;
1236
1237 if (FAILED(videodata->ime_threadmgrex->lpVtbl->ActivateEx(videodata->ime_threadmgrex, &clientid, TF_TMAE_UIELEMENTENABLEDONLY)))
1238 return SDL_FALSE;
1239
1240 videodata->ime_uielemsink = SDL_malloc(sizeof(TSFSink));
1241 videodata->ime_ippasink = SDL_malloc(sizeof(TSFSink));
1242
1243 videodata->ime_uielemsink->lpVtbl = vtUIElementSink;
1244 videodata->ime_uielemsink->refcount = 1;
1245 videodata->ime_uielemsink->data = videodata;
1246
1247 videodata->ime_ippasink->lpVtbl = vtIPPASink;
1248 videodata->ime_ippasink->refcount = 1;
1249 videodata->ime_ippasink->data = videodata;
1250
1251 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1252 if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie))) {
1253 if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfInputProcessorProfileActivationSink, (IUnknown *)videodata->ime_ippasink, &videodata->ime_alpnsinkcookie))) {
1254 result = SDL_TRUE;
1255 }
1256 }
1257 source->lpVtbl->Release(source);
1258 }
1259 return result;
1260 }
1261
1262 #define SAFE_RELEASE(p) \
1263 { \
1264 if (p) { \
1265 (p)->lpVtbl->Release((p)); \
1266 (p) = 0; \
1267 } \
1268 }
1269
1270 static void
UILess_ReleaseSinks(SDL_VideoData * videodata)1271 UILess_ReleaseSinks(SDL_VideoData *videodata)
1272 {
1273 ITfSource *source = 0;
1274 if (videodata->ime_threadmgrex && SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1275 source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
1276 source->lpVtbl->UnadviseSink(source, videodata->ime_alpnsinkcookie);
1277 SAFE_RELEASE(source);
1278 videodata->ime_threadmgrex->lpVtbl->Deactivate(videodata->ime_threadmgrex);
1279 SAFE_RELEASE(videodata->ime_threadmgrex);
1280 TSFSink_Release(videodata->ime_uielemsink);
1281 videodata->ime_uielemsink = 0;
1282 TSFSink_Release(videodata->ime_ippasink);
1283 videodata->ime_ippasink = 0;
1284 }
1285 }
1286
1287 static void *
StartDrawToBitmap(HDC hdc,HBITMAP * hhbm,int width,int height)1288 StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
1289 {
1290 BITMAPINFO info;
1291 BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
1292 BYTE *bits = NULL;
1293 if (hhbm) {
1294 SDL_zero(info);
1295 infoHeader->biSize = sizeof(BITMAPINFOHEADER);
1296 infoHeader->biWidth = width;
1297 infoHeader->biHeight = -1 * SDL_abs(height);
1298 infoHeader->biPlanes = 1;
1299 infoHeader->biBitCount = 32;
1300 infoHeader->biCompression = BI_RGB;
1301 *hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
1302 if (*hhbm)
1303 SelectObject(hdc, *hhbm);
1304 }
1305 return bits;
1306 }
1307
1308 static void
StopDrawToBitmap(HDC hdc,HBITMAP * hhbm)1309 StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
1310 {
1311 if (hhbm && *hhbm) {
1312 DeleteObject(*hhbm);
1313 *hhbm = NULL;
1314 }
1315 }
1316
1317 /* This draws only within the specified area and fills the entire region. */
1318 static void
DrawRect(HDC hdc,int left,int top,int right,int bottom,int pensize)1319 DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
1320 {
1321 /* The case of no pen (PenSize = 0) is automatically taken care of. */
1322 const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
1323 left += pensize / 2;
1324 top += pensize / 2;
1325 right -= penadjust;
1326 bottom -= penadjust;
1327 Rectangle(hdc, left, top, right, bottom);
1328 }
1329
1330 static void
IME_DestroyTextures(SDL_VideoData * videodata)1331 IME_DestroyTextures(SDL_VideoData *videodata)
1332 {
1333 }
1334
1335 #define SDL_swap(a,b) { \
1336 int c = (a); \
1337 (a) = (b); \
1338 (b) = c; \
1339 }
1340
1341 static void
IME_PositionCandidateList(SDL_VideoData * videodata,SIZE size)1342 IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
1343 {
1344 int left, top, right, bottom;
1345 SDL_bool ok = SDL_FALSE;
1346 int winw = videodata->ime_winwidth;
1347 int winh = videodata->ime_winheight;
1348
1349 /* Bottom */
1350 left = videodata->ime_rect.x;
1351 top = videodata->ime_rect.y + videodata->ime_rect.h;
1352 right = left + size.cx;
1353 bottom = top + size.cy;
1354 if (right >= winw) {
1355 left -= right - winw;
1356 right = winw;
1357 }
1358 if (bottom < winh)
1359 ok = SDL_TRUE;
1360
1361 /* Top */
1362 if (!ok) {
1363 left = videodata->ime_rect.x;
1364 top = videodata->ime_rect.y - size.cy;
1365 right = left + size.cx;
1366 bottom = videodata->ime_rect.y;
1367 if (right >= winw) {
1368 left -= right - winw;
1369 right = winw;
1370 }
1371 if (top >= 0)
1372 ok = SDL_TRUE;
1373 }
1374
1375 /* Right */
1376 if (!ok) {
1377 left = videodata->ime_rect.x + size.cx;
1378 top = 0;
1379 right = left + size.cx;
1380 bottom = size.cy;
1381 if (right < winw)
1382 ok = SDL_TRUE;
1383 }
1384
1385 /* Left */
1386 if (!ok) {
1387 left = videodata->ime_rect.x - size.cx;
1388 top = 0;
1389 right = videodata->ime_rect.x;
1390 bottom = size.cy;
1391 if (right >= 0)
1392 ok = SDL_TRUE;
1393 }
1394
1395 /* Window too small, show at (0,0) */
1396 if (!ok) {
1397 left = 0;
1398 top = 0;
1399 right = size.cx;
1400 bottom = size.cy;
1401 }
1402
1403 videodata->ime_candlistrect.x = left;
1404 videodata->ime_candlistrect.y = top;
1405 videodata->ime_candlistrect.w = right - left;
1406 videodata->ime_candlistrect.h = bottom - top;
1407 }
1408
1409 static void
IME_RenderCandidateList(SDL_VideoData * videodata,HDC hdc)1410 IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
1411 {
1412 int i, j;
1413 SIZE size = {0};
1414 SIZE candsizes[MAX_CANDLIST];
1415 SIZE maxcandsize = {0};
1416 HBITMAP hbm = NULL;
1417 const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
1418 SDL_bool vertical = videodata->ime_candvertical;
1419
1420 const int listborder = 1;
1421 const int listpadding = 0;
1422 const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
1423 const int listfillcolor = RGB(255, 255, 255);
1424
1425 const int candborder = 1;
1426 const int candpadding = 0;
1427 const int candmargin = 1;
1428 const COLORREF candbordercolor = RGB(255, 255, 255);
1429 const COLORREF candfillcolor = RGB(255, 255, 255);
1430 const COLORREF candtextcolor = RGB(0, 0, 0);
1431 const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
1432 const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
1433 const COLORREF seltextcolor = RGB(0, 0, 0);
1434 const int horzcandspacing = 5;
1435
1436 HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1437 HBRUSH listbrush = CreateSolidBrush(listfillcolor);
1438 HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1439 HBRUSH candbrush = CreateSolidBrush(candfillcolor);
1440 HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1441 HBRUSH selbrush = CreateSolidBrush(selfillcolor);
1442 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"));
1443
1444 SetBkMode(hdc, TRANSPARENT);
1445 SelectObject(hdc, font);
1446
1447 for (i = 0; i < candcount; ++i) {
1448 const WCHAR *s = videodata->ime_candidates[i];
1449 if (!*s)
1450 break;
1451
1452 GetTextExtentPoint32W(hdc, s, (int)SDL_wcslen(s), &candsizes[i]);
1453 maxcandsize.cx = SDL_max(maxcandsize.cx, candsizes[i].cx);
1454 maxcandsize.cy = SDL_max(maxcandsize.cy, candsizes[i].cy);
1455
1456 }
1457 if (vertical) {
1458 size.cx =
1459 (listborder * 2) +
1460 (listpadding * 2) +
1461 (candmargin * 2) +
1462 (candborder * 2) +
1463 (candpadding * 2) +
1464 (maxcandsize.cx)
1465 ;
1466 size.cy =
1467 (listborder * 2) +
1468 (listpadding * 2) +
1469 ((candcount + 1) * candmargin) +
1470 (candcount * candborder * 2) +
1471 (candcount * candpadding * 2) +
1472 (candcount * maxcandsize.cy)
1473 ;
1474 }
1475 else {
1476 size.cx =
1477 (listborder * 2) +
1478 (listpadding * 2) +
1479 ((candcount + 1) * candmargin) +
1480 (candcount * candborder * 2) +
1481 (candcount * candpadding * 2) +
1482 ((candcount - 1) * horzcandspacing);
1483 ;
1484
1485 for (i = 0; i < candcount; ++i)
1486 size.cx += candsizes[i].cx;
1487
1488 size.cy =
1489 (listborder * 2) +
1490 (listpadding * 2) +
1491 (candmargin * 2) +
1492 (candborder * 2) +
1493 (candpadding * 2) +
1494 (maxcandsize.cy)
1495 ;
1496 }
1497
1498 StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
1499
1500 SelectObject(hdc, listpen);
1501 SelectObject(hdc, listbrush);
1502 DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
1503
1504 SelectObject(hdc, candpen);
1505 SelectObject(hdc, candbrush);
1506 SetTextColor(hdc, candtextcolor);
1507 SetBkMode(hdc, TRANSPARENT);
1508
1509 for (i = 0; i < candcount; ++i) {
1510 const WCHAR *s = videodata->ime_candidates[i];
1511 int left, top, right, bottom;
1512 if (!*s)
1513 break;
1514
1515 if (vertical) {
1516 left = listborder + listpadding + candmargin;
1517 top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
1518 right = size.cx - listborder - listpadding - candmargin;
1519 bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
1520 }
1521 else {
1522 left = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * horzcandspacing);
1523
1524 for (j = 0; j < i; ++j)
1525 left += candsizes[j].cx;
1526
1527 top = listborder + listpadding + candmargin;
1528 right = left + candsizes[i].cx + (candpadding * 2) + (candborder * 2);
1529 bottom = size.cy - listborder - listpadding - candmargin;
1530 }
1531
1532 if (i == videodata->ime_candsel) {
1533 SelectObject(hdc, selpen);
1534 SelectObject(hdc, selbrush);
1535 SetTextColor(hdc, seltextcolor);
1536 }
1537 else {
1538 SelectObject(hdc, candpen);
1539 SelectObject(hdc, candbrush);
1540 SetTextColor(hdc, candtextcolor);
1541 }
1542
1543 DrawRect(hdc, left, top, right, bottom, candborder);
1544 ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, (int)SDL_wcslen(s), NULL);
1545 }
1546 StopDrawToBitmap(hdc, &hbm);
1547
1548 DeleteObject(listpen);
1549 DeleteObject(listbrush);
1550 DeleteObject(candpen);
1551 DeleteObject(candbrush);
1552 DeleteObject(selpen);
1553 DeleteObject(selbrush);
1554 DeleteObject(font);
1555
1556 IME_PositionCandidateList(videodata, size);
1557 }
1558
1559 static void
IME_Render(SDL_VideoData * videodata)1560 IME_Render(SDL_VideoData *videodata)
1561 {
1562 HDC hdc = CreateCompatibleDC(NULL);
1563
1564 if (videodata->ime_candlist)
1565 IME_RenderCandidateList(videodata, hdc);
1566
1567 DeleteDC(hdc);
1568
1569 videodata->ime_dirty = SDL_FALSE;
1570 }
1571
IME_Present(SDL_VideoData * videodata)1572 void IME_Present(SDL_VideoData *videodata)
1573 {
1574 if (videodata->ime_dirty)
1575 IME_Render(videodata);
1576
1577 /* FIXME: Need to show the IME bitmap */
1578 }
1579
1580 #endif /* SDL_DISABLE_WINDOWS_IME */
1581
1582 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
1583
1584 /* vi: set ts=4 sw=4 expandtab: */
1585