imm32: Fixed IMCC implementation.
[wine.git] / dlls / imm32 / tests / imm32.c
blob0a511e6b020cff357544254f6ddd0ac07fe6dec2
1 /*
2 * Unit tests for imm32
4 * Copyright (c) 2008 Michael Jung
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdio.h>
23 #include "wine/test.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 #include "imm.h"
27 #include "ddk/imm.h"
29 #define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
31 static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD);
32 static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM);
33 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
36 * msgspy - record and analyse message traces sent to a certain window
38 typedef struct _msgs {
39 CWPSTRUCT msg;
40 BOOL post;
41 } imm_msgs;
43 static struct _msg_spy {
44 HWND hwnd;
45 HHOOK get_msg_hook;
46 HHOOK call_wnd_proc_hook;
47 imm_msgs msgs[32];
48 unsigned int i_msg;
49 } msg_spy;
51 typedef struct
53 DWORD type;
54 union
56 MOUSEINPUT mi;
57 KEYBDINPUT ki;
58 HARDWAREINPUT hi;
59 } u;
60 } TEST_INPUT;
62 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
64 static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
66 if (HC_ACTION == nCode) {
67 MSG *msg = (MSG*)lParam;
69 if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
70 (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
72 msg_spy.msgs[msg_spy.i_msg].msg.hwnd = msg->hwnd;
73 msg_spy.msgs[msg_spy.i_msg].msg.message = msg->message;
74 msg_spy.msgs[msg_spy.i_msg].msg.wParam = msg->wParam;
75 msg_spy.msgs[msg_spy.i_msg].msg.lParam = msg->lParam;
76 msg_spy.msgs[msg_spy.i_msg].post = TRUE;
77 msg_spy.i_msg++;
81 return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
84 static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
85 LPARAM lParam)
87 if (HC_ACTION == nCode) {
88 CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
90 if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
91 (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
93 memcpy(&msg_spy.msgs[msg_spy.i_msg].msg, cwp, sizeof(msg_spy.msgs[0].msg));
94 msg_spy.msgs[msg_spy.i_msg].post = FALSE;
95 msg_spy.i_msg++;
99 return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
102 static void msg_spy_pump_msg_queue(void) {
103 MSG msg;
105 while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
106 TranslateMessage(&msg);
107 DispatchMessage(&msg);
110 return;
113 static void msg_spy_flush_msgs(void) {
114 msg_spy_pump_msg_queue();
115 msg_spy.i_msg = 0;
118 static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
119 UINT i;
121 msg_spy_pump_msg_queue();
123 if (msg_spy.i_msg >= NUMELEMS(msg_spy.msgs))
124 fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
125 __FILE__, __LINE__);
127 for (i = *start; i < msg_spy.i_msg; i++)
128 if (msg_spy.msgs[i].msg.message == message)
130 *start = i+1;
131 return &msg_spy.msgs[i];
134 return NULL;
137 static imm_msgs* msg_spy_find_msg(UINT message) {
138 UINT i = 0;
140 return msg_spy_find_next_msg(message, &i);
143 static void msg_spy_init(HWND hwnd) {
144 msg_spy.hwnd = hwnd;
145 msg_spy.get_msg_hook =
146 SetWindowsHookEx(WH_GETMESSAGE, get_msg_filter, GetModuleHandle(0),
147 GetCurrentThreadId());
148 msg_spy.call_wnd_proc_hook =
149 SetWindowsHookEx(WH_CALLWNDPROC, call_wnd_proc_filter,
150 GetModuleHandle(0), GetCurrentThreadId());
151 msg_spy.i_msg = 0;
153 msg_spy_flush_msgs();
156 static void msg_spy_cleanup(void) {
157 if (msg_spy.get_msg_hook)
158 UnhookWindowsHookEx(msg_spy.get_msg_hook);
159 if (msg_spy.call_wnd_proc_hook)
160 UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
161 memset(&msg_spy, 0, sizeof(msg_spy));
165 * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
166 * messages being sent to this window in response.
168 static const char wndcls[] = "winetest_imm32_wndcls";
169 static HWND hwnd;
171 static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
173 switch (msg)
175 case WM_IME_SETCONTEXT:
176 case WM_NCCREATE:
177 case WM_CREATE:
178 return TRUE;
181 return DefWindowProcA(hwnd,msg,wParam,lParam);
184 static BOOL init(void) {
185 WNDCLASSEX wc;
186 HIMC imc;
187 HMODULE hmod,huser;
189 hmod = GetModuleHandleA("imm32.dll");
190 huser = GetModuleHandleA("user32");
191 pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
192 pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
193 pSendInput = (void*)GetProcAddress(huser, "SendInput");
195 wc.cbSize = sizeof(WNDCLASSEX);
196 wc.style = 0;
197 wc.lpfnWndProc = wndProc;
198 wc.cbClsExtra = 0;
199 wc.cbWndExtra = 0;
200 wc.hInstance = GetModuleHandle(0);
201 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
202 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
203 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
204 wc.lpszMenuName = NULL;
205 wc.lpszClassName = wndcls;
206 wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
208 if (!RegisterClassExA(&wc))
209 return FALSE;
211 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
212 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
213 240, 120, NULL, NULL, GetModuleHandle(0), NULL);
214 if (!hwnd)
215 return FALSE;
217 imc = ImmGetContext(hwnd);
218 if (!imc)
220 win_skip("IME support not implemented\n");
221 return FALSE;
223 ImmReleaseContext(hwnd, imc);
225 ShowWindow(hwnd, SW_SHOWNORMAL);
226 UpdateWindow(hwnd);
228 msg_spy_init(hwnd);
230 return TRUE;
233 static void cleanup(void) {
234 msg_spy_cleanup();
235 if (hwnd)
236 DestroyWindow(hwnd);
237 UnregisterClass(wndcls, GetModuleHandle(0));
240 static void test_ImmNotifyIME(void) {
241 static const char string[] = "wine";
242 char resstr[16] = "";
243 HIMC imc;
244 BOOL ret;
246 imc = ImmGetContext(hwnd);
247 msg_spy_flush_msgs();
249 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
250 ok(broken(!ret) ||
251 ret, /* Vista+ */
252 "Canceling an empty composition string should succeed.\n");
253 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
254 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
255 "the composition string being canceled is empty.\n");
257 ImmSetCompositionString(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
258 msg_spy_flush_msgs();
260 ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
261 msg_spy_flush_msgs();
263 /* behavior differs between win9x and NT */
264 ret = ImmGetCompositionString(imc, GCS_COMPSTR, resstr, sizeof(resstr));
265 ok(!ret, "After being cancelled the composition string is empty.\n");
267 msg_spy_flush_msgs();
269 ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
270 ok(broken(!ret) ||
271 ret, /* Vista+ */
272 "Canceling an empty composition string should succeed.\n");
273 ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
274 "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
275 "the composition string being canceled is empty.\n");
277 msg_spy_flush_msgs();
278 ImmReleaseContext(hwnd, imc);
281 static void test_ImmGetCompositionString(void)
283 HIMC imc;
284 static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
285 char cstring[20];
286 WCHAR wstring[20];
287 DWORD len;
288 DWORD alen,wlen;
290 imc = ImmGetContext(hwnd);
291 ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0);
292 alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
293 wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
294 /* windows machines without any IME installed just return 0 above */
295 if( alen && wlen)
297 len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
298 ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n");
299 len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0);
300 ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n");
302 ImmReleaseContext(hwnd, imc);
305 static void test_ImmSetCompositionString(void)
307 HIMC imc;
308 BOOL ret;
310 SetLastError(0xdeadbeef);
311 imc = ImmGetContext(hwnd);
312 ok(imc != 0, "ImmGetContext() failed. Last error: %u\n", GetLastError());
313 if (!imc)
314 return;
316 ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
317 ok(broken(!ret) ||
318 ret, /* Vista+ */
319 "ImmSetCompositionStringW() failed.\n");
321 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
322 NULL, 0, NULL, 0);
323 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
325 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
326 NULL, 0, NULL, 0);
327 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
329 ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
330 NULL, 0, NULL, 0);
331 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
333 ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
334 NULL, 0, NULL, 0);
335 ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
337 ImmReleaseContext(hwnd, imc);
340 static void test_ImmIME(void)
342 HIMC imc;
344 imc = ImmGetContext(hwnd);
345 if (imc)
347 BOOL rc;
348 rc = ImmConfigureIMEA(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
349 ok (rc == 0, "ImmConfigureIMEA did not fail\n");
350 rc = ImmConfigureIMEW(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
351 ok (rc == 0, "ImmConfigureIMEW did not fail\n");
353 ImmReleaseContext(hwnd,imc);
356 static void test_ImmAssociateContextEx(void)
358 HIMC imc;
359 BOOL rc;
361 if (!pImmAssociateContextEx) return;
363 imc = ImmGetContext(hwnd);
364 if (imc)
366 HIMC retimc, newimc;
368 newimc = ImmCreateContext();
369 ok(newimc != imc, "handles should not be the same\n");
370 rc = pImmAssociateContextEx(NULL, NULL, 0);
371 ok(!rc, "ImmAssociateContextEx succeeded\n");
372 rc = pImmAssociateContextEx(hwnd, NULL, 0);
373 ok(rc, "ImmAssociateContextEx failed\n");
374 rc = pImmAssociateContextEx(NULL, imc, 0);
375 ok(!rc, "ImmAssociateContextEx succeeded\n");
377 rc = pImmAssociateContextEx(hwnd, imc, 0);
378 ok(rc, "ImmAssociateContextEx failed\n");
379 retimc = ImmGetContext(hwnd);
380 ok(retimc == imc, "handles should be the same\n");
381 ImmReleaseContext(hwnd,retimc);
383 rc = pImmAssociateContextEx(hwnd, newimc, 0);
384 ok(rc, "ImmAssociateContextEx failed\n");
385 retimc = ImmGetContext(hwnd);
386 ok(retimc == newimc, "handles should be the same\n");
387 ImmReleaseContext(hwnd,retimc);
389 rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
390 ok(rc, "ImmAssociateContextEx failed\n");
392 ImmReleaseContext(hwnd,imc);
395 typedef struct _igc_threadinfo {
396 HWND hwnd;
397 HANDLE event;
398 HIMC himc;
399 } igc_threadinfo;
402 static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam)
404 HIMC h1,h2;
405 HWND hwnd2;
406 COMPOSITIONFORM cf;
407 POINT pt;
408 igc_threadinfo *info= (igc_threadinfo*)lpParam;
409 info->hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
410 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
411 240, 120, NULL, NULL, GetModuleHandle(0), NULL);
413 h1 = ImmGetContext(hwnd);
414 todo_wine ok(info->himc == h1, "hwnd context changed in new thread\n");
415 h2 = ImmGetContext(info->hwnd);
416 todo_wine ok(h2 != h1, "new hwnd in new thread should have different context\n");
417 info->himc = h2;
418 ImmReleaseContext(hwnd,h1);
420 hwnd2 = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
421 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
422 240, 120, NULL, NULL, GetModuleHandle(0), NULL);
423 h1 = ImmGetContext(hwnd2);
425 ok(h1 == h2, "Windows in same thread should have same default context\n");
426 ImmReleaseContext(hwnd2,h1);
427 ImmReleaseContext(info->hwnd,h2);
428 DestroyWindow(hwnd2);
430 /* priming for later tests */
431 ImmSetCompositionWindow(h1, &cf);
432 ImmSetStatusWindowPos(h1, &pt);
434 SetEvent(info->event);
435 Sleep(INFINITE);
436 return 1;
439 static void test_ImmThreads(void)
441 HIMC himc, otherHimc, h1;
442 igc_threadinfo threadinfo;
443 HANDLE hThread;
444 DWORD dwThreadId;
445 BOOL rc;
446 LOGFONT lf;
447 COMPOSITIONFORM cf;
448 DWORD status, sentence;
449 POINT pt;
451 himc = ImmGetContext(hwnd);
452 threadinfo.event = CreateEvent(NULL, TRUE, FALSE, NULL);
453 threadinfo.himc = himc;
454 hThread = CreateThread(NULL, 0, ImmGetContextThreadFunc, &threadinfo, 0, &dwThreadId );
455 WaitForSingleObject(threadinfo.event, INFINITE);
457 otherHimc = ImmGetContext(threadinfo.hwnd);
459 todo_wine ok(himc != otherHimc, "Windows from other threads should have different himc\n");
460 todo_wine ok(otherHimc == threadinfo.himc, "Context from other thread should not change in main thread\n");
462 if (0) /* FIXME: Causes wine to hang */
464 h1 = ImmAssociateContext(hwnd,otherHimc);
465 ok(h1 == NULL, "Should fail to be able to Associate a default context from a different thread\n");
466 h1 = ImmGetContext(hwnd);
467 ok(h1 == himc, "Context for window should remain unchanged\n");
468 ImmReleaseContext(hwnd,h1);
472 /* OpenStatus */
473 rc = ImmSetOpenStatus(himc, TRUE);
474 ok(rc != 0, "ImmSetOpenStatus failed\n");
475 rc = ImmGetOpenStatus(himc);
476 ok(rc != 0, "ImmGetOpenStatus failed\n");
477 rc = ImmSetOpenStatus(himc, FALSE);
478 ok(rc != 0, "ImmSetOpenStatus failed\n");
479 rc = ImmGetOpenStatus(himc);
480 ok(rc == 0, "ImmGetOpenStatus failed\n");
482 rc = ImmSetOpenStatus(otherHimc, TRUE);
483 todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n");
484 rc = ImmGetOpenStatus(otherHimc);
485 todo_wine ok(rc == 0, "ImmGetOpenStatus failed\n");
486 rc = ImmSetOpenStatus(otherHimc, FALSE);
487 todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n");
488 rc = ImmGetOpenStatus(otherHimc);
489 ok(rc == 0, "ImmGetOpenStatus failed\n");
491 /* CompositionFont */
492 rc = ImmGetCompositionFont(himc, &lf);
493 ok(rc != 0, "ImmGetCompositionFont failed\n");
494 rc = ImmSetCompositionFont(himc, &lf);
495 ok(rc != 0, "ImmSetCompositionFont failed\n");
497 rc = ImmGetCompositionFont(otherHimc, &lf);
498 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont failed\n");
499 rc = ImmSetCompositionFont(otherHimc, &lf);
500 todo_wine ok(rc == 0, "ImmSetCompositionFont should fail\n");
502 /* CompositionWindow */
503 rc = ImmSetCompositionWindow(himc, &cf);
504 ok(rc != 0, "ImmSetCompositionWindow failed\n");
505 rc = ImmGetCompositionWindow(himc, &cf);
506 ok(rc != 0, "ImmGetCompositionWindow failed\n");
508 rc = ImmSetCompositionWindow(otherHimc, &cf);
509 todo_wine ok(rc == 0, "ImmSetCompositionWindow should fail\n");
510 rc = ImmGetCompositionWindow(otherHimc, &cf);
511 ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n");
513 /* ConversionStatus */
514 rc = ImmGetConversionStatus(himc, &status, &sentence);
515 ok(rc != 0, "ImmGetConversionStatus failed\n");
516 rc = ImmSetConversionStatus(himc, status, sentence);
517 ok(rc != 0, "ImmSetConversionStatus failed\n");
519 rc = ImmGetConversionStatus(otherHimc, &status, &sentence);
520 ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n");
521 rc = ImmSetConversionStatus(otherHimc, status, sentence);
522 todo_wine ok(rc == 0, "ImmSetConversionStatus should fail\n");
524 /* StatusWindowPos */
525 rc = ImmSetStatusWindowPos(himc, &pt);
526 ok(rc != 0, "ImmSetStatusWindowPos failed\n");
527 rc = ImmGetStatusWindowPos(himc, &pt);
528 ok(rc != 0, "ImmGetStatusWindowPos failed\n");
530 rc = ImmSetStatusWindowPos(otherHimc, &pt);
531 todo_wine ok(rc == 0, "ImmSetStatusWindowPos should fail\n");
532 rc = ImmGetStatusWindowPos(otherHimc, &pt);
533 ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n");
535 ImmReleaseContext(threadinfo.hwnd,otherHimc);
536 ImmReleaseContext(hwnd,himc);
538 DestroyWindow(threadinfo.hwnd);
539 TerminateThread(hThread, 1);
541 himc = ImmGetContext(GetDesktopWindow());
542 todo_wine ok(himc == NULL, "Should not be able to get himc from other process window\n");
545 static void test_ImmIsUIMessage(void)
547 struct test
549 UINT msg;
550 BOOL ret;
553 static const struct test tests[] =
555 { WM_MOUSEMOVE, FALSE },
556 { WM_IME_STARTCOMPOSITION, TRUE },
557 { WM_IME_ENDCOMPOSITION, TRUE },
558 { WM_IME_COMPOSITION, TRUE },
559 { WM_IME_SETCONTEXT, TRUE },
560 { WM_IME_NOTIFY, TRUE },
561 { WM_IME_CONTROL, FALSE },
562 { WM_IME_COMPOSITIONFULL, TRUE },
563 { WM_IME_SELECT, TRUE },
564 { WM_IME_CHAR, FALSE },
565 { 0x287 /* FIXME */, TRUE },
566 { WM_IME_REQUEST, FALSE },
567 { WM_IME_KEYDOWN, FALSE },
568 { WM_IME_KEYUP, FALSE },
569 { 0, FALSE } /* mark the end */
572 const struct test *test;
573 BOOL ret;
575 if (!pImmIsUIMessageA) return;
577 for (test = tests; test->msg; test++)
579 msg_spy_flush_msgs();
580 ret = pImmIsUIMessageA(NULL, test->msg, 0, 0);
581 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
582 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x for NULL hwnd\n", test->msg);
584 ret = pImmIsUIMessageA(hwnd, test->msg, 0, 0);
585 ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
586 if (ret)
587 ok(msg_spy_find_msg(test->msg) != NULL, "Windows does send 0x%x\n", test->msg);
588 else
589 ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x\n", test->msg);
593 static void test_ImmGetContext(void)
595 HIMC himc;
596 DWORD err;
598 SetLastError(0xdeadbeef);
599 himc = ImmGetContext((HWND)0xffffffff);
600 err = GetLastError();
601 ok(himc == NULL, "ImmGetContext succeeded\n");
602 ok(err == ERROR_INVALID_WINDOW_HANDLE, "got %u\n", err);
604 himc = ImmGetContext(hwnd);
605 ok(himc != NULL, "ImmGetContext failed\n");
606 ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
609 static void test_ImmGetDescription(void)
611 HKL hkl;
612 WCHAR japime[] = { 'E', '0', '0', '1', '0', '4', '1', '1', 0 };
613 WCHAR descW[100];
614 CHAR descA[100];
615 UINT ret, lret;
617 /* FIXME: invalid keyboard layouts should not pass */
618 ret = ImmGetDescriptionW(NULL, NULL, 0);
619 todo_wine ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
621 /* load a language with valid IMM descriptions */
622 hkl = LoadKeyboardLayoutW(japime, KLF_ACTIVATE);
623 todo_wine ok(hkl != 0, "LoadKeyboardLayoutW failed, expected != 0.\n");
625 ret = ImmGetDescriptionW(hkl, NULL, 0);
626 if(!ret)
628 win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n");
629 return;
632 ret = ImmGetDescriptionW(hkl, descW, 0);
633 ok(ret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
635 lret = ImmGetDescriptionW(hkl, descW, ret + 1);
636 ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
637 ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
639 lret = ImmGetDescriptionA(hkl, descA, ret + 1);
640 ok(lret, "ImmGetDescriptionA failed, expected != 0 received 0.\n");
641 todo_wine ok(lret == ret, "ImmGetDescriptionA failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
643 ret /= 2; /* try to copy partially */
644 lret = ImmGetDescriptionW(hkl, descW, ret + 1);
645 ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
646 ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
648 ret = ImmGetDescriptionW(hkl, descW, 1);
649 ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
651 UnloadKeyboardLayout(hkl);
654 static void test_ImmDefaultHwnd(void)
656 HIMC imc1, imc2, imc3;
657 HWND def1, def3;
658 HWND hwnd;
660 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
661 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
662 240, 120, NULL, NULL, GetModuleHandle(0), NULL);
664 ShowWindow(hwnd, SW_SHOWNORMAL);
666 imc1 = ImmGetContext(hwnd);
667 if (!imc1)
669 win_skip("IME support not implemented\n");
670 return;
673 def1 = ImmGetDefaultIMEWnd(hwnd);
675 imc2 = ImmCreateContext();
676 ImmSetOpenStatus(imc2, TRUE);
678 imc3 = ImmGetContext(hwnd);
679 def3 = ImmGetDefaultIMEWnd(hwnd);
681 ok(def3 == def1, "Default IME window should not change\n");
682 ok(imc1 == imc3, "IME context should not change\n");
683 ImmSetOpenStatus(imc2, FALSE);
685 ImmReleaseContext(hwnd, imc1);
686 ImmReleaseContext(hwnd, imc3);
687 ImmDestroyContext(imc2);
688 DestroyWindow(hwnd);
691 static void test_ImmGetIMCLockCount(void)
693 HIMC imc;
694 DWORD count, ret;
695 INPUTCONTEXT *ic;
697 imc = ImmCreateContext();
698 count = ImmGetIMCLockCount(imc);
699 ok(count == 0, "expect 0, returned %d\n", count);
700 ic = ImmLockIMC(imc);
701 ok(ic != NULL, "ImmLockIMC failed\n!");
702 count = ImmGetIMCLockCount(imc);
703 ok(count == 1, "expect 1, returned %d\n", count);
704 ret = ImmUnlockIMC(imc);
705 ok(ret == TRUE, "expect TRUE, ret %d\n", ret);
706 count = ImmGetIMCLockCount(imc);
707 ok(count == 0, "expect 0, returned %d\n", count);
708 ret = ImmUnlockIMC(imc);
709 ok(ret == TRUE, "expect TRUE, ret %d\n", ret);
710 count = ImmGetIMCLockCount(imc);
711 ok(count == 0, "expect 0, returned %d\n", count);
712 ImmDestroyContext(imc);
715 static void test_ImmGetIMCCLockCount(void)
717 HIMCC imcc;
718 DWORD count, g_count, ret, i;
719 VOID *p;
721 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
722 count = ImmGetIMCCLockCount(imcc);
723 ok(count == 0, "expect 0, returned %d\n", count);
724 ImmLockIMCC(imcc);
725 count = ImmGetIMCCLockCount(imcc);
726 ok(count == 1, "expect 1, returned %d\n", count);
727 ret = ImmUnlockIMCC(imcc);
728 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
729 count = ImmGetIMCCLockCount(imcc);
730 ok(count == 0, "expect 0, returned %d\n", count);
731 ret = ImmUnlockIMCC(imcc);
732 ok(ret == FALSE, "expect FALSE, ret %d\n", ret);
733 count = ImmGetIMCCLockCount(imcc);
734 ok(count == 0, "expect 0, returned %d\n", count);
736 p = ImmLockIMCC(imcc);
737 ok(GlobalHandle(p) == imcc, "expect %p, returned %p\n", imcc, GlobalHandle(p));
739 for (i = 0; i < GMEM_LOCKCOUNT * 2; i++)
741 ImmLockIMCC(imcc);
742 count = ImmGetIMCCLockCount(imcc);
743 g_count = GlobalFlags(imcc) & GMEM_LOCKCOUNT;
744 ok(count == g_count, "count %d, g_count %d\n", count, g_count);
746 count = ImmGetIMCCLockCount(imcc);
747 ok(count == GMEM_LOCKCOUNT, "expect GMEM_LOCKCOUNT, returned %d\n", count);
749 for (i = 0; i < GMEM_LOCKCOUNT - 1; i++)
750 GlobalUnlock(imcc);
751 count = ImmGetIMCCLockCount(imcc);
752 ok(count == 1, "expect 1, returned %d\n", count);
753 GlobalUnlock(imcc);
754 count = ImmGetIMCCLockCount(imcc);
755 ok(count == 0, "expect 0, returned %d\n", count);
757 ImmDestroyIMCC(imcc);
760 static void test_ImmDestroyIMCC(void)
762 HIMCC imcc;
763 DWORD ret, count, size;
764 VOID *p;
766 imcc = ImmCreateIMCC(sizeof(CANDIDATEINFO));
767 count = ImmGetIMCCLockCount(imcc);
768 ok(count == 0, "expect 0, returned %d\n", count);
769 p = ImmLockIMCC(imcc);
770 ok(p != NULL, "ImmLockIMCC failed!\n");
771 count = ImmGetIMCCLockCount(imcc);
772 ok(count == 1, "expect 1, returned %d\n", count);
773 size = ImmGetIMCCSize(imcc);
774 ok(size == sizeof(CANDIDATEINFO), "returned %d\n", size);
775 p = ImmDestroyIMCC(imcc);
776 ok(p == NULL, "Destroy a locked IMCC should success!\n");
777 p = ImmLockIMCC(imcc);
778 ok(p == NULL, "Lock a destroyed IMCC should fail!\n");
779 ret = ImmUnlockIMCC(imcc);
780 ok(ret == FALSE, "Unlock a destroyed IMCC should return FALSE!\n");
781 count = ImmGetIMCCLockCount(imcc);
782 ok(count == 0, "Get lock count of a destroyed IMCC should return 0!\n");
783 size = ImmGetIMCCSize(imcc);
784 ok(size == 0, "Get size of a destroyed IMCC should return 0!\n");
785 SetLastError(0xdeadbeef);
786 p = ImmDestroyIMCC(imcc);
787 ok(p != NULL, "returned NULL\n");
788 ret = GetLastError();
789 ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret);
792 static void test_ImmMessages(void)
794 CANDIDATEFORM cf;
795 imm_msgs *msg;
796 HWND defwnd;
797 HIMC imc;
798 UINT idx = 0;
800 HWND hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
801 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
802 240, 120, NULL, NULL, GetModuleHandle(0), NULL);
804 ShowWindow(hwnd, SW_SHOWNORMAL);
805 defwnd = ImmGetDefaultIMEWnd(hwnd);
806 imc = ImmGetContext(hwnd);
808 ImmSetOpenStatus(imc, TRUE);
809 msg_spy_flush_msgs();
810 SendMessage(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
813 msg = msg_spy_find_next_msg(WM_IME_CONTROL,&idx);
814 if (msg) ok(!msg->post, "Message should not be posted\n");
815 } while (msg);
816 msg_spy_flush_msgs();
817 ImmSetOpenStatus(imc, FALSE);
818 ImmReleaseContext(hwnd, imc);
819 DestroyWindow(hwnd);
822 static LRESULT CALLBACK processkey_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
823 LPARAM lParam )
825 return DefWindowProcW(hWnd, msg, wParam, lParam);
828 static void test_ime_processkey(void)
830 WCHAR classNameW[] = {'P','r','o','c','e','s','s', 'K','e','y','T','e','s','t','C','l','a','s','s',0};
831 WCHAR windowNameW[] = {'P','r','o','c','e','s','s', 'K','e','y',0};
833 MSG msg;
834 WNDCLASSW wclass;
835 HANDLE hInstance = GetModuleHandleW(NULL);
836 TEST_INPUT inputs[2];
837 HIMC imc;
838 INT rc;
839 HWND hWndTest;
841 wclass.lpszClassName = classNameW;
842 wclass.style = CS_HREDRAW | CS_VREDRAW;
843 wclass.lpfnWndProc = processkey_wnd_proc;
844 wclass.hInstance = hInstance;
845 wclass.hIcon = LoadIcon(0, IDI_APPLICATION);
846 wclass.hCursor = LoadCursor( NULL, IDC_ARROW);
847 wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
848 wclass.lpszMenuName = 0;
849 wclass.cbClsExtra = 0;
850 wclass.cbWndExtra = 0;
851 if(!RegisterClassW(&wclass)){
852 win_skip("Failed to register window.\n");
853 return;
856 /* create the test window that will receive the keystrokes */
857 hWndTest = CreateWindowW(wclass.lpszClassName, windowNameW,
858 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
859 NULL, NULL, hInstance, NULL);
861 ShowWindow(hWndTest, SW_SHOW);
862 SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
863 SetForegroundWindow(hWndTest);
864 UpdateWindow(hWndTest);
866 imc = ImmGetContext(hWndTest);
867 if (!imc)
869 win_skip("IME not supported\n");
870 DestroyWindow(hWndTest);
871 return;
874 rc = ImmSetOpenStatus(imc, TRUE);
875 if (rc != TRUE)
877 win_skip("Unable to open IME\n");
878 ImmReleaseContext(hWndTest, imc);
879 DestroyWindow(hWndTest);
880 return;
883 /* flush pending messages */
884 while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
886 SetFocus(hWndTest);
888 /* init input data that never changes */
889 inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
890 inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
891 inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
893 /* Pressing a key */
894 inputs[0].u.ki.wVk = 0x41;
895 inputs[0].u.ki.wScan = 0x1e;
896 inputs[0].u.ki.dwFlags = 0x0;
898 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
900 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
901 if(msg.message != WM_KEYDOWN)
902 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
903 else
905 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
906 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
907 if(msg.wParam == VK_PROCESSKEY)
908 trace("ProcessKey was correctly found\n");
910 TranslateMessage(&msg);
911 DispatchMessageW(&msg);
914 inputs[0].u.ki.wVk = 0x41;
915 inputs[0].u.ki.wScan = 0x1e;
916 inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
918 pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
920 while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
921 if(msg.message != WM_KEYUP)
922 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
923 else
925 ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
926 PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
927 ok(msg.wParam != VK_PROCESSKEY,"ProcessKey should still not be Found\n");
929 TranslateMessage(&msg);
930 DispatchMessageW(&msg);
933 ImmReleaseContext(hWndTest, imc);
934 ImmSetOpenStatus(imc, FALSE);
935 DestroyWindow(hWndTest);
938 START_TEST(imm32) {
939 if (init())
941 test_ImmNotifyIME();
942 test_ImmGetCompositionString();
943 test_ImmSetCompositionString();
944 test_ImmIME();
945 test_ImmAssociateContextEx();
946 test_ImmThreads();
947 test_ImmIsUIMessage();
948 test_ImmGetContext();
949 test_ImmGetDescription();
950 test_ImmDefaultHwnd();
951 test_ImmGetIMCLockCount();
952 test_ImmGetIMCCLockCount();
953 test_ImmDestroyIMCC();
954 msg_spy_cleanup();
955 /* Reinitialize the hooks to capture all windows */
956 msg_spy_init(NULL);
957 test_ImmMessages();
958 msg_spy_cleanup();
959 if (pSendInput)
960 test_ime_processkey();
961 else win_skip("SendInput is not available\n");
963 cleanup();