Mercurial > sdl-ios-xcode
comparison src/video/win32/SDL_win32keyboard.c @ 4763:518d1679d2d0
Merged Daniel's Google Summer of Code work from SDL-gsoc2010_IME
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sun, 22 Aug 2010 12:39:27 -0700 |
parents | e4b2b7207f79 c40027ee6d47 |
children | 27c458e4ae31 b2e434e0553b |
comparison
equal
deleted
inserted
replaced
4762:833a225613e2 | 4763:518d1679d2d0 |
---|---|
23 | 23 |
24 #include "SDL_win32video.h" | 24 #include "SDL_win32video.h" |
25 | 25 |
26 #include "../../events/SDL_keyboard_c.h" | 26 #include "../../events/SDL_keyboard_c.h" |
27 #include "../../events/scancodes_win32.h" | 27 #include "../../events/scancodes_win32.h" |
28 | |
29 #include <imm.h> | |
30 #include <oleauto.h> | |
31 | |
32 static void IME_Init(SDL_VideoData *videodata, HWND hwnd); | |
33 static void IME_Enable(SDL_VideoData *videodata, HWND hwnd); | |
34 static void IME_Disable(SDL_VideoData *videodata, HWND hwnd); | |
35 static void IME_Quit(SDL_VideoData *videodata); | |
28 | 36 |
29 #ifndef MAPVK_VK_TO_VSC | 37 #ifndef MAPVK_VK_TO_VSC |
30 #define MAPVK_VK_TO_VSC 0 | 38 #define MAPVK_VK_TO_VSC 0 |
31 #endif | 39 #endif |
32 #ifndef MAPVK_VSC_TO_VK | 40 #ifndef MAPVK_VSC_TO_VK |
79 } | 87 } |
80 } | 88 } |
81 | 89 |
82 data->key_layout = win32_scancode_table; | 90 data->key_layout = win32_scancode_table; |
83 | 91 |
92 data->ime_com_initialized = SDL_FALSE; | |
93 data->ime_threadmgr = 0; | |
94 data->ime_initialized = SDL_FALSE; | |
95 data->ime_enabled = SDL_FALSE; | |
96 data->ime_available = SDL_FALSE; | |
97 data->ime_hwnd_main = 0; | |
98 data->ime_hwnd_current = 0; | |
99 data->ime_himc = 0; | |
100 data->ime_composition[0] = 0; | |
101 data->ime_readingstring[0] = 0; | |
102 data->ime_cursor = 0; | |
103 data->ime_hkl = 0; | |
104 data->ime_himm32 = 0; | |
105 data->GetReadingString = 0; | |
106 data->ShowReadingWindow = 0; | |
107 data->ImmLockIMC = 0; | |
108 data->ImmUnlockIMC = 0; | |
109 data->ImmLockIMCC = 0; | |
110 data->ImmUnlockIMCC = 0; | |
111 data->ime_uiless = SDL_FALSE; | |
112 data->ime_threadmgrex = 0; | |
113 data->ime_uielemsinkcookie = TF_INVALID_COOKIE; | |
114 data->ime_alpnsinkcookie = TF_INVALID_COOKIE; | |
115 data->ime_openmodesinkcookie = TF_INVALID_COOKIE; | |
116 data->ime_convmodesinkcookie = TF_INVALID_COOKIE; | |
117 data->ime_uielemsink = 0; | |
118 data->ime_ippasink = 0; | |
119 | |
84 WIN_UpdateKeymap(); | 120 WIN_UpdateKeymap(); |
85 | 121 |
86 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); | 122 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); |
87 SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows"); | 123 SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows"); |
88 SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows"); | 124 SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows"); |
117 } | 153 } |
118 | 154 |
119 void | 155 void |
120 WIN_QuitKeyboard(_THIS) | 156 WIN_QuitKeyboard(_THIS) |
121 { | 157 { |
158 IME_Quit((SDL_VideoData *)_this->driverdata); | |
159 } | |
160 | |
161 void | |
162 WIN_StartTextInput(_THIS) | |
163 { | |
164 SDL_Window *window = SDL_GetKeyboardFocus(); | |
165 if (window) { | |
166 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; | |
167 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; | |
168 IME_Init(videodata, hwnd); | |
169 IME_Enable(videodata, hwnd); | |
170 } | |
171 } | |
172 | |
173 void | |
174 WIN_StopTextInput(_THIS) | |
175 { | |
176 SDL_Window *window = SDL_GetKeyboardFocus(); | |
177 if (window) { | |
178 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; | |
179 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; | |
180 IME_Init(videodata, hwnd); | |
181 IME_Disable(videodata, hwnd); | |
182 } | |
183 } | |
184 | |
185 void | |
186 WIN_SetTextInputRect(_THIS, SDL_Rect *rect) | |
187 { | |
188 | |
189 } | |
190 | |
191 #define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) | |
192 #define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) | |
193 | |
194 #define MAKEIMEVERSION(major,minor) ((DWORD) (((BYTE)(major) << 24) | ((BYTE)(minor) << 16) )) | |
195 #define IMEID_VER(id) ((id) & 0xffff0000) | |
196 #define IMEID_LANG(id) ((id) & 0x0000ffff) | |
197 | |
198 #define CHT_HKL_DAYI ((HKL)0xE0060404) | |
199 #define CHT_HKL_NEW_PHONETIC ((HKL)0xE0080404) | |
200 #define CHT_HKL_NEW_CHANG_JIE ((HKL)0xE0090404) | |
201 #define CHT_HKL_NEW_QUICK ((HKL)0xE00A0404) | |
202 #define CHT_HKL_HK_CANTONESE ((HKL)0xE00B0404) | |
203 #define CHT_IMEFILENAME1 "TINTLGNT.IME" | |
204 #define CHT_IMEFILENAME2 "CINTLGNT.IME" | |
205 #define CHT_IMEFILENAME3 "MSTCIPHA.IME" | |
206 #define IMEID_CHT_VER42 (LANG_CHT | MAKEIMEVERSION(4, 2)) | |
207 #define IMEID_CHT_VER43 (LANG_CHT | MAKEIMEVERSION(4, 3)) | |
208 #define IMEID_CHT_VER44 (LANG_CHT | MAKEIMEVERSION(4, 4)) | |
209 #define IMEID_CHT_VER50 (LANG_CHT | MAKEIMEVERSION(5, 0)) | |
210 #define IMEID_CHT_VER51 (LANG_CHT | MAKEIMEVERSION(5, 1)) | |
211 #define IMEID_CHT_VER52 (LANG_CHT | MAKEIMEVERSION(5, 2)) | |
212 #define IMEID_CHT_VER60 (LANG_CHT | MAKEIMEVERSION(6, 0)) | |
213 #define IMEID_CHT_VER_VISTA (LANG_CHT | MAKEIMEVERSION(7, 0)) | |
214 | |
215 #define CHS_HKL ((HKL)0xE00E0804) | |
216 #define CHS_IMEFILENAME1 "PINTLGNT.IME" | |
217 #define CHS_IMEFILENAME2 "MSSCIPYA.IME" | |
218 #define IMEID_CHS_VER41 (LANG_CHS | MAKEIMEVERSION(4, 1)) | |
219 #define IMEID_CHS_VER42 (LANG_CHS | MAKEIMEVERSION(4, 2)) | |
220 #define IMEID_CHS_VER53 (LANG_CHS | MAKEIMEVERSION(5, 3)) | |
221 | |
222 #define LANG() LOWORD((videodata->ime_hkl)) | |
223 #define PRIMLANG() ((WORD)PRIMARYLANGID(LANG())) | |
224 #define SUBLANG() SUBLANGID(LANG()) | |
225 | |
226 static void IME_UpdateInputLocale(SDL_VideoData *videodata); | |
227 static void IME_ClearComposition(SDL_VideoData *videodata); | |
228 static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd); | |
229 static void IME_SetupAPI(SDL_VideoData *videodata); | |
230 static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex); | |
231 static void IME_SendEditingEvent(SDL_VideoData *videodata); | |
232 #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2) | |
233 #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID))) | |
234 | |
235 static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata); | |
236 static void UILess_ReleaseSinks(SDL_VideoData *videodata); | |
237 static void UILess_EnableUIUpdates(SDL_VideoData *videodata); | |
238 static void UILess_DisableUIUpdates(SDL_VideoData *videodata); | |
239 | |
240 static void | |
241 IME_Init(SDL_VideoData *videodata, HWND hwnd) | |
242 { | |
243 if (videodata->ime_initialized) | |
244 return; | |
245 | |
246 videodata->ime_hwnd_main = hwnd; | |
247 if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) { | |
248 videodata->ime_com_initialized = SDL_TRUE; | |
249 CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, &videodata->ime_threadmgr); | |
250 } | |
251 videodata->ime_initialized = SDL_TRUE; | |
252 videodata->ime_himm32 = LoadLibraryA("imm32.dll"); | |
253 if (!videodata->ime_himm32) { | |
254 videodata->ime_available = SDL_FALSE; | |
255 return; | |
256 } | |
257 videodata->ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI *)(HIMC))GetProcAddress(videodata->ime_himm32, "ImmLockIMC"); | |
258 videodata->ImmUnlockIMC = (BOOL (WINAPI *)(HIMC))GetProcAddress(videodata->ime_himm32, "ImmUnlockIMC"); | |
259 videodata->ImmLockIMCC = (LPVOID (WINAPI *)(HIMCC))GetProcAddress(videodata->ime_himm32, "ImmLockIMCC"); | |
260 videodata->ImmUnlockIMCC = (BOOL (WINAPI *)(HIMCC))GetProcAddress(videodata->ime_himm32, "ImmUnlockIMCC"); | |
261 | |
262 IME_SetWindow(videodata, hwnd); | |
263 videodata->ime_himc = ImmGetContext(hwnd); | |
264 ImmReleaseContext(hwnd, videodata->ime_himc); | |
265 if (!videodata->ime_himc) { | |
266 videodata->ime_available = SDL_FALSE; | |
267 IME_Disable(videodata, hwnd); | |
268 return; | |
269 } | |
270 videodata->ime_available = SDL_TRUE; | |
271 IME_UpdateInputLocale(videodata); | |
272 IME_SetupAPI(videodata); | |
273 videodata->ime_uiless = UILess_SetupSinks(videodata); | |
274 IME_UpdateInputLocale(videodata); | |
275 IME_Disable(videodata, hwnd); | |
276 } | |
277 | |
278 static void | |
279 IME_Enable(SDL_VideoData *videodata, HWND hwnd) | |
280 { | |
281 if (!videodata->ime_initialized || !videodata->ime_hwnd_current) | |
282 return; | |
283 | |
284 if (!videodata->ime_available) { | |
285 IME_Disable(videodata, hwnd); | |
286 return; | |
287 } | |
288 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main) | |
289 ImmAssociateContext(videodata->ime_hwnd_current, videodata->ime_himc); | |
290 | |
291 videodata->ime_enabled = SDL_TRUE; | |
292 IME_UpdateInputLocale(videodata); | |
293 UILess_EnableUIUpdates(videodata); | |
294 } | |
295 | |
296 static void | |
297 IME_Disable(SDL_VideoData *videodata, HWND hwnd) | |
298 { | |
299 if (!videodata->ime_initialized || !videodata->ime_hwnd_current) | |
300 return; | |
301 | |
302 IME_ClearComposition(videodata); | |
303 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main) | |
304 ImmAssociateContext(videodata->ime_hwnd_current, NULL); | |
305 | |
306 videodata->ime_enabled = SDL_FALSE; | |
307 UILess_DisableUIUpdates(videodata); | |
308 } | |
309 | |
310 static void | |
311 IME_Quit(SDL_VideoData *videodata) | |
312 { | |
313 if (!videodata->ime_initialized) | |
314 return; | |
315 | |
316 UILess_ReleaseSinks(videodata); | |
317 if (videodata->ime_hwnd_main) | |
318 ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc); | |
319 | |
320 videodata->ime_hwnd_main = 0; | |
321 videodata->ime_himc = 0; | |
322 if (videodata->ime_himm32) { | |
323 FreeLibrary(videodata->ime_himm32); | |
324 videodata->ime_himm32 = 0; | |
325 } | |
326 if (videodata->ime_threadmgr) { | |
327 videodata->ime_threadmgr->lpVtbl->Release(videodata->ime_threadmgr); | |
328 videodata->ime_threadmgr = 0; | |
329 } | |
330 if (videodata->ime_com_initialized) { | |
331 CoUninitialize(); | |
332 videodata->ime_com_initialized = SDL_FALSE; | |
333 } | |
334 videodata->ime_initialized = SDL_FALSE; | |
335 } | |
336 | |
337 static void | |
338 IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd) | |
339 { | |
340 DWORD id = 0; | |
341 HIMC himc = 0; | |
342 WCHAR buffer[16]; | |
343 WCHAR *s = buffer; | |
344 DWORD len = 0; | |
345 DWORD err = 0; | |
346 BOOL vertical = FALSE; | |
347 UINT maxuilen = 0; | |
348 static OSVERSIONINFOA osversion = {0}; | |
349 if (videodata->ime_uiless) | |
350 return; | |
351 | |
352 videodata->ime_readingstring[0] = 0; | |
353 if (!osversion.dwOSVersionInfoSize) { | |
354 osversion.dwOSVersionInfoSize = sizeof(osversion); | |
355 GetVersionExA(&osversion); | |
356 } | |
357 id = IME_GetId(videodata, 0); | |
358 if (!id) | |
359 return; | |
360 | |
361 himc = ImmGetContext(hwnd); | |
362 if (!himc) | |
363 return; | |
364 | |
365 if (videodata->GetReadingString) { | |
366 len = videodata->GetReadingString(himc, 0, 0, &err, &vertical, &maxuilen); | |
367 if (len) { | |
368 if (len > SDL_arraysize(buffer)) | |
369 len = SDL_arraysize(buffer); | |
370 | |
371 len = videodata->GetReadingString(himc, len, s, &err, &vertical, &maxuilen); | |
372 } | |
373 SDL_wcslcpy(videodata->ime_readingstring, s, len); | |
374 } | |
375 else { | |
376 LPINPUTCONTEXT2 lpimc = videodata->ImmLockIMC(himc); | |
377 LPBYTE p = 0; | |
378 s = 0; | |
379 switch (id) | |
380 { | |
381 case IMEID_CHT_VER42: | |
382 case IMEID_CHT_VER43: | |
383 case IMEID_CHT_VER44: | |
384 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 24); | |
385 if (!p) | |
386 break; | |
387 | |
388 len = *(DWORD *)(p + 7*4 + 32*4); | |
389 s = (WCHAR *)(p + 56); | |
390 break; | |
391 case IMEID_CHT_VER51: | |
392 case IMEID_CHT_VER52: | |
393 case IMEID_CHS_VER53: | |
394 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 4); | |
395 if (!p) | |
396 break; | |
397 | |
398 p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4); | |
399 if (!p) | |
400 break; | |
401 | |
402 len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2); | |
403 s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4); | |
404 break; | |
405 case IMEID_CHS_VER41: | |
406 { | |
407 int offset = (IME_GetId(videodata, 1) >= 0x00000002) ? 8 : 7; | |
408 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + offset * 4); | |
409 if (!p) | |
410 break; | |
411 | |
412 len = *(DWORD *)(p + 7*4 + 16*2*4); | |
413 s = (WCHAR *)(p + 6*4 + 16*2*1); | |
414 } | |
415 break; | |
416 case IMEID_CHS_VER42: | |
417 if (osversion.dwPlatformId != VER_PLATFORM_WIN32_NT) | |
418 break; | |
419 | |
420 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 1*4 + 1*4 + 6*4); | |
421 if (!p) | |
422 break; | |
423 | |
424 len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2); | |
425 s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4); | |
426 break; | |
427 } | |
428 if (s) | |
429 SDL_wcslcpy(videodata->ime_readingstring, s, len + 1); | |
430 | |
431 videodata->ImmUnlockIMCC(lpimc->hPrivate); | |
432 videodata->ImmUnlockIMC(himc); | |
433 } | |
434 ImmReleaseContext(hwnd, himc); | |
435 IME_SendEditingEvent(videodata); | |
436 } | |
437 | |
438 static void | |
439 IME_InputLangChanged(SDL_VideoData *videodata) | |
440 { | |
441 UINT lang = PRIMLANG(); | |
442 HWND hwndime = 0; | |
443 IME_UpdateInputLocale(videodata); | |
444 IME_SetupAPI(videodata); | |
445 if (lang != PRIMLANG()) { | |
446 IME_ClearComposition(videodata); | |
447 } | |
448 hwndime = ImmGetDefaultIMEWnd(videodata->ime_hwnd_current); | |
449 if (hwndime) { | |
450 SendMessageA(hwndime, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0); | |
451 SendMessageA(hwndime, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0); | |
452 } | |
453 } | |
454 | |
455 static DWORD | |
456 IME_GetId(SDL_VideoData *videodata, UINT uIndex) | |
457 { | |
458 static HKL hklprev = 0; | |
459 static DWORD dwRet[2] = {0}; | |
460 DWORD dwVerSize = 0; | |
461 DWORD dwVerHandle = 0; | |
462 LPVOID lpVerBuffer = 0; | |
463 LPVOID lpVerData = 0; | |
464 UINT cbVerData = 0; | |
465 char szTemp[256]; | |
466 HKL hkl = 0; | |
467 DWORD dwLang = 0; | |
468 if (uIndex >= sizeof(dwRet) / sizeof(dwRet[0])) | |
469 return 0; | |
470 | |
471 hkl = videodata->ime_hkl; | |
472 if (hklprev == hkl) | |
473 return dwRet[uIndex]; | |
474 | |
475 hklprev = hkl; | |
476 dwLang = ((DWORD)hkl & 0xffff); | |
477 if (videodata->ime_uiless && LANG() == LANG_CHT) { | |
478 dwRet[0] = IMEID_CHT_VER_VISTA; | |
479 dwRet[1] = 0; | |
480 return dwRet[0]; | |
481 } | |
482 if (hkl != CHT_HKL_NEW_PHONETIC | |
483 && hkl != CHT_HKL_NEW_CHANG_JIE | |
484 && hkl != CHT_HKL_NEW_QUICK | |
485 && hkl != CHT_HKL_HK_CANTONESE | |
486 && hkl != CHS_HKL) { | |
487 dwRet[0] = dwRet[1] = 0; | |
488 return dwRet[uIndex]; | |
489 } | |
490 if (ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1) <= 0) { | |
491 dwRet[0] = dwRet[1] = 0; | |
492 return dwRet[uIndex]; | |
493 } | |
494 if (!videodata->GetReadingString) { | |
495 #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) | |
496 if (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME1, -1) != 2 | |
497 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME2, -1) != 2 | |
498 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME3, -1) != 2 | |
499 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME1, -1) != 2 | |
500 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME2, -1) != 2) { | |
501 dwRet[0] = dwRet[1] = 0; | |
502 return dwRet[uIndex]; | |
503 } | |
504 #undef LCID_INVARIANT | |
505 dwVerSize = GetFileVersionInfoSizeA(szTemp, &dwVerHandle); | |
506 if (dwVerSize) { | |
507 lpVerBuffer = SDL_malloc(dwVerSize); | |
508 if (lpVerBuffer) { | |
509 if (GetFileVersionInfoA(szTemp, dwVerHandle, dwVerSize, lpVerBuffer)) { | |
510 if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) { | |
511 #define pVerFixedInfo ((VS_FIXEDFILEINFO FAR*)lpVerData) | |
512 DWORD dwVer = pVerFixedInfo->dwFileVersionMS; | |
513 dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16; | |
514 if (videodata->GetReadingString || | |
515 dwLang == LANG_CHT && ( | |
516 dwVer == MAKEIMEVERSION(4, 2) || | |
517 dwVer == MAKEIMEVERSION(4, 3) || | |
518 dwVer == MAKEIMEVERSION(4, 4) || | |
519 dwVer == MAKEIMEVERSION(5, 0) || | |
520 dwVer == MAKEIMEVERSION(5, 1) || | |
521 dwVer == MAKEIMEVERSION(5, 2) || | |
522 dwVer == MAKEIMEVERSION(6, 0)) | |
523 || | |
524 dwLang == LANG_CHS && ( | |
525 dwVer == MAKEIMEVERSION(4, 1) || | |
526 dwVer == MAKEIMEVERSION(4, 2) || | |
527 dwVer == MAKEIMEVERSION(5, 3))) { | |
528 dwRet[0] = dwVer | dwLang; | |
529 dwRet[1] = pVerFixedInfo->dwFileVersionLS; | |
530 SDL_free(lpVerBuffer); | |
531 return dwRet[0]; | |
532 } | |
533 #undef pVerFixedInfo | |
534 } | |
535 } | |
536 } | |
537 SDL_free(lpVerBuffer); | |
538 } | |
539 } | |
540 dwRet[0] = dwRet[1] = 0; | |
541 return dwRet[uIndex]; | |
542 } | |
543 | |
544 static void | |
545 IME_SetupAPI(SDL_VideoData *videodata) | |
546 { | |
547 char ime_file[MAX_PATH + 1]; | |
548 HMODULE hime = 0; | |
549 HKL hkl = 0; | |
550 videodata->GetReadingString = 0; | |
551 videodata->ShowReadingWindow = 0; | |
552 if (videodata->ime_uiless) | |
553 return; | |
554 | |
555 hkl = videodata->ime_hkl; | |
556 if (ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1) <= 0) | |
557 return; | |
558 | |
559 hime = LoadLibraryA(ime_file); | |
560 if (!hime) | |
561 return; | |
562 | |
563 videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT)) | |
564 GetProcAddress(hime, "GetReadingString"); | |
565 videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL)) | |
566 GetProcAddress(hime, "ShowReadingWindow"); | |
567 | |
568 if (videodata->ShowReadingWindow) { | |
569 HIMC himc = ImmGetContext(videodata->ime_hwnd_current); | |
570 if (himc) { | |
571 videodata->ShowReadingWindow(himc, FALSE); | |
572 ImmReleaseContext(videodata->ime_hwnd_current, himc); | |
573 } | |
574 } | |
575 } | |
576 | |
577 static void | |
578 IME_SetWindow(SDL_VideoData* videodata, HWND hwnd) | |
579 { | |
580 videodata->ime_hwnd_current = hwnd; | |
581 if (videodata->ime_threadmgr) { | |
582 struct ITfDocumentMgr *document_mgr = 0; | |
583 if (SUCCEEDED(videodata->ime_threadmgr->lpVtbl->AssociateFocus(videodata->ime_threadmgr, hwnd, NULL, &document_mgr))) { | |
584 if (document_mgr) | |
585 document_mgr->lpVtbl->Release(document_mgr); | |
586 } | |
587 } | |
588 } | |
589 | |
590 static void | |
591 IME_UpdateInputLocale(SDL_VideoData *videodata) | |
592 { | |
593 static HKL hklprev = 0; | |
594 videodata->ime_hkl = GetKeyboardLayout(0); | |
595 if (hklprev == videodata->ime_hkl) | |
596 return; | |
597 | |
598 hklprev = videodata->ime_hkl; | |
599 } | |
600 | |
601 static void | |
602 IME_ClearComposition(SDL_VideoData *videodata) | |
603 { | |
604 HIMC himc = 0; | |
605 if (!videodata->ime_initialized) | |
606 return; | |
607 | |
608 himc = ImmGetContext(videodata->ime_hwnd_current); | |
609 if (!himc) | |
610 return; | |
611 | |
612 ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); | |
613 if (videodata->ime_uiless) | |
614 ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR)); | |
615 | |
616 ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0); | |
617 ImmReleaseContext(videodata->ime_hwnd_current, himc); | |
618 SDL_SendEditingText("", 0, 0); | |
619 } | |
620 | |
621 static void | |
622 IME_ClearEditing(SDL_VideoData *videodata) | |
623 { | |
624 | |
625 } | |
626 | |
627 static void | |
628 IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string) | |
629 { | |
630 LONG length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, sizeof(videodata->ime_composition)); | |
631 if (length < 0) | |
632 length = 0; | |
633 | |
634 length /= sizeof(videodata->ime_composition[0]); | |
635 videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0)); | |
636 if (videodata->ime_composition[videodata->ime_cursor] == 0x3000) { | |
637 int i; | |
638 for (i = videodata->ime_cursor + 1; i < length; ++i) | |
639 videodata->ime_composition[i - 1] = videodata->ime_composition[i]; | |
640 | |
641 --length; | |
642 } | |
643 videodata->ime_composition[length] = 0; | |
644 } | |
645 | |
646 static void | |
647 IME_SendInputEvent(SDL_VideoData *videodata) | |
648 { | |
649 char *s = 0; | |
650 s = WIN_StringToUTF8(videodata->ime_composition); | |
651 SDL_SendKeyboardText(s); | |
652 SDL_free(s); | |
653 | |
654 videodata->ime_composition[0] = 0; | |
655 videodata->ime_readingstring[0] = 0; | |
656 videodata->ime_cursor = 0; | |
657 } | |
658 | |
659 static void | |
660 IME_SendEditingEvent(SDL_VideoData *videodata) | |
661 { | |
662 char *s = 0; | |
663 WCHAR buffer[SDL_TEXTEDITINGEVENT_TEXT_SIZE]; | |
664 buffer[0] = 0; | |
665 if (videodata->ime_readingstring[0]) { | |
666 size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor); | |
667 SDL_wcslcpy(buffer, videodata->ime_composition, len + 1); | |
668 SDL_wcslcat(buffer, videodata->ime_readingstring, sizeof(buffer)); | |
669 SDL_wcslcat(buffer, &videodata->ime_composition[len], sizeof(buffer) - len); | |
670 } | |
671 else { | |
672 SDL_wcslcpy(buffer, videodata->ime_composition, sizeof(videodata->ime_composition)); | |
673 } | |
674 s = WIN_StringToUTF8(buffer); | |
675 SDL_SendEditingText(s, videodata->ime_cursor + SDL_wcslen(videodata->ime_readingstring), 0); | |
676 SDL_free(s); | |
677 } | |
678 | |
679 SDL_bool | |
680 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata) | |
681 { | |
682 SDL_bool trap = SDL_FALSE; | |
683 HIMC himc = 0; | |
684 if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled) | |
685 return SDL_FALSE; | |
686 | |
687 switch (msg) | |
688 { | |
689 case WM_INPUTLANGCHANGE: | |
690 //IME_InputLangChanged(videodata); | |
691 break; | |
692 case WM_IME_SETCONTEXT: | |
693 *lParam = 0; | |
694 break; | |
695 case WM_IME_STARTCOMPOSITION: | |
696 trap = SDL_TRUE; | |
697 break; | |
698 case WM_IME_COMPOSITION: | |
699 trap = SDL_TRUE; | |
700 himc = ImmGetContext(hwnd); | |
701 if (*lParam & GCS_RESULTSTR) { | |
702 IME_GetCompositionString(videodata, himc, GCS_RESULTSTR); | |
703 IME_SendInputEvent(videodata); | |
704 } | |
705 if (*lParam & GCS_COMPSTR) { | |
706 if (!videodata->ime_uiless) | |
707 videodata->ime_readingstring[0] = 0; | |
708 | |
709 IME_GetCompositionString(videodata, himc, GCS_COMPSTR); | |
710 IME_SendEditingEvent(videodata); | |
711 } | |
712 ImmReleaseContext(hwnd, himc); | |
713 break; | |
714 case WM_IME_ENDCOMPOSITION: | |
715 videodata->ime_composition[0] = 0; | |
716 videodata->ime_readingstring[0] = 0; | |
717 videodata->ime_cursor = 0; | |
718 SDL_SendEditingText("", 0, 0); | |
719 break; | |
720 case WM_IME_NOTIFY: | |
721 switch (wParam) | |
722 { | |
723 case IMN_SETCONVERSIONMODE: | |
724 case IMN_SETOPENSTATUS: | |
725 IME_UpdateInputLocale(videodata); | |
726 break; | |
727 case IMN_OPENCANDIDATE: | |
728 case IMN_CHANGECANDIDATE: | |
729 trap = SDL_TRUE; | |
730 break; | |
731 case IMN_CLOSECANDIDATE: | |
732 trap = SDL_TRUE; | |
733 break; | |
734 case IMN_PRIVATE: | |
735 { | |
736 DWORD dwId = IME_GetId(videodata, 0); | |
737 IME_GetReadingString(videodata, hwnd); | |
738 switch (dwId) | |
739 { | |
740 case IMEID_CHT_VER42: | |
741 case IMEID_CHT_VER43: | |
742 case IMEID_CHT_VER44: | |
743 case IMEID_CHS_VER41: | |
744 case IMEID_CHS_VER42: | |
745 if (*lParam == 1 || *lParam == 2) | |
746 trap = SDL_TRUE; | |
747 | |
748 break; | |
749 case IMEID_CHT_VER50: | |
750 case IMEID_CHT_VER51: | |
751 case IMEID_CHT_VER52: | |
752 case IMEID_CHT_VER60: | |
753 case IMEID_CHS_VER53: | |
754 if (*lParam == 16 | |
755 || *lParam == 17 | |
756 || *lParam == 26 | |
757 || *lParam == 27 | |
758 || *lParam == 28) | |
759 trap = SDL_TRUE; | |
760 break; | |
761 } | |
762 } | |
763 break; | |
764 default: | |
765 trap = SDL_TRUE; | |
766 break; | |
767 } | |
768 break; | |
769 } | |
770 return trap; | |
771 } | |
772 | |
773 STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink) | |
774 { | |
775 return ++sink->refcount; | |
776 } | |
777 | |
778 STDMETHODIMP_(ULONG)TSFSink_Release(TSFSink *sink) | |
779 { | |
780 --sink->refcount; | |
781 if (sink->refcount == 0) | |
782 { | |
783 SDL_free(sink); | |
784 return 0; | |
785 } | |
786 return sink->refcount; | |
787 } | |
788 | |
789 STDMETHODIMP UIElementSink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv) | |
790 { | |
791 if (!ppv) | |
792 return E_INVALIDARG; | |
793 | |
794 *ppv = 0; | |
795 if (SDL_IsEqualIID(riid, &IID_IUnknown)) | |
796 *ppv = (IUnknown *)sink; | |
797 else if (SDL_IsEqualIID(riid, &IID_ITfUIElementSink)) | |
798 *ppv = (ITfUIElementSink *)sink; | |
799 | |
800 if (*ppv) { | |
801 TSFSink_AddRef(sink); | |
802 return S_OK; | |
803 } | |
804 return E_NOINTERFACE; | |
805 } | |
806 | |
807 ITfUIElement *UILess_GetUIElement(SDL_VideoData *videodata, DWORD dwUIElementId) | |
808 { | |
809 ITfUIElementMgr *puiem = 0; | |
810 ITfUIElement *pelem = 0; | |
811 ITfThreadMgrEx *threadmgrex = videodata->ime_threadmgrex; | |
812 | |
813 if (SUCCEEDED(threadmgrex->lpVtbl->QueryInterface(threadmgrex, &IID_ITfUIElementMgr, (LPVOID *)&puiem))) { | |
814 puiem->lpVtbl->GetUIElement(puiem, dwUIElementId, &pelem); | |
815 puiem->lpVtbl->Release(puiem); | |
816 } | |
817 return pelem; | |
818 } | |
819 | |
820 STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow) | |
821 { | |
822 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); | |
823 ITfReadingInformationUIElement *preading = 0; | |
824 SDL_VideoData *videodata = (SDL_VideoData *)sink->data; | |
825 if (!element) | |
826 return E_INVALIDARG; | |
827 | |
828 *pbShow = FALSE; | |
829 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) { | |
830 BSTR bstr; | |
831 if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) { | |
832 WCHAR *s = (WCHAR *)bstr; | |
833 SysFreeString(bstr); | |
834 } | |
835 preading->lpVtbl->Release(preading); | |
836 } | |
837 return S_OK; | |
838 } | |
839 | |
840 STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId) | |
841 { | |
842 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); | |
843 ITfReadingInformationUIElement *preading = 0; | |
844 SDL_VideoData *videodata = (SDL_VideoData *)sink->data; | |
845 if (!element) | |
846 return E_INVALIDARG; | |
847 | |
848 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) { | |
849 BSTR bstr; | |
850 if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) { | |
851 WCHAR *s = (WCHAR *)bstr; | |
852 SDL_wcslcpy(videodata->ime_readingstring, s, sizeof(videodata->ime_readingstring)); | |
853 IME_SendEditingEvent(videodata); | |
854 SysFreeString(bstr); | |
855 } | |
856 preading->lpVtbl->Release(preading); | |
857 } | |
858 return S_OK; | |
859 } | |
860 | |
861 STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId) | |
862 { | |
863 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); | |
864 ITfReadingInformationUIElement *preading = 0; | |
865 SDL_VideoData *videodata = (SDL_VideoData *)sink->data; | |
866 if (!element) | |
867 return E_INVALIDARG; | |
868 | |
869 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) { | |
870 videodata->ime_readingstring[0] = 0; | |
871 IME_SendEditingEvent(videodata); | |
872 preading->lpVtbl->Release(preading); | |
873 } | |
874 return S_OK; | |
875 } | |
876 | |
877 STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv) | |
878 { | |
879 if (!ppv) | |
880 return E_INVALIDARG; | |
881 | |
882 *ppv = 0; | |
883 if (SDL_IsEqualIID(riid, &IID_IUnknown)) | |
884 *ppv = (IUnknown *)sink; | |
885 else if (SDL_IsEqualIID(riid, &IID_ITfInputProcessorProfileActivationSink)) | |
886 *ppv = (ITfInputProcessorProfileActivationSink *)sink; | |
887 | |
888 if (*ppv) { | |
889 TSFSink_AddRef(sink); | |
890 return S_OK; | |
891 } | |
892 return E_NOINTERFACE; | |
893 } | |
894 | |
895 STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags) | |
896 { | |
897 if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE)) | |
898 IME_InputLangChanged((SDL_VideoData *)sink->data); | |
899 | |
900 return S_OK; | |
901 } | |
902 | |
903 static void *vtUIElementSink[] = { | |
904 (void *)(UIElementSink_QueryInterface), | |
905 (void *)(TSFSink_AddRef), | |
906 (void *)(TSFSink_Release), | |
907 (void *)(UIElementSink_BeginUIElement), | |
908 (void *)(UIElementSink_UpdateUIElement), | |
909 (void *)(UIElementSink_EndUIElement) | |
910 }; | |
911 | |
912 static void *vtIPPASink[] = { | |
913 (void *)(IPPASink_QueryInterface), | |
914 (void *)(TSFSink_AddRef), | |
915 (void *)(TSFSink_Release), | |
916 (void *)(IPPASink_OnActivated) | |
917 }; | |
918 | |
919 static void | |
920 UILess_EnableUIUpdates(SDL_VideoData *videodata) | |
921 { | |
922 ITfSource *source = 0; | |
923 if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie != TF_INVALID_COOKIE) | |
924 return; | |
925 | |
926 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) { | |
927 source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie); | |
928 source->lpVtbl->Release(source); | |
929 } | |
930 } | |
931 | |
932 static void | |
933 UILess_DisableUIUpdates(SDL_VideoData *videodata) | |
934 { | |
935 ITfSource *source = 0; | |
936 if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie == TF_INVALID_COOKIE) | |
937 return; | |
938 | |
939 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) { | |
940 source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie); | |
941 videodata->ime_uielemsinkcookie = TF_INVALID_COOKIE; | |
942 source->lpVtbl->Release(source); | |
943 } | |
944 } | |
945 | |
946 static SDL_bool | |
947 UILess_SetupSinks(SDL_VideoData *videodata) | |
948 { | |
949 TfClientId clientid = 0; | |
950 SDL_bool result = SDL_FALSE; | |
951 ITfSource *source = 0; | |
952 if (FAILED(CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, &videodata->ime_threadmgrex))) | |
953 return SDL_FALSE; | |
954 | |
955 if (FAILED(videodata->ime_threadmgrex->lpVtbl->ActivateEx(videodata->ime_threadmgrex, &clientid, TF_TMAE_UIELEMENTENABLEDONLY))) | |
956 return SDL_FALSE; | |
957 | |
958 videodata->ime_uielemsink = SDL_malloc(sizeof(TSFSink)); | |
959 videodata->ime_ippasink = SDL_malloc(sizeof(TSFSink)); | |
960 | |
961 videodata->ime_uielemsink->lpVtbl = vtUIElementSink; | |
962 videodata->ime_uielemsink->refcount = 1; | |
963 videodata->ime_uielemsink->data = videodata; | |
964 | |
965 videodata->ime_ippasink->lpVtbl = vtIPPASink; | |
966 videodata->ime_ippasink->refcount = 1; | |
967 videodata->ime_ippasink->data = videodata; | |
968 | |
969 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) { | |
970 if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie))) { | |
971 if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfInputProcessorProfileActivationSink, (IUnknown *)videodata->ime_ippasink, &videodata->ime_alpnsinkcookie))) { | |
972 result = SDL_TRUE; | |
973 } | |
974 } | |
975 source->lpVtbl->Release(source); | |
976 } | |
977 return result; | |
978 } | |
979 | |
980 #define SAFE_RELEASE(p) \ | |
981 { \ | |
982 if (p) { \ | |
983 (p)->lpVtbl->Release((p)); \ | |
984 (p) = 0; \ | |
985 } \ | |
986 } | |
987 | |
988 static void | |
989 UILess_ReleaseSinks(SDL_VideoData *videodata) | |
990 { | |
991 ITfSource *source = 0; | |
992 if (videodata->ime_threadmgrex && SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, &source))) { | |
993 source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie); | |
994 source->lpVtbl->UnadviseSink(source, videodata->ime_alpnsinkcookie); | |
995 SAFE_RELEASE(source); | |
996 videodata->ime_threadmgrex->lpVtbl->Deactivate(videodata->ime_threadmgrex); | |
997 SAFE_RELEASE(videodata->ime_threadmgrex); | |
998 TSFSink_Release(videodata->ime_uielemsink); | |
999 videodata->ime_uielemsink = 0; | |
1000 TSFSink_Release(videodata->ime_ippasink); | |
1001 videodata->ime_ippasink = 0; | |
1002 } | |
122 } | 1003 } |
123 | 1004 |
124 /* vi: set ts=4 sw=4 expandtab: */ | 1005 /* vi: set ts=4 sw=4 expandtab: */ |