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