Cleaned up and fixed the Windows keyboard mapping code.
Use KP_PERIOD instead of KP_DECIMAL
Don't remap keys which are always keycode named versions of scancodes
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "SDL_config.h"
23 #if SDL_VIDEO_DRIVER_WINDOWS
25 #include "SDL_windowsvideo.h"
27 #include "../../events/SDL_keyboard_c.h"
28 #include "../../events/scancodes_windows.h"
33 #ifndef SDL_DISABLE_WINDOWS_IME
34 static void IME_Init(SDL_VideoData *videodata, HWND hwnd);
35 static void IME_Enable(SDL_VideoData *videodata, HWND hwnd);
36 static void IME_Disable(SDL_VideoData *videodata, HWND hwnd);
37 static void IME_Quit(SDL_VideoData *videodata);
38 #endif /* !SDL_DISABLE_WINDOWS_IME */
40 #ifndef MAPVK_VK_TO_VSC
41 #define MAPVK_VK_TO_VSC 0
43 #ifndef MAPVK_VSC_TO_VK
44 #define MAPVK_VSC_TO_VK 1
46 #ifndef MAPVK_VK_TO_CHAR
47 #define MAPVK_VK_TO_CHAR 2
50 /* Alphabetic scancodes for PC keyboards */
52 WIN_InitKeyboard(_THIS)
54 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
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;
64 data->ime_composition[0] = 0;
65 data->ime_readingstring[0] = 0;
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;
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;
85 data->GetReadingString = 0;
86 data->ShowReadingWindow = 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;
102 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
103 SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Windows");
104 SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Windows");
111 SDL_Scancode scancode;
112 SDL_Keycode keymap[SDL_NUM_SCANCODES];
114 SDL_GetDefaultKeymap(keymap);
116 for (i = 0; i < SDL_arraysize(windows_scancode_table); i++) {
118 /* Make sure this scancode is a valid character scancode */
119 scancode = windows_scancode_table[i];
120 if (scancode == SDL_SCANCODE_UNKNOWN ) {
124 /* If this key is one of the non-mappable keys, ignore it */
125 /* Don't allow the number keys right above the qwerty row to translate or the top left key (grave/backquote) */
126 /* Not mapping numbers fixes the French layout, giving numeric keycodes for the number keys, which is the expected behavior */
127 if ((keymap[scancode] & SDLK_SCANCODE_MASK) ||
128 scancode == SDL_SCANCODE_GRAVE ||
129 (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0) ) {
133 vk = MapVirtualKey(i, MAPVK_VSC_TO_VK);
135 int ch = (MapVirtualKey( vk, MAPVK_VK_TO_CHAR ) & 0x7FFF);
137 if ( ch >= 'A' && ch <= 'Z' ) {
138 keymap[scancode] = SDLK_a + ( ch - 'A' );
140 keymap[scancode] = ch;
146 SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
150 WIN_QuitKeyboard(_THIS)
152 #ifndef SDL_DISABLE_WINDOWS_IME
153 IME_Quit((SDL_VideoData *)_this->driverdata);
158 WIN_StartTextInput(_THIS)
160 #ifndef SDL_DISABLE_WINDOWS_IME
161 SDL_Window *window = SDL_GetKeyboardFocus();
163 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
164 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
165 SDL_GetWindowSize(window, &videodata->ime_winwidth, &videodata->ime_winheight);
166 IME_Init(videodata, hwnd);
167 IME_Enable(videodata, hwnd);
169 #endif /* !SDL_DISABLE_WINDOWS_IME */
173 WIN_StopTextInput(_THIS)
175 #ifndef SDL_DISABLE_WINDOWS_IME
176 SDL_Window *window = SDL_GetKeyboardFocus();
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);
183 #endif /* !SDL_DISABLE_WINDOWS_IME */
187 WIN_SetTextInputRect(_THIS, SDL_Rect *rect)
189 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
192 SDL_InvalidParamError("rect");
196 videodata->ime_rect = *rect;
199 #ifdef SDL_DISABLE_WINDOWS_IME
203 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
208 void IME_Present(SDL_VideoData *videodata)
216 #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}}
217 DEFINE_GUID(IID_ITfInputProcessorProfileActivationSink, 0x71C6E74E,0x0F28,0x11D8,0xA8,0x2A,0x00,0x06,0x5B,0x84,0x43,0x5C);
218 DEFINE_GUID(IID_ITfUIElementSink, 0xEA1EA136,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
219 DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD, 0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31);
220 DEFINE_GUID(IID_ITfSource, 0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7);
221 DEFINE_GUID(IID_ITfUIElementMgr, 0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
222 DEFINE_GUID(IID_ITfCandidateListUIElement, 0xEA1EA138,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
223 DEFINE_GUID(IID_ITfReadingInformationUIElement, 0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
224 DEFINE_GUID(IID_ITfThreadMgr, 0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E);
225 DEFINE_GUID(CLSID_TF_ThreadMgr, 0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50);
226 DEFINE_GUID(IID_ITfThreadMgrEx, 0x3E90ADE3,0x7594,0x4CB0,0xBB,0x58,0x69,0x62,0x8F,0x5F,0x45,0x8C);
229 #define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
230 #define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
232 #define MAKEIMEVERSION(major,minor) ((DWORD) (((BYTE)(major) << 24) | ((BYTE)(minor) << 16) ))
233 #define IMEID_VER(id) ((id) & 0xffff0000)
234 #define IMEID_LANG(id) ((id) & 0x0000ffff)
236 #define CHT_HKL_DAYI ((HKL)0xE0060404)
237 #define CHT_HKL_NEW_PHONETIC ((HKL)0xE0080404)
238 #define CHT_HKL_NEW_CHANG_JIE ((HKL)0xE0090404)
239 #define CHT_HKL_NEW_QUICK ((HKL)0xE00A0404)
240 #define CHT_HKL_HK_CANTONESE ((HKL)0xE00B0404)
241 #define CHT_IMEFILENAME1 "TINTLGNT.IME"
242 #define CHT_IMEFILENAME2 "CINTLGNT.IME"
243 #define CHT_IMEFILENAME3 "MSTCIPHA.IME"
244 #define IMEID_CHT_VER42 (LANG_CHT | MAKEIMEVERSION(4, 2))
245 #define IMEID_CHT_VER43 (LANG_CHT | MAKEIMEVERSION(4, 3))
246 #define IMEID_CHT_VER44 (LANG_CHT | MAKEIMEVERSION(4, 4))
247 #define IMEID_CHT_VER50 (LANG_CHT | MAKEIMEVERSION(5, 0))
248 #define IMEID_CHT_VER51 (LANG_CHT | MAKEIMEVERSION(5, 1))
249 #define IMEID_CHT_VER52 (LANG_CHT | MAKEIMEVERSION(5, 2))
250 #define IMEID_CHT_VER60 (LANG_CHT | MAKEIMEVERSION(6, 0))
251 #define IMEID_CHT_VER_VISTA (LANG_CHT | MAKEIMEVERSION(7, 0))
253 #define CHS_HKL ((HKL)0xE00E0804)
254 #define CHS_IMEFILENAME1 "PINTLGNT.IME"
255 #define CHS_IMEFILENAME2 "MSSCIPYA.IME"
256 #define IMEID_CHS_VER41 (LANG_CHS | MAKEIMEVERSION(4, 1))
257 #define IMEID_CHS_VER42 (LANG_CHS | MAKEIMEVERSION(4, 2))
258 #define IMEID_CHS_VER53 (LANG_CHS | MAKEIMEVERSION(5, 3))
260 #define LANG() LOWORD((videodata->ime_hkl))
261 #define PRIMLANG() ((WORD)PRIMARYLANGID(LANG()))
262 #define SUBLANG() SUBLANGID(LANG())
264 static void IME_UpdateInputLocale(SDL_VideoData *videodata);
265 static void IME_ClearComposition(SDL_VideoData *videodata);
266 static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd);
267 static void IME_SetupAPI(SDL_VideoData *videodata);
268 static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex);
269 static void IME_SendEditingEvent(SDL_VideoData *videodata);
270 static void IME_DestroyTextures(SDL_VideoData *videodata);
272 #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
273 #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
275 static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata);
276 static void UILess_ReleaseSinks(SDL_VideoData *videodata);
277 static void UILess_EnableUIUpdates(SDL_VideoData *videodata);
278 static void UILess_DisableUIUpdates(SDL_VideoData *videodata);
281 IME_Init(SDL_VideoData *videodata, HWND hwnd)
283 if (videodata->ime_initialized)
286 videodata->ime_hwnd_main = hwnd;
287 if (SUCCEEDED(WIN_CoInitialize())) {
288 videodata->ime_com_initialized = SDL_TRUE;
289 CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgr, (LPVOID *)&videodata->ime_threadmgr);
291 videodata->ime_initialized = SDL_TRUE;
292 videodata->ime_himm32 = SDL_LoadObject("imm32.dll");
293 if (!videodata->ime_himm32) {
294 videodata->ime_available = SDL_FALSE;
297 videodata->ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMC");
298 videodata->ImmUnlockIMC = (BOOL (WINAPI *)(HIMC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMC");
299 videodata->ImmLockIMCC = (LPVOID (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmLockIMCC");
300 videodata->ImmUnlockIMCC = (BOOL (WINAPI *)(HIMCC))SDL_LoadFunction(videodata->ime_himm32, "ImmUnlockIMCC");
302 IME_SetWindow(videodata, hwnd);
303 videodata->ime_himc = ImmGetContext(hwnd);
304 ImmReleaseContext(hwnd, videodata->ime_himc);
305 if (!videodata->ime_himc) {
306 videodata->ime_available = SDL_FALSE;
307 IME_Disable(videodata, hwnd);
310 videodata->ime_available = SDL_TRUE;
311 IME_UpdateInputLocale(videodata);
312 IME_SetupAPI(videodata);
313 videodata->ime_uiless = UILess_SetupSinks(videodata);
314 IME_UpdateInputLocale(videodata);
315 IME_Disable(videodata, hwnd);
319 IME_Enable(SDL_VideoData *videodata, HWND hwnd)
321 if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
324 if (!videodata->ime_available) {
325 IME_Disable(videodata, hwnd);
328 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
329 ImmAssociateContext(videodata->ime_hwnd_current, videodata->ime_himc);
331 videodata->ime_enabled = SDL_TRUE;
332 IME_UpdateInputLocale(videodata);
333 UILess_EnableUIUpdates(videodata);
337 IME_Disable(SDL_VideoData *videodata, HWND hwnd)
339 if (!videodata->ime_initialized || !videodata->ime_hwnd_current)
342 IME_ClearComposition(videodata);
343 if (videodata->ime_hwnd_current == videodata->ime_hwnd_main)
344 ImmAssociateContext(videodata->ime_hwnd_current, (HIMC)0);
346 videodata->ime_enabled = SDL_FALSE;
347 UILess_DisableUIUpdates(videodata);
351 IME_Quit(SDL_VideoData *videodata)
353 if (!videodata->ime_initialized)
356 UILess_ReleaseSinks(videodata);
357 if (videodata->ime_hwnd_main)
358 ImmAssociateContext(videodata->ime_hwnd_main, videodata->ime_himc);
360 videodata->ime_hwnd_main = 0;
361 videodata->ime_himc = 0;
362 if (videodata->ime_himm32) {
363 SDL_UnloadObject(videodata->ime_himm32);
364 videodata->ime_himm32 = 0;
366 if (videodata->ime_threadmgr) {
367 videodata->ime_threadmgr->lpVtbl->Release(videodata->ime_threadmgr);
368 videodata->ime_threadmgr = 0;
370 if (videodata->ime_com_initialized) {
371 WIN_CoUninitialize();
372 videodata->ime_com_initialized = SDL_FALSE;
374 IME_DestroyTextures(videodata);
375 videodata->ime_initialized = SDL_FALSE;
379 IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd)
387 BOOL vertical = FALSE;
389 static OSVERSIONINFOA osversion = {0};
390 if (videodata->ime_uiless)
393 videodata->ime_readingstring[0] = 0;
394 if (!osversion.dwOSVersionInfoSize) {
395 osversion.dwOSVersionInfoSize = sizeof(osversion);
396 GetVersionExA(&osversion);
398 id = IME_GetId(videodata, 0);
402 himc = ImmGetContext(hwnd);
406 if (videodata->GetReadingString) {
407 len = videodata->GetReadingString(himc, 0, 0, &err, &vertical, &maxuilen);
409 if (len > SDL_arraysize(buffer))
410 len = SDL_arraysize(buffer);
412 len = videodata->GetReadingString(himc, len, s, &err, &vertical, &maxuilen);
414 SDL_wcslcpy(videodata->ime_readingstring, s, len);
417 LPINPUTCONTEXT2 lpimc = videodata->ImmLockIMC(himc);
422 case IMEID_CHT_VER42:
423 case IMEID_CHT_VER43:
424 case IMEID_CHT_VER44:
425 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 24);
429 len = *(DWORD *)(p + 7*4 + 32*4);
430 s = (WCHAR *)(p + 56);
432 case IMEID_CHT_VER51:
433 case IMEID_CHT_VER52:
434 case IMEID_CHS_VER53:
435 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 4);
439 p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);
443 len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
444 s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
446 case IMEID_CHS_VER41:
448 int offset = (IME_GetId(videodata, 1) >= 0x00000002) ? 8 : 7;
449 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + offset * 4);
453 len = *(DWORD *)(p + 7*4 + 16*2*4);
454 s = (WCHAR *)(p + 6*4 + 16*2*1);
457 case IMEID_CHS_VER42:
458 if (osversion.dwPlatformId != VER_PLATFORM_WIN32_NT)
461 p = *(LPBYTE *)((LPBYTE)videodata->ImmLockIMCC(lpimc->hPrivate) + 1*4 + 1*4 + 6*4);
465 len = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16*2);
466 s = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
470 SDL_wcslcpy(videodata->ime_readingstring, s, len + 1);
472 videodata->ImmUnlockIMCC(lpimc->hPrivate);
473 videodata->ImmUnlockIMC(himc);
475 ImmReleaseContext(hwnd, himc);
476 IME_SendEditingEvent(videodata);
480 IME_InputLangChanged(SDL_VideoData *videodata)
482 UINT lang = PRIMLANG();
483 IME_UpdateInputLocale(videodata);
484 if (!videodata->ime_uiless)
485 videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
487 IME_SetupAPI(videodata);
488 if (lang != PRIMLANG()) {
489 IME_ClearComposition(videodata);
494 IME_GetId(SDL_VideoData *videodata, UINT uIndex)
496 static HKL hklprev = 0;
497 static DWORD dwRet[2] = {0};
499 DWORD dwVerHandle = 0;
500 LPVOID lpVerBuffer = 0;
501 LPVOID lpVerData = 0;
506 if (uIndex >= sizeof(dwRet) / sizeof(dwRet[0]))
509 hkl = videodata->ime_hkl;
511 return dwRet[uIndex];
514 dwLang = ((DWORD_PTR)hkl & 0xffff);
515 if (videodata->ime_uiless && LANG() == LANG_CHT) {
516 dwRet[0] = IMEID_CHT_VER_VISTA;
520 if (hkl != CHT_HKL_NEW_PHONETIC
521 && hkl != CHT_HKL_NEW_CHANG_JIE
522 && hkl != CHT_HKL_NEW_QUICK
523 && hkl != CHT_HKL_HK_CANTONESE
525 dwRet[0] = dwRet[1] = 0;
526 return dwRet[uIndex];
528 if (ImmGetIMEFileNameA(hkl, szTemp, sizeof(szTemp) - 1) <= 0) {
529 dwRet[0] = dwRet[1] = 0;
530 return dwRet[uIndex];
532 if (!videodata->GetReadingString) {
533 #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
534 if (CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME1, -1) != 2
535 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME2, -1) != 2
536 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHT_IMEFILENAME3, -1) != 2
537 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME1, -1) != 2
538 && CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTemp, -1, CHS_IMEFILENAME2, -1) != 2) {
539 dwRet[0] = dwRet[1] = 0;
540 return dwRet[uIndex];
542 #undef LCID_INVARIANT
543 dwVerSize = GetFileVersionInfoSizeA(szTemp, &dwVerHandle);
545 lpVerBuffer = SDL_malloc(dwVerSize);
547 if (GetFileVersionInfoA(szTemp, dwVerHandle, dwVerSize, lpVerBuffer)) {
548 if (VerQueryValueA(lpVerBuffer, "\\", &lpVerData, &cbVerData)) {
549 #define pVerFixedInfo ((VS_FIXEDFILEINFO FAR*)lpVerData)
550 DWORD dwVer = pVerFixedInfo->dwFileVersionMS;
551 dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16;
552 if ((videodata->GetReadingString) ||
553 ((dwLang == LANG_CHT) && (
554 dwVer == MAKEIMEVERSION(4, 2) ||
555 dwVer == MAKEIMEVERSION(4, 3) ||
556 dwVer == MAKEIMEVERSION(4, 4) ||
557 dwVer == MAKEIMEVERSION(5, 0) ||
558 dwVer == MAKEIMEVERSION(5, 1) ||
559 dwVer == MAKEIMEVERSION(5, 2) ||
560 dwVer == MAKEIMEVERSION(6, 0)))
562 ((dwLang == LANG_CHS) && (
563 dwVer == MAKEIMEVERSION(4, 1) ||
564 dwVer == MAKEIMEVERSION(4, 2) ||
565 dwVer == MAKEIMEVERSION(5, 3)))) {
566 dwRet[0] = dwVer | dwLang;
567 dwRet[1] = pVerFixedInfo->dwFileVersionLS;
568 SDL_free(lpVerBuffer);
575 SDL_free(lpVerBuffer);
578 dwRet[0] = dwRet[1] = 0;
579 return dwRet[uIndex];
583 IME_SetupAPI(SDL_VideoData *videodata)
585 char ime_file[MAX_PATH + 1];
588 videodata->GetReadingString = 0;
589 videodata->ShowReadingWindow = 0;
590 if (videodata->ime_uiless)
593 hkl = videodata->ime_hkl;
594 if (ImmGetIMEFileNameA(hkl, ime_file, sizeof(ime_file) - 1) <= 0)
597 hime = SDL_LoadObject(ime_file);
601 videodata->GetReadingString = (UINT (WINAPI *)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT))
602 SDL_LoadFunction(hime, "GetReadingString");
603 videodata->ShowReadingWindow = (BOOL (WINAPI *)(HIMC, BOOL))
604 SDL_LoadFunction(hime, "ShowReadingWindow");
606 if (videodata->ShowReadingWindow) {
607 HIMC himc = ImmGetContext(videodata->ime_hwnd_current);
609 videodata->ShowReadingWindow(himc, FALSE);
610 ImmReleaseContext(videodata->ime_hwnd_current, himc);
616 IME_SetWindow(SDL_VideoData* videodata, HWND hwnd)
618 videodata->ime_hwnd_current = hwnd;
619 if (videodata->ime_threadmgr) {
620 struct ITfDocumentMgr *document_mgr = 0;
621 if (SUCCEEDED(videodata->ime_threadmgr->lpVtbl->AssociateFocus(videodata->ime_threadmgr, hwnd, NULL, &document_mgr))) {
623 document_mgr->lpVtbl->Release(document_mgr);
629 IME_UpdateInputLocale(SDL_VideoData *videodata)
631 static HKL hklprev = 0;
632 videodata->ime_hkl = GetKeyboardLayout(0);
633 if (hklprev == videodata->ime_hkl)
636 hklprev = videodata->ime_hkl;
637 switch (PRIMLANG()) {
639 videodata->ime_candvertical = SDL_TRUE;
640 if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
641 videodata->ime_candvertical = SDL_FALSE;
645 videodata->ime_candvertical = SDL_TRUE;
648 videodata->ime_candvertical = SDL_FALSE;
654 IME_ClearComposition(SDL_VideoData *videodata)
657 if (!videodata->ime_initialized)
660 himc = ImmGetContext(videodata->ime_hwnd_current);
664 ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
665 if (videodata->ime_uiless)
666 ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
668 ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0);
669 ImmReleaseContext(videodata->ime_hwnd_current, himc);
670 SDL_SendEditingText("", 0, 0);
674 IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
676 LONG length = ImmGetCompositionStringW(himc, string, videodata->ime_composition, sizeof(videodata->ime_composition));
680 length /= sizeof(videodata->ime_composition[0]);
681 videodata->ime_cursor = LOWORD(ImmGetCompositionStringW(himc, GCS_CURSORPOS, 0, 0));
682 if (videodata->ime_composition[videodata->ime_cursor] == 0x3000) {
684 for (i = videodata->ime_cursor + 1; i < length; ++i)
685 videodata->ime_composition[i - 1] = videodata->ime_composition[i];
689 videodata->ime_composition[length] = 0;
693 IME_SendInputEvent(SDL_VideoData *videodata)
696 s = WIN_StringToUTF8(videodata->ime_composition);
697 SDL_SendKeyboardText(s);
700 videodata->ime_composition[0] = 0;
701 videodata->ime_readingstring[0] = 0;
702 videodata->ime_cursor = 0;
706 IME_SendEditingEvent(SDL_VideoData *videodata)
709 WCHAR buffer[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
711 if (videodata->ime_readingstring[0]) {
712 size_t len = SDL_min(SDL_wcslen(videodata->ime_composition), (size_t)videodata->ime_cursor);
713 SDL_wcslcpy(buffer, videodata->ime_composition, len + 1);
714 SDL_wcslcat(buffer, videodata->ime_readingstring, sizeof(buffer));
715 SDL_wcslcat(buffer, &videodata->ime_composition[len], sizeof(buffer) - len);
718 SDL_wcslcpy(buffer, videodata->ime_composition, sizeof(videodata->ime_composition));
720 s = WIN_StringToUTF8(buffer);
721 SDL_SendEditingText(s, videodata->ime_cursor + SDL_wcslen(videodata->ime_readingstring), 0);
726 IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
728 LPWSTR dst = videodata->ime_candidates[i];
729 *dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
730 if (videodata->ime_candvertical)
733 while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
734 *dst++ = *candidate++;
740 IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
742 LPCANDIDATELIST cand_list = 0;
743 DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
745 cand_list = (LPCANDIDATELIST)SDL_malloc(size);
747 size = ImmGetCandidateListW(himc, 0, cand_list, size);
752 videodata->ime_candsel = cand_list->dwSelection;
753 videodata->ime_candcount = cand_list->dwCount;
755 if (LANG() == LANG_CHS && IME_GetId(videodata, 0)) {
756 const UINT maxcandchar = 18;
760 for (; i < videodata->ime_candcount; ++i) {
761 UINT len = SDL_wcslen((LPWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i])) + 1;
762 if (len + cchars > maxcandchar) {
763 if (i > cand_list->dwSelection)
773 videodata->ime_candpgsize = i - page_start;
776 videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
777 page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
779 SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
780 for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++) {
781 LPCWSTR candidate = (LPCWSTR)((DWORD_PTR)cand_list + cand_list->dwOffset[i]);
782 IME_AddCandidate(videodata, j, candidate);
784 if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
785 videodata->ime_candsel = -1;
794 IME_ShowCandidateList(SDL_VideoData *videodata)
796 videodata->ime_dirty = SDL_TRUE;
797 videodata->ime_candlist = SDL_TRUE;
798 IME_DestroyTextures(videodata);
799 IME_SendEditingEvent(videodata);
803 IME_HideCandidateList(SDL_VideoData *videodata)
805 videodata->ime_dirty = SDL_FALSE;
806 videodata->ime_candlist = SDL_FALSE;
807 IME_DestroyTextures(videodata);
808 IME_SendEditingEvent(videodata);
812 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
814 SDL_bool trap = SDL_FALSE;
816 if (!videodata->ime_initialized || !videodata->ime_available || !videodata->ime_enabled)
820 case WM_INPUTLANGCHANGE:
821 IME_InputLangChanged(videodata);
823 case WM_IME_SETCONTEXT:
826 case WM_IME_STARTCOMPOSITION:
829 case WM_IME_COMPOSITION:
831 himc = ImmGetContext(hwnd);
832 if (*lParam & GCS_RESULTSTR) {
833 IME_GetCompositionString(videodata, himc, GCS_RESULTSTR);
834 IME_SendInputEvent(videodata);
836 if (*lParam & GCS_COMPSTR) {
837 if (!videodata->ime_uiless)
838 videodata->ime_readingstring[0] = 0;
840 IME_GetCompositionString(videodata, himc, GCS_COMPSTR);
841 IME_SendEditingEvent(videodata);
843 ImmReleaseContext(hwnd, himc);
845 case WM_IME_ENDCOMPOSITION:
846 videodata->ime_composition[0] = 0;
847 videodata->ime_readingstring[0] = 0;
848 videodata->ime_cursor = 0;
849 SDL_SendEditingText("", 0, 0);
853 case IMN_SETCONVERSIONMODE:
854 case IMN_SETOPENSTATUS:
855 IME_UpdateInputLocale(videodata);
857 case IMN_OPENCANDIDATE:
858 case IMN_CHANGECANDIDATE:
859 if (videodata->ime_uiless)
863 IME_ShowCandidateList(videodata);
864 himc = ImmGetContext(hwnd);
868 IME_GetCandidateList(himc, videodata);
869 ImmReleaseContext(hwnd, himc);
871 case IMN_CLOSECANDIDATE:
873 IME_HideCandidateList(videodata);
877 DWORD dwId = IME_GetId(videodata, 0);
878 IME_GetReadingString(videodata, hwnd);
881 case IMEID_CHT_VER42:
882 case IMEID_CHT_VER43:
883 case IMEID_CHT_VER44:
884 case IMEID_CHS_VER41:
885 case IMEID_CHS_VER42:
886 if (*lParam == 1 || *lParam == 2)
890 case IMEID_CHT_VER50:
891 case IMEID_CHT_VER51:
892 case IMEID_CHT_VER52:
893 case IMEID_CHT_VER60:
894 case IMEID_CHS_VER53:
915 IME_CloseCandidateList(SDL_VideoData *videodata)
917 IME_HideCandidateList(videodata);
918 videodata->ime_candcount = 0;
919 SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
923 UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
932 pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
933 pcandlist->lpVtbl->GetCount(pcandlist, &count);
934 pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
936 videodata->ime_candsel = selection;
937 videodata->ime_candcount = count;
938 IME_ShowCandidateList(videodata);
940 pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
942 UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
944 pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
945 pgstart = idxlist[page];
946 if (page < pgcount - 1)
947 pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
949 pgsize = count - pgstart;
954 videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
955 videodata->ime_candsel = videodata->ime_candsel - pgstart;
957 SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
958 for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++) {
960 if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr))) {
962 IME_AddCandidate(videodata, j, bstr);
967 if (PRIMLANG() == LANG_KOREAN)
968 videodata->ime_candsel = -1;
971 STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
973 return ++sink->refcount;
976 STDMETHODIMP_(ULONG)TSFSink_Release(TSFSink *sink)
979 if (sink->refcount == 0) {
983 return sink->refcount;
986 STDMETHODIMP UIElementSink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
992 if (SDL_IsEqualIID(riid, &IID_IUnknown))
993 *ppv = (IUnknown *)sink;
994 else if (SDL_IsEqualIID(riid, &IID_ITfUIElementSink))
995 *ppv = (ITfUIElementSink *)sink;
998 TSFSink_AddRef(sink);
1001 return E_NOINTERFACE;
1004 ITfUIElement *UILess_GetUIElement(SDL_VideoData *videodata, DWORD dwUIElementId)
1006 ITfUIElementMgr *puiem = 0;
1007 ITfUIElement *pelem = 0;
1008 ITfThreadMgrEx *threadmgrex = videodata->ime_threadmgrex;
1010 if (SUCCEEDED(threadmgrex->lpVtbl->QueryInterface(threadmgrex, &IID_ITfUIElementMgr, (LPVOID *)&puiem))) {
1011 puiem->lpVtbl->GetUIElement(puiem, dwUIElementId, &pelem);
1012 puiem->lpVtbl->Release(puiem);
1017 STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow)
1019 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
1020 ITfReadingInformationUIElement *preading = 0;
1021 ITfCandidateListUIElement *pcandlist = 0;
1022 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1024 return E_INVALIDARG;
1027 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
1029 if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
1030 SysFreeString(bstr);
1032 preading->lpVtbl->Release(preading);
1034 else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1035 videodata->ime_candref++;
1036 UILess_GetCandidateList(videodata, pcandlist);
1037 pcandlist->lpVtbl->Release(pcandlist);
1042 STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
1044 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
1045 ITfReadingInformationUIElement *preading = 0;
1046 ITfCandidateListUIElement *pcandlist = 0;
1047 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1049 return E_INVALIDARG;
1051 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
1053 if (SUCCEEDED(preading->lpVtbl->GetString(preading, &bstr)) && bstr) {
1054 WCHAR *s = (WCHAR *)bstr;
1055 SDL_wcslcpy(videodata->ime_readingstring, s, sizeof(videodata->ime_readingstring));
1056 IME_SendEditingEvent(videodata);
1057 SysFreeString(bstr);
1059 preading->lpVtbl->Release(preading);
1061 else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1062 UILess_GetCandidateList(videodata, pcandlist);
1063 pcandlist->lpVtbl->Release(pcandlist);
1068 STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
1070 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
1071 ITfReadingInformationUIElement *preading = 0;
1072 ITfCandidateListUIElement *pcandlist = 0;
1073 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1075 return E_INVALIDARG;
1077 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
1078 videodata->ime_readingstring[0] = 0;
1079 IME_SendEditingEvent(videodata);
1080 preading->lpVtbl->Release(preading);
1082 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1083 videodata->ime_candref--;
1084 if (videodata->ime_candref == 0)
1085 IME_CloseCandidateList(videodata);
1087 pcandlist->lpVtbl->Release(pcandlist);
1092 STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
1095 return E_INVALIDARG;
1098 if (SDL_IsEqualIID(riid, &IID_IUnknown))
1099 *ppv = (IUnknown *)sink;
1100 else if (SDL_IsEqualIID(riid, &IID_ITfInputProcessorProfileActivationSink))
1101 *ppv = (ITfInputProcessorProfileActivationSink *)sink;
1104 TSFSink_AddRef(sink);
1107 return E_NOINTERFACE;
1110 STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
1112 static const GUID TF_PROFILE_DAYI = { 0x037B2C25, 0x480C, 0x4D7F, { 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A } };
1113 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1114 videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
1115 if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
1116 IME_InputLangChanged((SDL_VideoData *)sink->data);
1118 IME_HideCandidateList(videodata);
1122 static void *vtUIElementSink[] = {
1123 (void *)(UIElementSink_QueryInterface),
1124 (void *)(TSFSink_AddRef),
1125 (void *)(TSFSink_Release),
1126 (void *)(UIElementSink_BeginUIElement),
1127 (void *)(UIElementSink_UpdateUIElement),
1128 (void *)(UIElementSink_EndUIElement)
1131 static void *vtIPPASink[] = {
1132 (void *)(IPPASink_QueryInterface),
1133 (void *)(TSFSink_AddRef),
1134 (void *)(TSFSink_Release),
1135 (void *)(IPPASink_OnActivated)
1139 UILess_EnableUIUpdates(SDL_VideoData *videodata)
1141 ITfSource *source = 0;
1142 if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie != TF_INVALID_COOKIE)
1145 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1146 source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie);
1147 source->lpVtbl->Release(source);
1152 UILess_DisableUIUpdates(SDL_VideoData *videodata)
1154 ITfSource *source = 0;
1155 if (!videodata->ime_threadmgrex || videodata->ime_uielemsinkcookie == TF_INVALID_COOKIE)
1158 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1159 source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
1160 videodata->ime_uielemsinkcookie = TF_INVALID_COOKIE;
1161 source->lpVtbl->Release(source);
1166 UILess_SetupSinks(SDL_VideoData *videodata)
1168 TfClientId clientid = 0;
1169 SDL_bool result = SDL_FALSE;
1170 ITfSource *source = 0;
1171 if (FAILED(CoCreateInstance(&CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, &IID_ITfThreadMgrEx, (LPVOID *)&videodata->ime_threadmgrex)))
1174 if (FAILED(videodata->ime_threadmgrex->lpVtbl->ActivateEx(videodata->ime_threadmgrex, &clientid, TF_TMAE_UIELEMENTENABLEDONLY)))
1177 videodata->ime_uielemsink = SDL_malloc(sizeof(TSFSink));
1178 videodata->ime_ippasink = SDL_malloc(sizeof(TSFSink));
1180 videodata->ime_uielemsink->lpVtbl = vtUIElementSink;
1181 videodata->ime_uielemsink->refcount = 1;
1182 videodata->ime_uielemsink->data = videodata;
1184 videodata->ime_ippasink->lpVtbl = vtIPPASink;
1185 videodata->ime_ippasink->refcount = 1;
1186 videodata->ime_ippasink->data = videodata;
1188 if (SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1189 if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfUIElementSink, (IUnknown *)videodata->ime_uielemsink, &videodata->ime_uielemsinkcookie))) {
1190 if (SUCCEEDED(source->lpVtbl->AdviseSink(source, &IID_ITfInputProcessorProfileActivationSink, (IUnknown *)videodata->ime_ippasink, &videodata->ime_alpnsinkcookie))) {
1194 source->lpVtbl->Release(source);
1199 #define SAFE_RELEASE(p) \
1202 (p)->lpVtbl->Release((p)); \
1208 UILess_ReleaseSinks(SDL_VideoData *videodata)
1210 ITfSource *source = 0;
1211 if (videodata->ime_threadmgrex && SUCCEEDED(videodata->ime_threadmgrex->lpVtbl->QueryInterface(videodata->ime_threadmgrex, &IID_ITfSource, (LPVOID *)&source))) {
1212 source->lpVtbl->UnadviseSink(source, videodata->ime_uielemsinkcookie);
1213 source->lpVtbl->UnadviseSink(source, videodata->ime_alpnsinkcookie);
1214 SAFE_RELEASE(source);
1215 videodata->ime_threadmgrex->lpVtbl->Deactivate(videodata->ime_threadmgrex);
1216 SAFE_RELEASE(videodata->ime_threadmgrex);
1217 TSFSink_Release(videodata->ime_uielemsink);
1218 videodata->ime_uielemsink = 0;
1219 TSFSink_Release(videodata->ime_ippasink);
1220 videodata->ime_ippasink = 0;
1225 StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
1228 BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
1232 infoHeader->biSize = sizeof(BITMAPINFOHEADER);
1233 infoHeader->biWidth = width;
1234 infoHeader->biHeight = -1 * SDL_abs(height);
1235 infoHeader->biPlanes = 1;
1236 infoHeader->biBitCount = 32;
1237 infoHeader->biCompression = BI_RGB;
1238 *hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
1240 SelectObject(hdc, *hhbm);
1246 StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
1248 if (hhbm && *hhbm) {
1249 DeleteObject(*hhbm);
1254 /* This draws only within the specified area and fills the entire region. */
1256 DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
1258 /* The case of no pen (PenSize = 0) is automatically taken care of. */
1259 const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
1260 left += pensize / 2;
1263 bottom -= penadjust;
1264 Rectangle(hdc, left, top, right, bottom);
1268 IME_DestroyTextures(SDL_VideoData *videodata)
1272 #define SDL_swap(a,b) { \
1279 IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
1281 int left, top, right, bottom;
1282 SDL_bool ok = SDL_FALSE;
1283 int winw = videodata->ime_winwidth;
1284 int winh = videodata->ime_winheight;
1287 left = videodata->ime_rect.x;
1288 top = videodata->ime_rect.y + videodata->ime_rect.h;
1289 right = left + size.cx;
1290 bottom = top + size.cy;
1291 if (right >= winw) {
1292 left -= right - winw;
1300 left = videodata->ime_rect.x;
1301 top = videodata->ime_rect.y - size.cy;
1302 right = left + size.cx;
1303 bottom = videodata->ime_rect.y;
1304 if (right >= winw) {
1305 left -= right - winw;
1314 left = videodata->ime_rect.x + size.cx;
1316 right = left + size.cx;
1324 left = videodata->ime_rect.x - size.cx;
1326 right = videodata->ime_rect.x;
1332 /* Window too small, show at (0,0) */
1340 videodata->ime_candlistrect.x = left;
1341 videodata->ime_candlistrect.y = top;
1342 videodata->ime_candlistrect.w = right - left;
1343 videodata->ime_candlistrect.h = bottom - top;
1347 IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
1351 SIZE candsizes[MAX_CANDLIST];
1352 SIZE maxcandsize = {0};
1355 const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
1356 SDL_bool vertical = videodata->ime_candvertical;
1358 const int listborder = 1;
1359 const int listpadding = 0;
1360 const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
1361 const int listfillcolor = RGB(255, 255, 255);
1363 const int candborder = 1;
1364 const int candpadding = 0;
1365 const int candmargin = 1;
1366 const COLORREF candbordercolor = RGB(255, 255, 255);
1367 const COLORREF candfillcolor = RGB(255, 255, 255);
1368 const COLORREF candtextcolor = RGB(0, 0, 0);
1369 const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
1370 const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
1371 const COLORREF seltextcolor = RGB(0, 0, 0);
1372 const int horzcandspacing = 5;
1374 HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1375 HBRUSH listbrush = CreateSolidBrush(listfillcolor);
1376 HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1377 HBRUSH candbrush = CreateSolidBrush(candfillcolor);
1378 HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1379 HBRUSH selbrush = CreateSolidBrush(selfillcolor);
1380 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"));
1382 SetBkMode(hdc, TRANSPARENT);
1383 SelectObject(hdc, font);
1385 for (i = 0; i < candcount; ++i) {
1386 const WCHAR *s = videodata->ime_candidates[i];
1390 GetTextExtentPoint32W(hdc, s, SDL_wcslen(s), &candsizes[i]);
1391 maxcandsize.cx = SDL_max(maxcandsize.cx, candsizes[i].cx);
1392 maxcandsize.cy = SDL_max(maxcandsize.cy, candsizes[i].cy);
1407 ((candcount + 1) * candmargin) +
1408 (candcount * candborder * 2) +
1409 (candcount * candpadding * 2) +
1410 (candcount * maxcandsize.cy)
1417 ((candcount + 1) * candmargin) +
1418 (candcount * candborder * 2) +
1419 (candcount * candpadding * 2) +
1420 ((candcount - 1) * horzcandspacing);
1423 for (i = 0; i < candcount; ++i)
1424 size.cx += candsizes[i].cx;
1436 bits = StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
1438 SelectObject(hdc, listpen);
1439 SelectObject(hdc, listbrush);
1440 DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
1442 SelectObject(hdc, candpen);
1443 SelectObject(hdc, candbrush);
1444 SetTextColor(hdc, candtextcolor);
1445 SetBkMode(hdc, TRANSPARENT);
1447 for (i = 0; i < candcount; ++i) {
1448 const WCHAR *s = videodata->ime_candidates[i];
1449 int left, top, right, bottom;
1454 left = listborder + listpadding + candmargin;
1455 top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
1456 right = size.cx - listborder - listpadding - candmargin;
1457 bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
1460 left = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * horzcandspacing);
1462 for (j = 0; j < i; ++j)
1463 left += candsizes[j].cx;
1465 top = listborder + listpadding + candmargin;
1466 right = left + candsizes[i].cx + (candpadding * 2) + (candborder * 2);
1467 bottom = size.cy - listborder - listpadding - candmargin;
1470 if (i == videodata->ime_candsel) {
1471 SelectObject(hdc, selpen);
1472 SelectObject(hdc, selbrush);
1473 SetTextColor(hdc, seltextcolor);
1476 SelectObject(hdc, candpen);
1477 SelectObject(hdc, candbrush);
1478 SetTextColor(hdc, candtextcolor);
1481 DrawRect(hdc, left, top, right, bottom, candborder);
1482 ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, SDL_wcslen(s), NULL);
1484 StopDrawToBitmap(hdc, &hbm);
1486 DeleteObject(listpen);
1487 DeleteObject(listbrush);
1488 DeleteObject(candpen);
1489 DeleteObject(candbrush);
1490 DeleteObject(selpen);
1491 DeleteObject(selbrush);
1494 IME_PositionCandidateList(videodata, size);
1498 IME_Render(SDL_VideoData *videodata)
1500 HDC hdc = CreateCompatibleDC(NULL);
1502 if (videodata->ime_candlist)
1503 IME_RenderCandidateList(videodata, hdc);
1507 videodata->ime_dirty = SDL_FALSE;
1510 void IME_Present(SDL_VideoData *videodata)
1512 if (videodata->ime_dirty)
1513 IME_Render(videodata);
1515 // FIXME: Need to show the IME bitmap
1518 #endif /* SDL_DISABLE_WINDOWS_IME */
1520 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
1522 /* vi: set ts=4 sw=4 expandtab: */