a07a6c6965afbbf72a88aec11549a9ab3e4113c5
[reactos.git] / dll / win32 / imm32 / imm.c
1 /*
2 * PROJECT: ReactOS IMM32
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Implementing Far-Eastern languages input
5 * COPYRIGHT: Copyright 1998 Patrik Stridvall
6 * Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
7 * Copyright 2017 James Tabor <james.tabor@reactos.org>
8 * Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
9 * Copyright 2020 Oleg Dubinskiy <oleg.dubinskij2013@yandex.ua>
10 * Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
11 */
12
13 #include "precomp.h"
14
15 WINE_DEFAULT_DEBUG_CHANNEL(imm);
16
17 HMODULE g_hImm32Inst = NULL;
18 PSERVERINFO g_psi = NULL;
19 SHAREDINFO g_SharedInfo = { NULL };
20 BYTE g_bClientRegd = FALSE;
21
22 static BOOL APIENTRY Imm32InitInstance(HMODULE hMod)
23 {
24 NTSTATUS status;
25
26 if (hMod)
27 g_hImm32Inst = hMod;
28
29 if (g_bClientRegd)
30 return TRUE;
31
32 status = RtlInitializeCriticalSection(&g_csImeDpi);
33 if (NT_ERROR(status))
34 return FALSE;
35
36 g_bClientRegd = TRUE;
37 return TRUE;
38 }
39
40 /***********************************************************************
41 * ImmRegisterClient(IMM32.@)
42 * ( Undocumented, called from user32.dll )
43 */
44 BOOL WINAPI ImmRegisterClient(PSHAREDINFO ptr, HINSTANCE hMod)
45 {
46 g_SharedInfo = *ptr;
47 g_psi = g_SharedInfo.psi;
48 return Imm32InitInstance(hMod);
49 }
50
51 /***********************************************************************
52 * ImmLoadLayout (IMM32.@)
53 */
54 HKL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx)
55 {
56 DWORD cbData;
57 UNICODE_STRING UnicodeString;
58 HKEY hLayoutKey = NULL, hLayoutsKey = NULL;
59 LONG error;
60 NTSTATUS Status;
61 WCHAR szLayout[MAX_PATH];
62
63 TRACE("(%p, %p)\n", hKL, pImeInfoEx);
64
65 if (IS_IME_HKL(hKL) ||
66 !g_psi || !(g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED) ||
67 ((PW32CLIENTINFO)NtCurrentTeb()->Win32ClientInfo)->W32ClientInfo[0] & 2)
68 {
69 UnicodeString.Buffer = szLayout;
70 UnicodeString.MaximumLength = sizeof(szLayout);
71 Status = RtlIntegerToUnicodeString((DWORD_PTR)hKL, 16, &UnicodeString);
72 if (!NT_SUCCESS(Status))
73 return NULL;
74
75 error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hLayoutsKey);
76 if (error)
77 return NULL;
78
79 error = RegOpenKeyW(hLayoutsKey, szLayout, &hLayoutKey);
80 }
81 else
82 {
83 error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_IMM, &hLayoutKey);
84 }
85
86 if (error)
87 {
88 ERR("RegOpenKeyW error: 0x%08lX\n", error);
89 hKL = NULL;
90 }
91 else
92 {
93 cbData = sizeof(pImeInfoEx->wszImeFile);
94 error = RegQueryValueExW(hLayoutKey, L"Ime File", 0, 0,
95 (LPBYTE)pImeInfoEx->wszImeFile, &cbData);
96 if (error)
97 hKL = NULL;
98 }
99
100 RegCloseKey(hLayoutKey);
101 if (hLayoutsKey)
102 RegCloseKey(hLayoutsKey);
103 return hKL;
104 }
105
106 typedef struct _tagImmHkl
107 {
108 struct list entry;
109 HKL hkl;
110 HMODULE hIME;
111 IMEINFO imeInfo;
112 WCHAR imeClassName[17]; /* 16 character max */
113 ULONG uSelected;
114 HWND UIWnd;
115
116 /* Function Pointers */
117 BOOL (WINAPI *pImeInquire)(IMEINFO *, WCHAR *, const WCHAR *);
118 BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *);
119 BOOL (WINAPI *pImeDestroy)(UINT);
120 LRESULT (WINAPI *pImeEscape)(HIMC, UINT, void *);
121 BOOL (WINAPI *pImeSelect)(HIMC, BOOL);
122 BOOL (WINAPI *pImeSetActiveContext)(HIMC, BOOL);
123 UINT (WINAPI *pImeToAsciiEx)(UINT, UINT, const BYTE *, DWORD *, UINT, HIMC);
124 BOOL (WINAPI *pNotifyIME)(HIMC, DWORD, DWORD, DWORD);
125 BOOL (WINAPI *pImeRegisterWord)(const WCHAR *, DWORD, const WCHAR *);
126 BOOL (WINAPI *pImeUnregisterWord)(const WCHAR *, DWORD, const WCHAR *);
127 UINT (WINAPI *pImeEnumRegisterWord)(REGISTERWORDENUMPROCW, const WCHAR *, DWORD, const WCHAR *, void *);
128 BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void *, DWORD, const void *, DWORD);
129 DWORD (WINAPI *pImeConversionList)(HIMC, const WCHAR *, CANDIDATELIST *, DWORD, UINT);
130 BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE *);
131 UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, STYLEBUFW *);
132 DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, IMEMENUITEMINFOW *, IMEMENUITEMINFOW *, DWORD);
133 } ImmHkl;
134
135 typedef struct tagInputContextData
136 {
137 DWORD dwLock;
138 INPUTCONTEXT IMC;
139 DWORD threadID;
140
141 ImmHkl *immKbd;
142 UINT lastVK;
143 BOOL threadDefault;
144 DWORD magic;
145 } InputContextData;
146
147 #define WINE_IMC_VALID_MAGIC 0x56434D49
148
149 typedef struct _tagIMMThreadData
150 {
151 struct list entry;
152 DWORD threadID;
153 HIMC defaultContext;
154 HWND hwndDefault;
155 BOOL disableIME;
156 DWORD windowRefs;
157 } IMMThreadData;
158
159 static struct list ImmHklList = LIST_INIT(ImmHklList);
160 static struct list ImmThreadDataList = LIST_INIT(ImmThreadDataList);
161
162 static const WCHAR szwWineIMCProperty[] = {'W','i','n','e','I','m','m','H','I','M','C','P','r','o','p','e','r','t','y',0};
163
164 static const WCHAR szImeFileW[] = {'I','m','e',' ','F','i','l','e',0};
165 static const WCHAR szLayoutTextW[] = {'L','a','y','o','u','t',' ','T','e','x','t',0};
166 static const WCHAR szImeRegFmt[] = {'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s','\\','%','0','8','l','x',0};
167
168 static inline BOOL is_himc_ime_unicode(const InputContextData *data)
169 {
170 return !!(data->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE);
171 }
172
173 static inline BOOL is_kbd_ime_unicode(const ImmHkl *hkl)
174 {
175 return !!(hkl->imeInfo.fdwProperty & IME_PROP_UNICODE);
176 }
177
178 static InputContextData* get_imc_data(HIMC hIMC);
179
180 static HMODULE load_graphics_driver(void)
181 {
182 static const WCHAR display_device_guid_propW[] = {
183 '_','_','w','i','n','e','_','d','i','s','p','l','a','y','_',
184 'd','e','v','i','c','e','_','g','u','i','d',0 };
185 static const WCHAR key_pathW[] = {
186 'S','y','s','t','e','m','\\',
187 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
188 'C','o','n','t','r','o','l','\\',
189 'V','i','d','e','o','\\','{',0};
190 static const WCHAR displayW[] = {'}','\\','0','0','0','0',0};
191 static const WCHAR driverW[] = {'G','r','a','p','h','i','c','s','D','r','i','v','e','r',0};
192
193 HMODULE ret = 0;
194 HKEY hkey;
195 DWORD size;
196 WCHAR path[MAX_PATH];
197 WCHAR key[ARRAY_SIZE( key_pathW ) + ARRAY_SIZE( displayW ) + 40];
198 UINT guid_atom = HandleToULong( GetPropW( GetDesktopWindow(), display_device_guid_propW ));
199
200 if (!guid_atom) return 0;
201 memcpy( key, key_pathW, sizeof(key_pathW) );
202 if (!GlobalGetAtomNameW( guid_atom, key + lstrlenW(key), 40 )) return 0;
203 lstrcatW( key, displayW );
204 if (RegOpenKeyW( HKEY_LOCAL_MACHINE, key, &hkey )) return 0;
205 size = sizeof(path);
206 if (!RegQueryValueExW( hkey, driverW, NULL, NULL, (BYTE *)path, &size )) ret = LoadLibraryW( path );
207 RegCloseKey( hkey );
208 TRACE( "%s %p\n", debugstr_w(path), ret );
209 return ret;
210 }
211
212 /* ImmHkl loading and freeing */
213 #define LOAD_FUNCPTR(f) if((ptr->p##f = (LPVOID)GetProcAddress(ptr->hIME, #f)) == NULL){WARN("Can't find function %s in ime\n", #f);}
214 static ImmHkl *IMM_GetImmHkl(HKL hkl)
215 {
216 ImmHkl *ptr;
217 WCHAR filename[MAX_PATH];
218
219 TRACE("Seeking ime for keyboard %p\n",hkl);
220
221 LIST_FOR_EACH_ENTRY(ptr, &ImmHklList, ImmHkl, entry)
222 {
223 if (ptr->hkl == hkl)
224 return ptr;
225 }
226 /* not found... create it */
227
228 ptr = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ImmHkl));
229
230 ptr->hkl = hkl;
231 if (ImmGetIMEFileNameW(hkl, filename, MAX_PATH)) ptr->hIME = LoadLibraryW(filename);
232 if (!ptr->hIME) ptr->hIME = load_graphics_driver();
233 if (ptr->hIME)
234 {
235 LOAD_FUNCPTR(ImeInquire);
236 if (!ptr->pImeInquire || !ptr->pImeInquire(&ptr->imeInfo, ptr->imeClassName, NULL))
237 {
238 FreeLibrary(ptr->hIME);
239 ptr->hIME = NULL;
240 }
241 else
242 {
243 LOAD_FUNCPTR(ImeDestroy);
244 LOAD_FUNCPTR(ImeSelect);
245 if (!ptr->pImeSelect || !ptr->pImeDestroy)
246 {
247 FreeLibrary(ptr->hIME);
248 ptr->hIME = NULL;
249 }
250 else
251 {
252 LOAD_FUNCPTR(ImeConfigure);
253 LOAD_FUNCPTR(ImeEscape);
254 LOAD_FUNCPTR(ImeSetActiveContext);
255 LOAD_FUNCPTR(ImeToAsciiEx);
256 LOAD_FUNCPTR(NotifyIME);
257 LOAD_FUNCPTR(ImeRegisterWord);
258 LOAD_FUNCPTR(ImeUnregisterWord);
259 LOAD_FUNCPTR(ImeEnumRegisterWord);
260 LOAD_FUNCPTR(ImeSetCompositionString);
261 LOAD_FUNCPTR(ImeConversionList);
262 LOAD_FUNCPTR(ImeProcessKey);
263 LOAD_FUNCPTR(ImeGetRegisterWordStyle);
264 LOAD_FUNCPTR(ImeGetImeMenuItems);
265 /* make sure our classname is WCHAR */
266 if (!is_kbd_ime_unicode(ptr))
267 {
268 WCHAR bufW[17];
269 MultiByteToWideChar(CP_ACP, 0, (LPSTR)ptr->imeClassName,
270 -1, bufW, 17);
271 lstrcpyW(ptr->imeClassName, bufW);
272 }
273 }
274 }
275 }
276 list_add_head(&ImmHklList,&ptr->entry);
277
278 return ptr;
279 }
280 #undef LOAD_FUNCPTR
281
282 static InputContextData* get_imc_data(HIMC hIMC)
283 {
284 InputContextData *data = (InputContextData *)hIMC;
285
286 if (hIMC == NULL)
287 return NULL;
288
289 if(IsBadReadPtr(data, sizeof(InputContextData)) || data->magic != WINE_IMC_VALID_MAGIC)
290 {
291 SetLastError(ERROR_INVALID_HANDLE);
292 return NULL;
293 }
294 return data;
295 }
296
297 static HIMC get_default_context( HWND hwnd )
298 {
299 FIXME("Don't use this function\n");
300 return FALSE;
301 }
302
303 static BOOL IMM_IsCrossThreadAccess(HWND hWnd, HIMC hIMC)
304 {
305 InputContextData *data;
306
307 if (hWnd)
308 {
309 DWORD thread = GetWindowThreadProcessId(hWnd, NULL);
310 if (thread != GetCurrentThreadId()) return TRUE;
311 }
312 data = get_imc_data(hIMC);
313 if (data && data->threadID != GetCurrentThreadId())
314 return TRUE;
315
316 return FALSE;
317 }
318
319 /***********************************************************************
320 * ImmAssociateContext (IMM32.@)
321 */
322 HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
323 {
324 HIMC old = NULL;
325 InputContextData *data = get_imc_data(hIMC);
326
327 TRACE("(%p, %p):\n", hWnd, hIMC);
328
329 if(hIMC && !data)
330 return NULL;
331
332 /*
333 * If already associated just return
334 */
335 if (hIMC && data->IMC.hWnd == hWnd)
336 return hIMC;
337
338 if (hIMC && IMM_IsCrossThreadAccess(hWnd, hIMC))
339 return NULL;
340
341 if (hWnd)
342 {
343 HIMC defaultContext = get_default_context( hWnd );
344 old = RemovePropW(hWnd,szwWineIMCProperty);
345
346 if (old == NULL)
347 old = defaultContext;
348 else if (old == (HIMC)-1)
349 old = NULL;
350
351 if (hIMC != defaultContext)
352 {
353 if (hIMC == NULL) /* Meaning disable imm for that window*/
354 SetPropW(hWnd,szwWineIMCProperty,(HANDLE)-1);
355 else
356 SetPropW(hWnd,szwWineIMCProperty,hIMC);
357 }
358
359 if (old)
360 {
361 InputContextData *old_data = (InputContextData *)old;
362 if (old_data->IMC.hWnd == hWnd)
363 old_data->IMC.hWnd = NULL;
364 }
365 }
366
367 if (!hIMC)
368 return old;
369
370 if(GetActiveWindow() == data->IMC.hWnd)
371 {
372 SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, FALSE, ISC_SHOWUIALL);
373 data->IMC.hWnd = hWnd;
374 SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, TRUE, ISC_SHOWUIALL);
375 }
376
377 return old;
378 }
379
380 /*
381 * Helper function for ImmAssociateContextEx
382 */
383 static BOOL CALLBACK _ImmAssociateContextExEnumProc(HWND hwnd, LPARAM lParam)
384 {
385 HIMC hImc = (HIMC)lParam;
386 ImmAssociateContext(hwnd,hImc);
387 return TRUE;
388 }
389
390 /***********************************************************************
391 * ImmAssociateContextEx (IMM32.@)
392 */
393 BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
394 {
395 TRACE("(%p, %p, 0x%x):\n", hWnd, hIMC, dwFlags);
396
397 if (!hWnd)
398 return FALSE;
399
400 switch (dwFlags)
401 {
402 case 0:
403 ImmAssociateContext(hWnd,hIMC);
404 return TRUE;
405 case IACE_DEFAULT:
406 {
407 HIMC defaultContext = get_default_context( hWnd );
408 if (!defaultContext) return FALSE;
409 ImmAssociateContext(hWnd,defaultContext);
410 return TRUE;
411 }
412 case IACE_IGNORENOCONTEXT:
413 if (GetPropW(hWnd,szwWineIMCProperty))
414 ImmAssociateContext(hWnd,hIMC);
415 return TRUE;
416 case IACE_CHILDREN:
417 EnumChildWindows(hWnd,_ImmAssociateContextExEnumProc,(LPARAM)hIMC);
418 return TRUE;
419 default:
420 FIXME("Unknown dwFlags 0x%x\n",dwFlags);
421 return FALSE;
422 }
423 }
424
425 /***********************************************************************
426 * ImmCreateContext (IMM32.@)
427 */
428 HIMC WINAPI ImmCreateContext(void)
429 {
430 PCLIENTIMC pClientImc;
431 HIMC hIMC;
432
433 TRACE("()\n");
434
435 if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
436 return NULL;
437
438 pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
439 if (pClientImc == NULL)
440 return NULL;
441
442 hIMC = NtUserCreateInputContext(pClientImc);
443 if (hIMC == NULL)
444 {
445 HeapFree(g_hImm32Heap, 0, pClientImc);
446 return NULL;
447 }
448
449 RtlInitializeCriticalSection(&pClientImc->cs);
450
451 // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
452 pClientImc->unknown = NtUserGetThreadState(13);
453
454 return hIMC;
455 }
456
457 static VOID APIENTRY Imm32CleanupContextExtra(LPINPUTCONTEXT pIC)
458 {
459 FIXME("We have to do something do here");
460 }
461
462 static PCLIENTIMC APIENTRY Imm32FindClientImc(HIMC hIMC)
463 {
464 // FIXME
465 return NULL;
466 }
467
468 BOOL APIENTRY Imm32CleanupContext(HIMC hIMC, HKL hKL, BOOL bKeep)
469 {
470 PIMEDPI pImeDpi;
471 LPINPUTCONTEXT pIC;
472 PCLIENTIMC pClientImc;
473
474 if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32) || hIMC == NULL)
475 return FALSE;
476
477 FIXME("We have do something to do here\n");
478 pClientImc = Imm32FindClientImc(hIMC);
479 if (!pClientImc)
480 return FALSE;
481
482 if (pClientImc->hImc == NULL)
483 {
484 pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
485 ImmUnlockClientImc(pClientImc);
486 if (!bKeep)
487 return NtUserDestroyInputContext(hIMC);
488 return TRUE;
489 }
490
491 pIC = ImmLockIMC(hIMC);
492 if (pIC == NULL)
493 {
494 ImmUnlockClientImc(pClientImc);
495 return FALSE;
496 }
497
498 FIXME("We have do something to do here\n");
499
500 if (pClientImc->hKL == hKL)
501 {
502 pImeDpi = ImmLockImeDpi(hKL);
503 if (pImeDpi != NULL)
504 {
505 if (IS_IME_HKL(hKL))
506 {
507 pImeDpi->ImeSelect(hIMC, FALSE);
508 }
509 else if (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
510 {
511 FIXME("We have do something to do here\n");
512 }
513 ImmUnlockImeDpi(pImeDpi);
514 }
515 pClientImc->hKL = NULL;
516 }
517
518 ImmDestroyIMCC(pIC->hPrivate);
519 ImmDestroyIMCC(pIC->hMsgBuf);
520 ImmDestroyIMCC(pIC->hGuideLine);
521 ImmDestroyIMCC(pIC->hCandInfo);
522 ImmDestroyIMCC(pIC->hCompStr);
523
524 Imm32CleanupContextExtra(pIC);
525
526 ImmUnlockIMC(hIMC);
527
528 pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
529 ImmUnlockClientImc(pClientImc);
530
531 if (!bKeep)
532 return NtUserDestroyInputContext(hIMC);
533
534 return TRUE;
535 }
536
537 /***********************************************************************
538 * ImmDestroyContext (IMM32.@)
539 */
540 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
541 {
542 HKL hKL;
543
544 TRACE("(%p)\n", hIMC);
545
546 if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
547 return FALSE;
548
549 if (Imm32IsCrossThreadAccess(hIMC))
550 return FALSE;
551
552 hKL = GetKeyboardLayout(0);
553 return Imm32CleanupContext(hIMC, hKL, FALSE);
554 }
555
556 static inline BOOL EscapeRequiresWA(UINT uEscape)
557 {
558 if (uEscape == IME_ESC_GET_EUDC_DICTIONARY ||
559 uEscape == IME_ESC_SET_EUDC_DICTIONARY ||
560 uEscape == IME_ESC_IME_NAME ||
561 uEscape == IME_ESC_GETHELPFILENAME)
562 return TRUE;
563 return FALSE;
564 }
565
566 /***********************************************************************
567 * ImmEscapeA (IMM32.@)
568 */
569 LRESULT WINAPI ImmEscapeA(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID lpData)
570 {
571 ImmHkl *immHkl = IMM_GetImmHkl(hKL);
572 TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData);
573
574 if (immHkl->hIME && immHkl->pImeEscape)
575 {
576 if (!EscapeRequiresWA(uEscape) || !is_kbd_ime_unicode(immHkl))
577 return immHkl->pImeEscape(hIMC,uEscape,lpData);
578 else
579 {
580 WCHAR buffer[81]; /* largest required buffer should be 80 */
581 LRESULT rc;
582 if (uEscape == IME_ESC_SET_EUDC_DICTIONARY)
583 {
584 MultiByteToWideChar(CP_ACP,0,lpData,-1,buffer,81);
585 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
586 }
587 else
588 {
589 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
590 WideCharToMultiByte(CP_ACP,0,buffer,-1,lpData,80, NULL, NULL);
591 }
592 return rc;
593 }
594 }
595 else
596 return 0;
597 }
598
599 /***********************************************************************
600 * ImmEscapeW (IMM32.@)
601 */
602 LRESULT WINAPI ImmEscapeW(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID lpData)
603 {
604 ImmHkl *immHkl = IMM_GetImmHkl(hKL);
605 TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData);
606
607 if (immHkl->hIME && immHkl->pImeEscape)
608 {
609 if (!EscapeRequiresWA(uEscape) || is_kbd_ime_unicode(immHkl))
610 return immHkl->pImeEscape(hIMC,uEscape,lpData);
611 else
612 {
613 CHAR buffer[81]; /* largest required buffer should be 80 */
614 LRESULT rc;
615 if (uEscape == IME_ESC_SET_EUDC_DICTIONARY)
616 {
617 WideCharToMultiByte(CP_ACP,0,lpData,-1,buffer,81, NULL, NULL);
618 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
619 }
620 else
621 {
622 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
623 MultiByteToWideChar(CP_ACP,0,buffer,-1,lpData,80);
624 }
625 return rc;
626 }
627 }
628 else
629 return 0;
630 }
631
632 static PCLIENTIMC APIENTRY Imm32GetClientImcCache(void)
633 {
634 // FIXME: Do something properly here
635 return NULL;
636 }
637
638 /***********************************************************************
639 * ImmLockClientImc (IMM32.@)
640 */
641 PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc)
642 {
643 PCLIENTIMC pClientImc;
644
645 TRACE("(%p)\n", hImc);
646
647 if (hImc == NULL)
648 return NULL;
649
650 pClientImc = Imm32GetClientImcCache();
651 if (!pClientImc)
652 {
653 pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
654 if (!pClientImc)
655 return NULL;
656
657 RtlInitializeCriticalSection(&pClientImc->cs);
658
659 // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
660 pClientImc->unknown = NtUserGetThreadState(13);
661
662 if (!NtUserUpdateInputContext(hImc, 0, pClientImc))
663 {
664 HeapFree(g_hImm32Heap, 0, pClientImc);
665 return NULL;
666 }
667
668 pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2;
669 }
670 else
671 {
672 if (pClientImc->dwFlags & CLIENTIMC_UNKNOWN1)
673 return NULL;
674 }
675
676 InterlockedIncrement(&pClientImc->cLockObj);
677 return pClientImc;
678 }
679
680 /***********************************************************************
681 * ImmUnlockClientImc (IMM32.@)
682 */
683 VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc)
684 {
685 LONG cLocks;
686 HIMC hImc;
687
688 TRACE("(%p)\n", pClientImc);
689
690 cLocks = InterlockedDecrement(&pClientImc->cLockObj);
691 if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_UNKNOWN1))
692 return;
693
694 hImc = pClientImc->hImc;
695 if (hImc)
696 LocalFree(hImc);
697
698 RtlDeleteCriticalSection(&pClientImc->cs);
699 HeapFree(g_hImm32Heap, 0, pClientImc);
700 }
701
702 static HIMC APIENTRY Imm32GetContextEx(HWND hWnd, DWORD dwContextFlags)
703 {
704 HIMC hIMC;
705 PCLIENTIMC pClientImc;
706 PWND pWnd;
707
708 if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
709 return NULL;
710
711 if (!hWnd)
712 {
713 // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
714 hIMC = (HIMC)NtUserGetThreadState(4);
715 goto Quit;
716 }
717
718 pWnd = ValidateHwndNoErr(hWnd);
719 if (!pWnd || Imm32IsCrossProcessAccess(hWnd))
720 return NULL;
721
722 hIMC = pWnd->hImc;
723 if (!hIMC && (dwContextFlags & 1))
724 hIMC = (HIMC)NtUserQueryWindow(hWnd, QUERY_WINDOW_DEFAULT_ICONTEXT);
725
726 Quit:
727 pClientImc = ImmLockClientImc(hIMC);
728 if (pClientImc == NULL)
729 return NULL;
730 if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_UNKNOWN3))
731 hIMC = NULL;
732 ImmUnlockClientImc(pClientImc);
733 return hIMC;
734 }
735
736
737 /* Helpers for the GetCompositionString functions */
738
739 /* Source encoding is defined by context, source length is always given in respective characters. Destination buffer
740 length is always in bytes. */
741 static INT
742 CopyCompStringIMEtoClient(const InputContextData *data, const void *src, INT src_len, void *dst,
743 INT dst_len, BOOL unicode)
744 {
745 int char_size = unicode ? sizeof(WCHAR) : sizeof(char);
746 INT ret;
747
748 if (is_himc_ime_unicode(data) ^ unicode)
749 {
750 if (unicode)
751 ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR));
752 else
753 ret = WideCharToMultiByte(CP_ACP, 0, src, src_len, dst, dst_len, NULL, NULL);
754 ret *= char_size;
755 }
756 else
757 {
758 if (dst_len)
759 {
760 ret = min(src_len * char_size, dst_len);
761 memcpy(dst, src, ret);
762 }
763 else
764 ret = src_len * char_size;
765 }
766
767 return ret;
768 }
769
770 /* Composition string encoding is defined by context, returned attributes correspond to string, converted according to
771 passed mode. String length is in characters, attributes are in byte arrays. */
772 static INT
773 CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src, INT src_len, const void *comp_string,
774 INT str_len, BYTE *dst, INT dst_len, BOOL unicode)
775 {
776 union
777 {
778 const void *str;
779 const WCHAR *strW;
780 const char *strA;
781 } string;
782 INT rc;
783
784 string.str = comp_string;
785
786 if (is_himc_ime_unicode(data) && !unicode)
787 {
788 rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL);
789 if (dst_len)
790 {
791 int i, j = 0, k = 0;
792
793 if (rc < dst_len)
794 dst_len = rc;
795 for (i = 0; i < str_len; ++i)
796 {
797 int len;
798
799 len = WideCharToMultiByte(CP_ACP, 0, string.strW + i, 1, NULL, 0, NULL, NULL);
800 for (; len > 0; --len)
801 {
802 dst[j++] = src[k];
803
804 if (j >= dst_len)
805 goto end;
806 }
807 ++k;
808 }
809 end:
810 rc = j;
811 }
812 }
813 else if (!is_himc_ime_unicode(data) && unicode)
814 {
815 rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0);
816 if (dst_len)
817 {
818 int i, j = 0;
819
820 if (rc < dst_len)
821 dst_len = rc;
822 for (i = 0; i < str_len; ++i)
823 {
824 if (IsDBCSLeadByte(string.strA[i]))
825 continue;
826
827 dst[j++] = src[i];
828
829 if (j >= dst_len)
830 break;
831 }
832 rc = j;
833 }
834 }
835 else
836 {
837 memcpy(dst, src, min(src_len, dst_len));
838 rc = src_len;
839 }
840
841 return rc;
842 }
843
844 static INT
845 CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource,
846 LPBYTE target, INT tlen, BOOL unicode )
847 {
848 INT rc;
849
850 if (is_himc_ime_unicode(data) && !unicode)
851 {
852 if (tlen)
853 {
854 int i;
855
856 if (slen < tlen)
857 tlen = slen;
858 tlen /= sizeof (DWORD);
859 for (i = 0; i < tlen; ++i)
860 {
861 ((DWORD *)target)[i] = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource,
862 ((DWORD *)source)[i],
863 NULL, 0,
864 NULL, NULL);
865 }
866 rc = sizeof (DWORD) * i;
867 }
868 else
869 rc = slen;
870 }
871 else if (!is_himc_ime_unicode(data) && unicode)
872 {
873 if (tlen)
874 {
875 int i;
876
877 if (slen < tlen)
878 tlen = slen;
879 tlen /= sizeof (DWORD);
880 for (i = 0; i < tlen; ++i)
881 {
882 ((DWORD *)target)[i] = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource,
883 ((DWORD *)source)[i],
884 NULL, 0);
885 }
886 rc = sizeof (DWORD) * i;
887 }
888 else
889 rc = slen;
890 }
891 else
892 {
893 memcpy( target, source, min(slen,tlen));
894 rc = slen;
895 }
896
897 return rc;
898 }
899
900 static INT
901 CopyCompOffsetIMEtoClient(InputContextData *data, DWORD offset, LPBYTE ssource, BOOL unicode)
902 {
903 int rc;
904
905 if (is_himc_ime_unicode(data) && !unicode)
906 {
907 rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL);
908 }
909 else if (!is_himc_ime_unicode(data) && unicode)
910 {
911 rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0);
912 }
913 else
914 rc = offset;
915
916 return rc;
917 }
918
919 static LONG
920 ImmGetCompositionStringT(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf,
921 DWORD dwBufLen, BOOL unicode)
922 {
923 LONG rc = 0;
924 InputContextData *data = get_imc_data(hIMC);
925 LPCOMPOSITIONSTRING compstr;
926 LPBYTE compdata;
927
928 TRACE("(%p, 0x%x, %p, %d)\n", hIMC, dwIndex, lpBuf, dwBufLen);
929
930 if (!data)
931 return FALSE;
932
933 if (!data->IMC.hCompStr)
934 return FALSE;
935
936 compdata = ImmLockIMCC(data->IMC.hCompStr);
937 compstr = (LPCOMPOSITIONSTRING)compdata;
938
939 switch (dwIndex)
940 {
941 case GCS_RESULTSTR:
942 TRACE("GCS_RESULTSTR\n");
943 rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode);
944 break;
945 case GCS_COMPSTR:
946 TRACE("GCS_COMPSTR\n");
947 rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode);
948 break;
949 case GCS_COMPATTR:
950 TRACE("GCS_COMPATTR\n");
951 rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen,
952 compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen,
953 lpBuf, dwBufLen, unicode);
954 break;
955 case GCS_COMPCLAUSE:
956 TRACE("GCS_COMPCLAUSE\n");
957 rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen,
958 compdata + compstr->dwCompStrOffset,
959 lpBuf, dwBufLen, unicode);
960 break;
961 case GCS_RESULTCLAUSE:
962 TRACE("GCS_RESULTCLAUSE\n");
963 rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen,
964 compdata + compstr->dwResultStrOffset,
965 lpBuf, dwBufLen, unicode);
966 break;
967 case GCS_RESULTREADSTR:
968 TRACE("GCS_RESULTREADSTR\n");
969 rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode);
970 break;
971 case GCS_RESULTREADCLAUSE:
972 TRACE("GCS_RESULTREADCLAUSE\n");
973 rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen,
974 compdata + compstr->dwResultStrOffset,
975 lpBuf, dwBufLen, unicode);
976 break;
977 case GCS_COMPREADSTR:
978 TRACE("GCS_COMPREADSTR\n");
979 rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode);
980 break;
981 case GCS_COMPREADATTR:
982 TRACE("GCS_COMPREADATTR\n");
983 rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen,
984 compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen,
985 lpBuf, dwBufLen, unicode);
986 break;
987 case GCS_COMPREADCLAUSE:
988 TRACE("GCS_COMPREADCLAUSE\n");
989 rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen,
990 compdata + compstr->dwCompStrOffset,
991 lpBuf, dwBufLen, unicode);
992 break;
993 case GCS_CURSORPOS:
994 TRACE("GCS_CURSORPOS\n");
995 rc = CopyCompOffsetIMEtoClient(data, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode);
996 break;
997 case GCS_DELTASTART:
998 TRACE("GCS_DELTASTART\n");
999 rc = CopyCompOffsetIMEtoClient(data, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode);
1000 break;
1001 default:
1002 FIXME("Unhandled index 0x%x\n",dwIndex);
1003 break;
1004 }
1005
1006 ImmUnlockIMCC(data->IMC.hCompStr);
1007
1008 return rc;
1009 }
1010
1011 /***********************************************************************
1012 * ImmGetCompositionStringA (IMM32.@)
1013 */
1014 LONG WINAPI ImmGetCompositionStringA(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
1015 {
1016 return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, FALSE);
1017 }
1018
1019 /***********************************************************************
1020 * ImmGetCompositionStringW (IMM32.@)
1021 */
1022 LONG WINAPI ImmGetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
1023 {
1024 return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, TRUE);
1025 }
1026
1027 /***********************************************************************
1028 * ImmGetContext (IMM32.@)
1029 */
1030 HIMC WINAPI ImmGetContext(HWND hWnd)
1031 {
1032 TRACE("(%p)\n", hWnd);
1033 if (hWnd == NULL)
1034 return NULL;
1035 return Imm32GetContextEx(hWnd, 2);
1036 }
1037
1038 /***********************************************************************
1039 * CtfImmIsCiceroEnabled (IMM32.@)
1040 */
1041 BOOL WINAPI CtfImmIsCiceroEnabled(VOID)
1042 {
1043 return (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED));
1044 }
1045
1046 /***********************************************************************
1047 * ImmInstallIMEA (IMM32.@)
1048 */
1049 HKL WINAPI ImmInstallIMEA(LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText)
1050 {
1051 HKL hKL = NULL;
1052 LPWSTR pszFileNameW = NULL, pszLayoutTextW = NULL;
1053
1054 TRACE("(%s, %s)\n", debugstr_a(lpszIMEFileName), debugstr_a(lpszLayoutText));
1055
1056 pszFileNameW = Imm32WideFromAnsi(lpszIMEFileName);
1057 if (pszFileNameW == NULL)
1058 goto Quit;
1059
1060 pszLayoutTextW = Imm32WideFromAnsi(lpszLayoutText);
1061 if (pszLayoutTextW == NULL)
1062 goto Quit;
1063
1064 hKL = ImmInstallIMEW(pszFileNameW, pszLayoutTextW);
1065
1066 Quit:
1067 if (pszFileNameW)
1068 HeapFree(g_hImm32Heap, 0, pszFileNameW);
1069 if (pszLayoutTextW)
1070 HeapFree(g_hImm32Heap, 0, pszLayoutTextW);
1071 return hKL;
1072 }
1073
1074 /***********************************************************************
1075 * ImmInstallIMEW (IMM32.@)
1076 */
1077 HKL WINAPI ImmInstallIMEW(LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText)
1078 {
1079 INT lcid = GetUserDefaultLCID();
1080 INT count;
1081 HKL hkl;
1082 DWORD rc;
1083 HKEY hkey;
1084 WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8];
1085
1086 TRACE ("(%s, %s):\n", debugstr_w(lpszIMEFileName),
1087 debugstr_w(lpszLayoutText));
1088
1089 /* Start with 2. e001 will be blank and so default to the wine internal IME */
1090 count = 2;
1091
1092 while (count < 0xfff)
1093 {
1094 DWORD disposition = 0;
1095
1096 hkl = (HKL)MAKELPARAM( lcid, 0xe000 | count );
1097 wsprintfW( regKey, szImeRegFmt, (ULONG_PTR)hkl);
1098
1099 rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, regKey, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &disposition);
1100 if (rc == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY)
1101 break;
1102 else if (rc == ERROR_SUCCESS)
1103 RegCloseKey(hkey);
1104
1105 count++;
1106 }
1107
1108 if (count == 0xfff)
1109 {
1110 WARN("Unable to find slot to install IME\n");
1111 return 0;
1112 }
1113
1114 if (rc == ERROR_SUCCESS)
1115 {
1116 rc = RegSetValueExW(hkey, szImeFileW, 0, REG_SZ, (const BYTE*)lpszIMEFileName,
1117 (lstrlenW(lpszIMEFileName) + 1) * sizeof(WCHAR));
1118 if (rc == ERROR_SUCCESS)
1119 rc = RegSetValueExW(hkey, szLayoutTextW, 0, REG_SZ, (const BYTE*)lpszLayoutText,
1120 (lstrlenW(lpszLayoutText) + 1) * sizeof(WCHAR));
1121 RegCloseKey(hkey);
1122 return hkl;
1123 }
1124 else
1125 {
1126 WARN("Unable to set IME registry values\n");
1127 return 0;
1128 }
1129 }
1130
1131 /***********************************************************************
1132 * ImmLockIMC(IMM32.@)
1133 */
1134 LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
1135 {
1136 InputContextData *data = get_imc_data(hIMC);
1137
1138 if (!data)
1139 return NULL;
1140 data->dwLock++;
1141 return &data->IMC;
1142 }
1143
1144 /***********************************************************************
1145 * ImmUnlockIMC(IMM32.@)
1146 */
1147 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
1148 {
1149 PCLIENTIMC pClientImc;
1150 HIMC hClientImc;
1151
1152 pClientImc = ImmLockClientImc(hIMC);
1153 if (pClientImc == NULL)
1154 return FALSE;
1155
1156 hClientImc = pClientImc->hImc;
1157 if (hClientImc)
1158 LocalUnlock(hClientImc);
1159
1160 InterlockedDecrement(&pClientImc->cLockObj);
1161 ImmUnlockClientImc(pClientImc);
1162 return TRUE;
1163 }
1164
1165 /***********************************************************************
1166 * ImmRequestMessageA(IMM32.@)
1167 */
1168 LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1169 {
1170 InputContextData *data = get_imc_data(hIMC);
1171
1172 TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
1173
1174 if (data) return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
1175
1176 SetLastError(ERROR_INVALID_HANDLE);
1177 return 0;
1178 }
1179
1180 /***********************************************************************
1181 * ImmRequestMessageW(IMM32.@)
1182 */
1183 LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1184 {
1185 InputContextData *data = get_imc_data(hIMC);
1186
1187 TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
1188
1189 if (data) return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
1190
1191 SetLastError(ERROR_INVALID_HANDLE);
1192 return 0;
1193 }
1194
1195 /***********************************************************************
1196 * ImmReleaseContext (IMM32.@)
1197 */
1198 BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
1199 {
1200 TRACE("(%p, %p)\n", hWnd, hIMC);
1201 UNREFERENCED_PARAMETER(hWnd);
1202 UNREFERENCED_PARAMETER(hIMC);
1203 return TRUE; // Do nothing. This is correct.
1204 }
1205
1206 /***********************************************************************
1207 * ImmSetCompositionStringA (IMM32.@)
1208 */
1209 BOOL WINAPI
1210 ImmSetCompositionStringA(HIMC hIMC, DWORD dwIndex,
1211 LPCVOID lpComp, DWORD dwCompLen,
1212 LPCVOID lpRead, DWORD dwReadLen)
1213 {
1214 DWORD comp_len;
1215 DWORD read_len;
1216 WCHAR *CompBuffer = NULL;
1217 WCHAR *ReadBuffer = NULL;
1218 BOOL rc;
1219 InputContextData *data = get_imc_data(hIMC);
1220
1221 TRACE("(%p, %d, %p, %d, %p, %d):\n",
1222 hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
1223
1224 if (!data)
1225 return FALSE;
1226
1227 if (!(dwIndex == SCS_SETSTR ||
1228 dwIndex == SCS_CHANGEATTR ||
1229 dwIndex == SCS_CHANGECLAUSE ||
1230 dwIndex == SCS_SETRECONVERTSTRING ||
1231 dwIndex == SCS_QUERYRECONVERTSTRING))
1232 return FALSE;
1233
1234 if (!is_himc_ime_unicode(data))
1235 return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
1236 dwCompLen, lpRead, dwReadLen);
1237
1238 comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0);
1239 if (comp_len)
1240 {
1241 CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len * sizeof(WCHAR));
1242 MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len);
1243 }
1244
1245 read_len = MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, NULL, 0);
1246 if (read_len)
1247 {
1248 ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len * sizeof(WCHAR));
1249 MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len);
1250 }
1251
1252 rc = ImmSetCompositionStringW(hIMC, dwIndex, CompBuffer, comp_len,
1253 ReadBuffer, read_len);
1254
1255 HeapFree(GetProcessHeap(), 0, CompBuffer);
1256 HeapFree(GetProcessHeap(), 0, ReadBuffer);
1257
1258 return rc;
1259 }
1260
1261 /***********************************************************************
1262 * ImmSetCompositionStringW (IMM32.@)
1263 */
1264 BOOL WINAPI
1265 ImmSetCompositionStringW(HIMC hIMC, DWORD dwIndex,
1266 LPCVOID lpComp, DWORD dwCompLen,
1267 LPCVOID lpRead, DWORD dwReadLen)
1268 {
1269 DWORD comp_len;
1270 DWORD read_len;
1271 CHAR *CompBuffer = NULL;
1272 CHAR *ReadBuffer = NULL;
1273 BOOL rc;
1274 InputContextData *data = get_imc_data(hIMC);
1275
1276 TRACE("(%p, %d, %p, %d, %p, %d):\n",
1277 hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
1278
1279 if (!data)
1280 return FALSE;
1281
1282 if (!(dwIndex == SCS_SETSTR ||
1283 dwIndex == SCS_CHANGEATTR ||
1284 dwIndex == SCS_CHANGECLAUSE ||
1285 dwIndex == SCS_SETRECONVERTSTRING ||
1286 dwIndex == SCS_QUERYRECONVERTSTRING))
1287 return FALSE;
1288
1289 if (is_himc_ime_unicode(data))
1290 return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
1291 dwCompLen, lpRead, dwReadLen);
1292
1293 comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL,
1294 NULL);
1295 if (comp_len)
1296 {
1297 CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len);
1298 WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len,
1299 NULL, NULL);
1300 }
1301
1302 read_len = WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, NULL, 0, NULL,
1303 NULL);
1304 if (read_len)
1305 {
1306 ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len);
1307 WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len,
1308 NULL, NULL);
1309 }
1310
1311 rc = ImmSetCompositionStringA(hIMC, dwIndex, CompBuffer, comp_len,
1312 ReadBuffer, read_len);
1313
1314 HeapFree(GetProcessHeap(), 0, CompBuffer);
1315 HeapFree(GetProcessHeap(), 0, ReadBuffer);
1316
1317 return rc;
1318 }
1319
1320 /***********************************************************************
1321 * ImmCreateSoftKeyboard(IMM32.@)
1322 */
1323 HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
1324 {
1325 FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
1326 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1327 return 0;
1328 }
1329
1330 /***********************************************************************
1331 * ImmDestroySoftKeyboard(IMM32.@)
1332 */
1333 BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
1334 {
1335 TRACE("(%p)\n", hSoftWnd);
1336 return DestroyWindow(hSoftWnd);
1337 }
1338
1339 /***********************************************************************
1340 * ImmShowSoftKeyboard(IMM32.@)
1341 */
1342 BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
1343 {
1344 TRACE("(%p, %d)\n", hSoftWnd, nCmdShow);
1345 if (hSoftWnd)
1346 return ShowWindow(hSoftWnd, nCmdShow);
1347 return FALSE;
1348 }
1349
1350 /***********************************************************************
1351 * ImmDisableTextFrameService(IMM32.@)
1352 */
1353 BOOL WINAPI ImmDisableTextFrameService(DWORD dwThreadId)
1354 {
1355 FIXME("Stub\n");
1356 return FALSE;
1357 }
1358
1359 /***********************************************************************
1360 * ImmEnumInputContext(IMM32.@)
1361 */
1362 BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam)
1363 {
1364 HIMC *phList;
1365 DWORD dwIndex, dwCount;
1366 BOOL ret = TRUE;
1367 HIMC hIMC;
1368
1369 TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam);
1370
1371 dwCount = Imm32AllocAndBuildHimcList(dwThreadId, &phList);
1372 if (!dwCount)
1373 return FALSE;
1374
1375 for (dwIndex = 0; dwIndex < dwCount; ++dwIndex)
1376 {
1377 hIMC = phList[dwIndex];
1378 ret = (*lpfn)(hIMC, lParam);
1379 if (!ret)
1380 break;
1381 }
1382
1383 HeapFree(g_hImm32Heap, 0, phList);
1384 return ret;
1385 }
1386
1387 /***********************************************************************
1388 * ImmSetActiveContext(IMM32.@)
1389 */
1390 BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC hIMC, BOOL fFlag)
1391 {
1392 FIXME("(%p, %p, %d): stub\n", hwnd, hIMC, fFlag);
1393 return FALSE;
1394 }
1395
1396 /***********************************************************************
1397 * ImmSetActiveContextConsoleIME(IMM32.@)
1398 */
1399 BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag)
1400 {
1401 HIMC hIMC;
1402 TRACE("(%p, %d)\n", hwnd, fFlag);
1403
1404 hIMC = ImmGetContext(hwnd);
1405 if (hIMC)
1406 return ImmSetActiveContext(hwnd, hIMC, fFlag);
1407 return FALSE;
1408 }
1409
1410 /***********************************************************************
1411 * ImmGetImeMenuItemsA (IMM32.@)
1412 */
1413 DWORD WINAPI
1414 ImmGetImeMenuItemsA(HIMC hIMC, DWORD dwFlags, DWORD dwType,
1415 LPIMEMENUITEMINFOA lpImeParentMenu,
1416 LPIMEMENUITEMINFOA lpImeMenu, DWORD dwSize)
1417 {
1418 InputContextData *data = get_imc_data(hIMC);
1419 TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
1420 lpImeParentMenu, lpImeMenu, dwSize);
1421
1422 if (!data)
1423 {
1424 SetLastError(ERROR_INVALID_HANDLE);
1425 return 0;
1426 }
1427
1428 if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
1429 {
1430 if (!is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
1431 return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1432 (IMEMENUITEMINFOW*)lpImeParentMenu,
1433 (IMEMENUITEMINFOW*)lpImeMenu, dwSize);
1434 else
1435 {
1436 IMEMENUITEMINFOW lpImeParentMenuW;
1437 IMEMENUITEMINFOW *lpImeMenuW, *parent = NULL;
1438 DWORD rc;
1439
1440 if (lpImeParentMenu)
1441 parent = &lpImeParentMenuW;
1442 if (lpImeMenu)
1443 {
1444 int count = dwSize / sizeof(LPIMEMENUITEMINFOA);
1445 dwSize = count * sizeof(IMEMENUITEMINFOW);
1446 lpImeMenuW = HeapAlloc(GetProcessHeap(), 0, dwSize);
1447 }
1448 else
1449 lpImeMenuW = NULL;
1450
1451 rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1452 parent, lpImeMenuW, dwSize);
1453
1454 if (lpImeParentMenu)
1455 {
1456 memcpy(lpImeParentMenu,&lpImeParentMenuW,sizeof(IMEMENUITEMINFOA));
1457 lpImeParentMenu->hbmpItem = lpImeParentMenuW.hbmpItem;
1458 WideCharToMultiByte(CP_ACP, 0, lpImeParentMenuW.szString,
1459 -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE,
1460 NULL, NULL);
1461 }
1462 if (lpImeMenu && rc)
1463 {
1464 unsigned int i;
1465 for (i = 0; i < rc; i++)
1466 {
1467 memcpy(&lpImeMenu[i],&lpImeMenuW[1],sizeof(IMEMENUITEMINFOA));
1468 lpImeMenu[i].hbmpItem = lpImeMenuW[i].hbmpItem;
1469 WideCharToMultiByte(CP_ACP, 0, lpImeMenuW[i].szString,
1470 -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE,
1471 NULL, NULL);
1472 }
1473 }
1474 HeapFree(GetProcessHeap(),0,lpImeMenuW);
1475 return rc;
1476 }
1477 }
1478 else
1479 return 0;
1480 }
1481
1482 /***********************************************************************
1483 * ImmGetImeMenuItemsW (IMM32.@)
1484 */
1485 DWORD WINAPI
1486 ImmGetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType,
1487 LPIMEMENUITEMINFOW lpImeParentMenu,
1488 LPIMEMENUITEMINFOW lpImeMenu, DWORD dwSize)
1489 {
1490 InputContextData *data = get_imc_data(hIMC);
1491 TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
1492 lpImeParentMenu, lpImeMenu, dwSize);
1493
1494 if (!data)
1495 {
1496 SetLastError(ERROR_INVALID_HANDLE);
1497 return 0;
1498 }
1499
1500 if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
1501 {
1502 if (is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
1503 return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1504 lpImeParentMenu, lpImeMenu, dwSize);
1505 else
1506 {
1507 IMEMENUITEMINFOA lpImeParentMenuA;
1508 IMEMENUITEMINFOA *lpImeMenuA, *parent = NULL;
1509 DWORD rc;
1510
1511 if (lpImeParentMenu)
1512 parent = &lpImeParentMenuA;
1513 if (lpImeMenu)
1514 {
1515 int count = dwSize / sizeof(LPIMEMENUITEMINFOW);
1516 dwSize = count * sizeof(IMEMENUITEMINFOA);
1517 lpImeMenuA = HeapAlloc(GetProcessHeap(), 0, dwSize);
1518 }
1519 else
1520 lpImeMenuA = NULL;
1521
1522 rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1523 (IMEMENUITEMINFOW*)parent,
1524 (IMEMENUITEMINFOW*)lpImeMenuA, dwSize);
1525
1526 if (lpImeParentMenu)
1527 {
1528 memcpy(lpImeParentMenu,&lpImeParentMenuA,sizeof(IMEMENUITEMINFOA));
1529 lpImeParentMenu->hbmpItem = lpImeParentMenuA.hbmpItem;
1530 MultiByteToWideChar(CP_ACP, 0, lpImeParentMenuA.szString,
1531 -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE);
1532 }
1533 if (lpImeMenu && rc)
1534 {
1535 unsigned int i;
1536 for (i = 0; i < rc; i++)
1537 {
1538 memcpy(&lpImeMenu[i],&lpImeMenuA[1],sizeof(IMEMENUITEMINFOA));
1539 lpImeMenu[i].hbmpItem = lpImeMenuA[i].hbmpItem;
1540 MultiByteToWideChar(CP_ACP, 0, lpImeMenuA[i].szString,
1541 -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE);
1542 }
1543 }
1544 HeapFree(GetProcessHeap(),0,lpImeMenuA);
1545 return rc;
1546 }
1547 }
1548 else
1549 return 0;
1550 }
1551
1552 BOOL WINAPI User32InitializeImmEntryTable(DWORD);
1553
1554 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
1555 {
1556 HKL hKL;
1557 HIMC hIMC;
1558 PTEB pTeb;
1559
1560 TRACE("(%p, 0x%X, %p)\n", hinstDLL, fdwReason, lpReserved);
1561
1562 switch (fdwReason)
1563 {
1564 case DLL_PROCESS_ATTACH:
1565 //Imm32GenerateRandomSeed(hinstDLL, 1, lpReserved); // Non-sense
1566 if (!Imm32InitInstance(hinstDLL))
1567 {
1568 ERR("Imm32InitInstance failed\n");
1569 return FALSE;
1570 }
1571 if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC))
1572 {
1573 ERR("User32InitializeImmEntryTable failed\n");
1574 return FALSE;
1575 }
1576 break;
1577
1578 case DLL_THREAD_ATTACH:
1579 break;
1580
1581 case DLL_THREAD_DETACH:
1582 if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
1583 return TRUE;
1584
1585 pTeb = NtCurrentTeb();
1586 if (pTeb->Win32ThreadInfo == NULL)
1587 return TRUE;
1588
1589 hKL = GetKeyboardLayout(0);
1590 // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
1591 hIMC = (HIMC)NtUserGetThreadState(4);
1592 Imm32CleanupContext(hIMC, hKL, TRUE);
1593 break;
1594
1595 case DLL_PROCESS_DETACH:
1596 RtlDeleteCriticalSection(&g_csImeDpi);
1597 TRACE("imm32.dll is unloaded\n");
1598 break;
1599 }
1600
1601 return TRUE;
1602 }