6dddd9df22ab8c5ffade571003df7708180ea24d
[reactos.git] / dll / win32 / imm32 / utils.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 IMM32 helper functions
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 HANDLE g_hImm32Heap = NULL;
18
19 BOOL WINAPI Imm32IsImcAnsi(HIMC hIMC)
20 {
21 BOOL ret;
22 PCLIENTIMC pClientImc = ImmLockClientImc(hIMC);
23 if (!pClientImc)
24 return -1;
25 ret = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
26 ImmUnlockClientImc(pClientImc);
27 return ret;
28 }
29
30 LPWSTR APIENTRY Imm32WideFromAnsi(LPCSTR pszA)
31 {
32 INT cch = lstrlenA(pszA);
33 LPWSTR pszW = Imm32HeapAlloc(0, (cch + 1) * sizeof(WCHAR));
34 if (pszW == NULL)
35 return NULL;
36 cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszA, cch, pszW, cch + 1);
37 pszW[cch] = 0;
38 return pszW;
39 }
40
41 LPSTR APIENTRY Imm32AnsiFromWide(LPCWSTR pszW)
42 {
43 INT cchW = lstrlenW(pszW);
44 INT cchA = (cchW + 1) * sizeof(WCHAR);
45 LPSTR pszA = Imm32HeapAlloc(0, cchA);
46 if (!pszA)
47 return NULL;
48 cchA = WideCharToMultiByte(CP_ACP, 0, pszW, cchW, pszA, cchA, NULL, NULL);
49 pszA[cchA] = 0;
50 return pszA;
51 }
52
53 /* Converts the character index */
54 LONG APIENTRY IchWideFromAnsi(LONG cchAnsi, LPCSTR pchAnsi, UINT uCodePage)
55 {
56 LONG cchWide;
57 for (cchWide = 0; cchAnsi > 0; ++cchWide)
58 {
59 if (IsDBCSLeadByteEx(uCodePage, *pchAnsi) && pchAnsi[1])
60 {
61 cchAnsi -= 2;
62 pchAnsi += 2;
63 }
64 else
65 {
66 --cchAnsi;
67 ++pchAnsi;
68 }
69 }
70 return cchWide;
71 }
72
73 /* Converts the character index */
74 LONG APIENTRY IchAnsiFromWide(LONG cchWide, LPCWSTR pchWide, UINT uCodePage)
75 {
76 LONG cb, cchAnsi;
77 for (cchAnsi = 0; cchWide > 0; ++cchAnsi, ++pchWide, --cchWide)
78 {
79 cb = WideCharToMultiByte(uCodePage, 0, pchWide, 1, NULL, 0, NULL, NULL);
80 if (cb > 1)
81 ++cchAnsi;
82 }
83 return cchAnsi;
84 }
85
86 BOOL Imm32GetSystemLibraryPath(LPWSTR pszPath, DWORD cchPath, LPCWSTR pszFileName)
87 {
88 if (!pszFileName[0] || !GetSystemDirectoryW(pszPath, cchPath))
89 return FALSE;
90 StringCchCatW(pszPath, cchPath, L"\\");
91 StringCchCatW(pszPath, cchPath, pszFileName);
92 return TRUE;
93 }
94
95 VOID APIENTRY LogFontAnsiToWide(const LOGFONTA *plfA, LPLOGFONTW plfW)
96 {
97 size_t cch;
98 RtlCopyMemory(plfW, plfA, offsetof(LOGFONTA, lfFaceName));
99 StringCchLengthA(plfA->lfFaceName, _countof(plfA->lfFaceName), &cch);
100 cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plfA->lfFaceName, (INT)cch,
101 plfW->lfFaceName, _countof(plfW->lfFaceName));
102 if (cch > _countof(plfW->lfFaceName) - 1)
103 cch = _countof(plfW->lfFaceName) - 1;
104 plfW->lfFaceName[cch] = 0;
105 }
106
107 VOID APIENTRY LogFontWideToAnsi(const LOGFONTW *plfW, LPLOGFONTA plfA)
108 {
109 size_t cch;
110 RtlCopyMemory(plfA, plfW, offsetof(LOGFONTW, lfFaceName));
111 StringCchLengthW(plfW->lfFaceName, _countof(plfW->lfFaceName), &cch);
112 cch = WideCharToMultiByte(CP_ACP, 0, plfW->lfFaceName, (INT)cch,
113 plfA->lfFaceName, _countof(plfA->lfFaceName), NULL, NULL);
114 if (cch > _countof(plfA->lfFaceName) - 1)
115 cch = _countof(plfA->lfFaceName) - 1;
116 plfA->lfFaceName[cch] = 0;
117 }
118
119 LPVOID FASTCALL ValidateHandleNoErr(HANDLE hObject, UINT uType)
120 {
121 INT index;
122 PUSER_HANDLE_TABLE ht;
123 PUSER_HANDLE_ENTRY he;
124 WORD generation;
125
126 if (!NtUserValidateHandleSecure(hObject))
127 return NULL;
128
129 ht = g_SharedInfo.aheList; /* handle table */
130 ASSERT(ht);
131 /* ReactOS-Specific! */
132 ASSERT(g_SharedInfo.ulSharedDelta != 0);
133 he = (PUSER_HANDLE_ENTRY)((ULONG_PTR)ht->handles - g_SharedInfo.ulSharedDelta);
134
135 index = (LOWORD(hObject) - FIRST_USER_HANDLE) >> 1;
136 if (index < 0 || ht->nb_handles <= index || he[index].type != uType)
137 return NULL;
138
139 generation = HIWORD(hObject);
140 if (generation != he[index].generation && generation && generation != 0xFFFF)
141 return NULL;
142
143 return &he[index];
144 }
145
146 PWND FASTCALL ValidateHwndNoErr(HWND hwnd)
147 {
148 /* See if the window is cached */
149 PCLIENTINFO ClientInfo = GetWin32ClientInfo();
150 if (hwnd == ClientInfo->CallbackWnd.hWnd)
151 return ClientInfo->CallbackWnd.pWnd;
152
153 return ValidateHandleNoErr(hwnd, TYPE_WINDOW);
154 }
155
156 BOOL APIENTRY Imm32CheckImcProcess(PIMC pIMC)
157 {
158 HIMC hIMC;
159 DWORD dwProcessID;
160 if (pIMC->head.pti == NtCurrentTeb()->Win32ThreadInfo)
161 return TRUE;
162
163 hIMC = pIMC->head.h;
164 dwProcessID = NtUserQueryInputContext(hIMC, 0);
165 return dwProcessID == (DWORD_PTR)NtCurrentTeb()->ClientId.UniqueProcess;
166 }
167
168 LPVOID APIENTRY Imm32HeapAlloc(DWORD dwFlags, DWORD dwBytes)
169 {
170 if (!g_hImm32Heap)
171 {
172 g_hImm32Heap = RtlGetProcessHeap();
173 if (g_hImm32Heap == NULL)
174 return NULL;
175 }
176 return HeapAlloc(g_hImm32Heap, dwFlags, dwBytes);
177 }
178
179 BOOL APIENTRY
180 Imm32NotifyAction(HIMC hIMC, HWND hwnd, DWORD dwAction, DWORD_PTR dwIndex, DWORD_PTR dwValue,
181 DWORD_PTR dwCommand, DWORD_PTR dwData)
182 {
183 DWORD dwThreadId;
184 HKL hKL;
185 PIMEDPI pImeDpi;
186
187 if (dwAction)
188 {
189 dwThreadId = NtUserQueryInputContext(hIMC, 1);
190 if (dwThreadId)
191 {
192 /* find keyboard layout and lock it */
193 hKL = GetKeyboardLayout(dwThreadId);
194 pImeDpi = ImmLockImeDpi(hKL);
195 if (pImeDpi)
196 {
197 /* do notify */
198 pImeDpi->NotifyIME(hIMC, dwAction, dwIndex, dwValue);
199
200 ImmUnlockImeDpi(pImeDpi); /* unlock */
201 }
202 }
203 }
204
205 if (hwnd && dwCommand)
206 SendMessageW(hwnd, WM_IME_NOTIFY, dwCommand, dwData);
207
208 return TRUE;
209 }
210
211 DWORD APIENTRY Imm32AllocAndBuildHimcList(DWORD dwThreadId, HIMC **pphList)
212 {
213 #define INITIAL_COUNT 0x40
214 #define MAX_RETRY 10
215 NTSTATUS Status;
216 DWORD dwCount = INITIAL_COUNT, cRetry = 0;
217 HIMC *phNewList;
218
219 phNewList = Imm32HeapAlloc(0, dwCount * sizeof(HIMC));
220 if (phNewList == NULL)
221 return 0;
222
223 Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
224 while (Status == STATUS_BUFFER_TOO_SMALL)
225 {
226 Imm32HeapFree(phNewList);
227 if (cRetry++ >= MAX_RETRY)
228 return 0;
229
230 phNewList = Imm32HeapAlloc(0, dwCount * sizeof(HIMC));
231 if (phNewList == NULL)
232 return 0;
233
234 Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
235 }
236
237 if (NT_ERROR(Status) || !dwCount)
238 {
239 Imm32HeapFree(phNewList);
240 return 0;
241 }
242
243 *pphList = phNewList;
244 return dwCount;
245 #undef INITIAL_COUNT
246 #undef MAX_RETRY
247 }
248
249 INT APIENTRY
250 Imm32ImeMenuAnsiToWide(const IMEMENUITEMINFOA *pItemA, LPIMEMENUITEMINFOW pItemW,
251 UINT uCodePage, BOOL bBitmap)
252 {
253 INT ret;
254 pItemW->cbSize = pItemA->cbSize;
255 pItemW->fType = pItemA->fType;
256 pItemW->fState = pItemA->fState;
257 pItemW->wID = pItemA->wID;
258 if (bBitmap)
259 {
260 pItemW->hbmpChecked = pItemA->hbmpChecked;
261 pItemW->hbmpUnchecked = pItemA->hbmpUnchecked;
262 pItemW->hbmpItem = pItemA->hbmpItem;
263 }
264 pItemW->dwItemData = pItemA->dwItemData;
265 ret = MultiByteToWideChar(uCodePage, 0, pItemA->szString, -1,
266 pItemW->szString, _countof(pItemW->szString));
267 if (ret >= _countof(pItemW->szString))
268 {
269 ret = 0;
270 pItemW->szString[0] = 0;
271 }
272 return ret;
273 }
274
275 INT APIENTRY
276 Imm32ImeMenuWideToAnsi(const IMEMENUITEMINFOW *pItemW, LPIMEMENUITEMINFOA pItemA,
277 UINT uCodePage)
278 {
279 INT ret;
280 pItemA->cbSize = pItemW->cbSize;
281 pItemA->fType = pItemW->fType;
282 pItemA->fState = pItemW->fState;
283 pItemA->wID = pItemW->wID;
284 pItemA->hbmpChecked = pItemW->hbmpChecked;
285 pItemA->hbmpUnchecked = pItemW->hbmpUnchecked;
286 pItemA->dwItemData = pItemW->dwItemData;
287 pItemA->hbmpItem = pItemW->hbmpItem;
288 ret = WideCharToMultiByte(uCodePage, 0, pItemW->szString, -1,
289 pItemA->szString, _countof(pItemA->szString), NULL, NULL);
290 if (ret >= _countof(pItemA->szString))
291 {
292 ret = 0;
293 pItemA->szString[0] = 0;
294 }
295 return ret;
296 }
297
298 PIME_STATE APIENTRY
299 Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL)
300 {
301 PIME_STATE pState;
302 WORD Lang = PRIMARYLANGID(LOWORD(hKL));
303 for (pState = pIC->pState; pState; pState = pState->pNext)
304 {
305 if (pState->wLang == Lang)
306 break;
307 }
308 if (!pState)
309 {
310 pState = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(IME_STATE));
311 if (pState)
312 {
313 pState->wLang = Lang;
314 pState->pNext = pIC->pState;
315 pIC->pState = pState;
316 }
317 }
318 return pState;
319 }
320
321 PIME_SUBSTATE APIENTRY
322 Imm32FetchImeSubState(PIME_STATE pState, HKL hKL)
323 {
324 PIME_SUBSTATE pSubState;
325 for (pSubState = pState->pSubState; pSubState; pSubState = pSubState->pNext)
326 {
327 if (pSubState->hKL == hKL)
328 return pSubState;
329 }
330 pSubState = Imm32HeapAlloc(0, sizeof(IME_SUBSTATE));
331 if (!pSubState)
332 return NULL;
333 pSubState->dwValue = 0;
334 pSubState->hKL = hKL;
335 pSubState->pNext = pState->pSubState;
336 pState->pSubState = pSubState;
337 return pSubState;
338 }
339
340 BOOL APIENTRY
341 Imm32LoadImeStateSentence(LPINPUTCONTEXTDX pIC, PIME_STATE pState, HKL hKL)
342 {
343 PIME_SUBSTATE pSubState = Imm32FetchImeSubState(pState, hKL);
344 if (pSubState)
345 {
346 pIC->fdwSentence |= pSubState->dwValue;
347 return TRUE;
348 }
349 return FALSE;
350 }
351
352 BOOL APIENTRY
353 Imm32SaveImeStateSentence(LPINPUTCONTEXTDX pIC, PIME_STATE pState, HKL hKL)
354 {
355 PIME_SUBSTATE pSubState = Imm32FetchImeSubState(pState, hKL);
356 if (pSubState)
357 {
358 pSubState->dwValue = (pIC->fdwSentence & 0xffff0000);
359 return TRUE;
360 }
361 return FALSE;
362 }
363
364 /***********************************************************************
365 * CtfImmIsTextFrameServiceDisabled(IMM32.@)
366 */
367 BOOL WINAPI CtfImmIsTextFrameServiceDisabled(VOID)
368 {
369 return !!(GetWin32ClientInfo()->CI_flags & CI_TFSDISABLED);
370 }
371
372 /***********************************************************************
373 * ImmCreateIMCC(IMM32.@)
374 */
375 HIMCC WINAPI ImmCreateIMCC(DWORD size)
376 {
377 if (size < sizeof(DWORD))
378 size = sizeof(DWORD);
379 return LocalAlloc(LHND, size);
380 }
381
382 /***********************************************************************
383 * ImmDestroyIMCC(IMM32.@)
384 */
385 HIMCC WINAPI ImmDestroyIMCC(HIMCC block)
386 {
387 if (block)
388 return LocalFree(block);
389 return NULL;
390 }
391
392 /***********************************************************************
393 * ImmLockIMCC(IMM32.@)
394 */
395 LPVOID WINAPI ImmLockIMCC(HIMCC imcc)
396 {
397 if (imcc)
398 return LocalLock(imcc);
399 return NULL;
400 }
401
402 /***********************************************************************
403 * ImmUnlockIMCC(IMM32.@)
404 */
405 BOOL WINAPI ImmUnlockIMCC(HIMCC imcc)
406 {
407 if (imcc)
408 return LocalUnlock(imcc);
409 return FALSE;
410 }
411
412 /***********************************************************************
413 * ImmGetIMCCLockCount(IMM32.@)
414 */
415 DWORD WINAPI ImmGetIMCCLockCount(HIMCC imcc)
416 {
417 return LocalFlags(imcc) & LMEM_LOCKCOUNT;
418 }
419
420 /***********************************************************************
421 * ImmReSizeIMCC(IMM32.@)
422 */
423 HIMCC WINAPI ImmReSizeIMCC(HIMCC imcc, DWORD size)
424 {
425 if (!imcc)
426 return NULL;
427 return LocalReAlloc(imcc, size, LHND);
428 }
429
430 /***********************************************************************
431 * ImmGetIMCCSize(IMM32.@)
432 */
433 DWORD WINAPI ImmGetIMCCSize(HIMCC imcc)
434 {
435 if (imcc)
436 return LocalSize(imcc);
437 return 0;
438 }
439
440 /***********************************************************************
441 * ImmGetIMCLockCount(IMM32.@)
442 */
443 DWORD WINAPI ImmGetIMCLockCount(HIMC hIMC)
444 {
445 DWORD ret;
446 HANDLE hInputContext;
447 PCLIENTIMC pClientImc;
448
449 pClientImc = ImmLockClientImc(hIMC);
450 if (pClientImc == NULL)
451 return 0;
452
453 ret = 0;
454 hInputContext = pClientImc->hInputContext;
455 if (hInputContext)
456 ret = (LocalFlags(hInputContext) & LMEM_LOCKCOUNT);
457
458 ImmUnlockClientImc(pClientImc);
459 return ret;
460 }
461
462 /***********************************************************************
463 * ImmIMPGetIMEA(IMM32.@)
464 */
465 BOOL WINAPI ImmIMPGetIMEA(HWND hWnd, LPIMEPROA pImePro)
466 {
467 FIXME("(%p, %p)\n", hWnd, pImePro);
468 return FALSE;
469 }
470
471 /***********************************************************************
472 * ImmIMPGetIMEW(IMM32.@)
473 */
474 BOOL WINAPI ImmIMPGetIMEW(HWND hWnd, LPIMEPROW pImePro)
475 {
476 FIXME("(%p, %p)\n", hWnd, pImePro);
477 return FALSE;
478 }
479
480 /***********************************************************************
481 * ImmIMPQueryIMEA(IMM32.@)
482 */
483 BOOL WINAPI ImmIMPQueryIMEA(LPIMEPROA pImePro)
484 {
485 FIXME("(%p)\n", pImePro);
486 return FALSE;
487 }
488
489 /***********************************************************************
490 * ImmIMPQueryIMEW(IMM32.@)
491 */
492 BOOL WINAPI ImmIMPQueryIMEW(LPIMEPROW pImePro)
493 {
494 FIXME("(%p)\n", pImePro);
495 return FALSE;
496 }
497
498 /***********************************************************************
499 * ImmIMPSetIMEA(IMM32.@)
500 */
501 BOOL WINAPI ImmIMPSetIMEA(HWND hWnd, LPIMEPROA pImePro)
502 {
503 FIXME("(%p, %p)\n", hWnd, pImePro);
504 return FALSE;
505 }
506
507 /***********************************************************************
508 * ImmIMPSetIMEW(IMM32.@)
509 */
510 BOOL WINAPI ImmIMPSetIMEW(HWND hWnd, LPIMEPROW pImePro)
511 {
512 FIXME("(%p, %p)\n", hWnd, pImePro);
513 return FALSE;
514 }