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: */