Release 1.5.29.
[wine] / dlls / imm32 / tests / imm32.c
1 /*
2  * Unit tests for imm32
3  *
4  * Copyright (c) 2008 Michael Jung
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <stdio.h>
22
23 #include "wine/test.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 #include "imm.h"
27
28 #define NUMELEMS(array) (sizeof((array))/sizeof((array)[0]))
29
30 static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD);
31 static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM);
32 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
33
34 /*
35  * msgspy - record and analyse message traces sent to a certain window
36  */
37 typedef struct _msgs {
38     CWPSTRUCT    msg;
39     BOOL         post;
40 } imm_msgs;
41
42 static struct _msg_spy {
43     HWND         hwnd;
44     HHOOK        get_msg_hook;
45     HHOOK        call_wnd_proc_hook;
46     imm_msgs     msgs[32];
47     unsigned int i_msg;
48 } msg_spy;
49
50 typedef struct
51 {
52     DWORD type;
53     union
54     {
55         MOUSEINPUT      mi;
56         KEYBDINPUT      ki;
57         HARDWAREINPUT   hi;
58     } u;
59 } TEST_INPUT;
60
61 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
62
63 static LRESULT CALLBACK get_msg_filter(int nCode, WPARAM wParam, LPARAM lParam)
64 {
65     if (HC_ACTION == nCode) {
66         MSG *msg = (MSG*)lParam;
67
68         if ((msg->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL) &&
69             (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
70         {
71             msg_spy.msgs[msg_spy.i_msg].msg.hwnd    = msg->hwnd;
72             msg_spy.msgs[msg_spy.i_msg].msg.message = msg->message;
73             msg_spy.msgs[msg_spy.i_msg].msg.wParam  = msg->wParam;
74             msg_spy.msgs[msg_spy.i_msg].msg.lParam  = msg->lParam;
75             msg_spy.msgs[msg_spy.i_msg].post = TRUE;
76             msg_spy.i_msg++;
77         }
78     }
79
80     return CallNextHookEx(msg_spy.get_msg_hook, nCode, wParam, lParam);
81 }
82
83 static LRESULT CALLBACK call_wnd_proc_filter(int nCode, WPARAM wParam,
84                                              LPARAM lParam)
85 {
86     if (HC_ACTION == nCode) {
87         CWPSTRUCT *cwp = (CWPSTRUCT*)lParam;
88
89         if (((cwp->hwnd == msg_spy.hwnd || msg_spy.hwnd == NULL)) &&
90             (msg_spy.i_msg < NUMELEMS(msg_spy.msgs)))
91         {
92             memcpy(&msg_spy.msgs[msg_spy.i_msg].msg, cwp, sizeof(msg_spy.msgs[0].msg));
93             msg_spy.msgs[msg_spy.i_msg].post = FALSE;
94             msg_spy.i_msg++;
95         }
96     }
97
98     return CallNextHookEx(msg_spy.call_wnd_proc_hook, nCode, wParam, lParam);
99 }
100
101 static void msg_spy_pump_msg_queue(void) {
102     MSG msg;
103
104     while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
105         TranslateMessage(&msg);
106         DispatchMessage(&msg);
107     }
108
109     return;
110 }
111
112 static void msg_spy_flush_msgs(void) {
113     msg_spy_pump_msg_queue();
114     msg_spy.i_msg = 0;
115 }
116
117 static imm_msgs* msg_spy_find_next_msg(UINT message, UINT *start) {
118     UINT i;
119
120     msg_spy_pump_msg_queue();
121
122     if (msg_spy.i_msg >= NUMELEMS(msg_spy.msgs))
123         fprintf(stdout, "%s:%d: msg_spy: message buffer overflow!\n",
124                 __FILE__, __LINE__);
125
126     for (i = *start; i < msg_spy.i_msg; i++)
127         if (msg_spy.msgs[i].msg.message == message)
128         {
129             *start = i+1;
130             return &msg_spy.msgs[i];
131         }
132
133     return NULL;
134 }
135
136 static imm_msgs* msg_spy_find_msg(UINT message) {
137     UINT i = 0;
138
139     return msg_spy_find_next_msg(message, &i);
140 }
141
142 static void msg_spy_init(HWND hwnd) {
143     msg_spy.hwnd = hwnd;
144     msg_spy.get_msg_hook =
145             SetWindowsHookEx(WH_GETMESSAGE, get_msg_filter, GetModuleHandle(0),
146                              GetCurrentThreadId());
147     msg_spy.call_wnd_proc_hook =
148             SetWindowsHookEx(WH_CALLWNDPROC, call_wnd_proc_filter,
149                              GetModuleHandle(0), GetCurrentThreadId());
150     msg_spy.i_msg = 0;
151
152     msg_spy_flush_msgs();
153 }
154
155 static void msg_spy_cleanup(void) {
156     if (msg_spy.get_msg_hook)
157         UnhookWindowsHookEx(msg_spy.get_msg_hook);
158     if (msg_spy.call_wnd_proc_hook)
159         UnhookWindowsHookEx(msg_spy.call_wnd_proc_hook);
160     memset(&msg_spy, 0, sizeof(msg_spy));
161 }
162
163 /*
164  * imm32 test cases - Issue some IMM commands on a dummy window and analyse the
165  * messages being sent to this window in response.
166  */
167 static const char wndcls[] = "winetest_imm32_wndcls";
168 static HWND hwnd;
169
170 static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
171 {
172     switch (msg)
173     {
174         case WM_IME_SETCONTEXT:
175         case WM_NCCREATE:
176         case WM_CREATE:
177             return TRUE;
178     }
179
180     return DefWindowProcA(hwnd,msg,wParam,lParam);
181 }
182
183 static BOOL init(void) {
184     WNDCLASSEX wc;
185     HIMC imc;
186     HMODULE hmod,huser;
187
188     hmod = GetModuleHandleA("imm32.dll");
189     huser = GetModuleHandleA("user32");
190     pImmAssociateContextEx = (void*)GetProcAddress(hmod, "ImmAssociateContextEx");
191     pImmIsUIMessageA = (void*)GetProcAddress(hmod, "ImmIsUIMessageA");
192     pSendInput = (void*)GetProcAddress(huser, "SendInput");
193
194     wc.cbSize        = sizeof(WNDCLASSEX);
195     wc.style         = 0;
196     wc.lpfnWndProc   = wndProc;
197     wc.cbClsExtra    = 0;
198     wc.cbWndExtra    = 0;
199     wc.hInstance     = GetModuleHandle(0);
200     wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
201     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
202     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
203     wc.lpszMenuName  = NULL;
204     wc.lpszClassName = wndcls;
205     wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
206
207     if (!RegisterClassExA(&wc))
208         return FALSE;
209
210     hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
211                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
212                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
213     if (!hwnd)
214         return FALSE;
215
216     imc = ImmGetContext(hwnd);
217     if (!imc)
218     {
219         win_skip("IME support not implemented\n");
220         return FALSE;
221     }
222     ImmReleaseContext(hwnd, imc);
223
224     ShowWindow(hwnd, SW_SHOWNORMAL);
225     UpdateWindow(hwnd);
226
227     msg_spy_init(hwnd);
228
229     return TRUE;
230 }
231
232 static void cleanup(void) {
233     msg_spy_cleanup();
234     if (hwnd)
235         DestroyWindow(hwnd);
236     UnregisterClass(wndcls, GetModuleHandle(0));
237 }
238
239 static void test_ImmNotifyIME(void) {
240     static const char string[] = "wine";
241     char resstr[16] = "";
242     HIMC imc;
243     BOOL ret;
244
245     imc = ImmGetContext(hwnd);
246     msg_spy_flush_msgs();
247
248     ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
249     ok(broken(!ret) ||
250        ret, /* Vista+ */
251        "Canceling an empty composition string should succeed.\n");
252     ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
253        "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
254        "the composition string being canceled is empty.\n");
255
256     ImmSetCompositionString(imc, SCS_SETSTR, string, sizeof(string), NULL, 0);
257     msg_spy_flush_msgs();
258
259     ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
260     msg_spy_flush_msgs();
261
262     /* behavior differs between win9x and NT */
263     ret = ImmGetCompositionString(imc, GCS_COMPSTR, resstr, sizeof(resstr));
264     ok(!ret, "After being cancelled the composition string is empty.\n");
265
266     msg_spy_flush_msgs();
267
268     ret = ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
269     ok(broken(!ret) ||
270        ret, /* Vista+ */
271        "Canceling an empty composition string should succeed.\n");
272     ok(!msg_spy_find_msg(WM_IME_COMPOSITION), "Windows does not post "
273        "WM_IME_COMPOSITION in response to NI_COMPOSITIONSTR / CPS_CANCEL, if "
274        "the composition string being canceled is empty.\n");
275
276     msg_spy_flush_msgs();
277     ImmReleaseContext(hwnd, imc);
278 }
279
280 static void test_ImmGetCompositionString(void)
281 {
282     HIMC imc;
283     static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e};
284     char cstring[20];
285     WCHAR wstring[20];
286     DWORD len;
287     DWORD alen,wlen;
288
289     imc = ImmGetContext(hwnd);
290     ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0);
291     alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20);
292     wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20);
293     /* windows machines without any IME installed just return 0 above */
294     if( alen && wlen)
295     {
296         len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
297         ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n");
298         len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0);
299         ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n");
300     }
301     ImmReleaseContext(hwnd, imc);
302 }
303
304 static void test_ImmSetCompositionString(void)
305 {
306     HIMC imc;
307     BOOL ret;
308
309     SetLastError(0xdeadbeef);
310     imc = ImmGetContext(hwnd);
311     ok(imc != 0, "ImmGetContext() failed. Last error: %u\n", GetLastError());
312     if (!imc)
313         return;
314
315     ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 0, NULL, 0);
316     ok(broken(!ret) ||
317        ret, /* Vista+ */
318        "ImmSetCompositionStringW() failed.\n");
319
320     ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR,
321         NULL, 0, NULL, 0);
322     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
323
324     ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGECLAUSE,
325         NULL, 0, NULL, 0);
326     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
327
328     ret = ImmSetCompositionStringW(imc, SCS_CHANGEATTR | SCS_CHANGECLAUSE,
329         NULL, 0, NULL, 0);
330     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
331
332     ret = ImmSetCompositionStringW(imc, SCS_SETSTR | SCS_CHANGEATTR | SCS_CHANGECLAUSE,
333         NULL, 0, NULL, 0);
334     ok(!ret, "ImmSetCompositionStringW() succeeded.\n");
335
336     ImmReleaseContext(hwnd, imc);
337 }
338
339 static void test_ImmIME(void)
340 {
341     HIMC imc;
342
343     imc = ImmGetContext(hwnd);
344     if (imc)
345     {
346         BOOL rc;
347         rc = ImmConfigureIMEA(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
348         ok (rc == 0, "ImmConfigureIMEA did not fail\n");
349         rc = ImmConfigureIMEW(imc, NULL, IME_CONFIG_REGISTERWORD, NULL);
350         ok (rc == 0, "ImmConfigureIMEW did not fail\n");
351     }
352     ImmReleaseContext(hwnd,imc);
353 }
354
355 static void test_ImmAssociateContextEx(void)
356 {
357     HIMC imc;
358     BOOL rc;
359
360     if (!pImmAssociateContextEx) return;
361
362     imc = ImmGetContext(hwnd);
363     if (imc)
364     {
365         HIMC retimc, newimc;
366
367         newimc = ImmCreateContext();
368         ok(newimc != imc, "handles should not be the same\n");
369         rc = pImmAssociateContextEx(NULL, NULL, 0);
370         ok(!rc, "ImmAssociateContextEx succeeded\n");
371         rc = pImmAssociateContextEx(hwnd, NULL, 0);
372         ok(rc, "ImmAssociateContextEx failed\n");
373         rc = pImmAssociateContextEx(NULL, imc, 0);
374         ok(!rc, "ImmAssociateContextEx succeeded\n");
375
376         rc = pImmAssociateContextEx(hwnd, imc, 0);
377         ok(rc, "ImmAssociateContextEx failed\n");
378         retimc = ImmGetContext(hwnd);
379         ok(retimc == imc, "handles should be the same\n");
380         ImmReleaseContext(hwnd,retimc);
381
382         rc = pImmAssociateContextEx(hwnd, newimc, 0);
383         ok(rc, "ImmAssociateContextEx failed\n");
384         retimc = ImmGetContext(hwnd);
385         ok(retimc == newimc, "handles should be the same\n");
386         ImmReleaseContext(hwnd,retimc);
387
388         rc = pImmAssociateContextEx(hwnd, NULL, IACE_DEFAULT);
389         ok(rc, "ImmAssociateContextEx failed\n");
390     }
391     ImmReleaseContext(hwnd,imc);
392 }
393
394 typedef struct _igc_threadinfo {
395     HWND hwnd;
396     HANDLE event;
397     HIMC himc;
398 } igc_threadinfo;
399
400
401 static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam)
402 {
403     HIMC h1,h2;
404     HWND hwnd2;
405     COMPOSITIONFORM cf;
406     POINT pt;
407     igc_threadinfo *info= (igc_threadinfo*)lpParam;
408     info->hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
409                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
410                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
411
412     h1 = ImmGetContext(hwnd);
413     todo_wine ok(info->himc == h1, "hwnd context changed in new thread\n");
414     h2 = ImmGetContext(info->hwnd);
415     todo_wine ok(h2 != h1, "new hwnd in new thread should have different context\n");
416     info->himc = h2;
417     ImmReleaseContext(hwnd,h1);
418
419     hwnd2 = CreateWindowEx(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test",
420                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
421                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
422     h1 = ImmGetContext(hwnd2);
423
424     ok(h1 == h2, "Windows in same thread should have same default context\n");
425     ImmReleaseContext(hwnd2,h1);
426     ImmReleaseContext(info->hwnd,h2);
427     DestroyWindow(hwnd2);
428
429     /* priming for later tests */
430     ImmSetCompositionWindow(h1, &cf);
431     ImmSetStatusWindowPos(h1, &pt);
432
433     SetEvent(info->event);
434     Sleep(INFINITE);
435     return 1;
436 }
437
438 static void test_ImmThreads(void)
439 {
440     HIMC himc, otherHimc, h1;
441     igc_threadinfo threadinfo;
442     HANDLE hThread;
443     DWORD dwThreadId;
444     BOOL rc;
445     LOGFONT lf;
446     COMPOSITIONFORM cf;
447     DWORD status, sentence;
448     POINT pt;
449
450     himc = ImmGetContext(hwnd);
451     threadinfo.event = CreateEvent(NULL, TRUE, FALSE, NULL);
452     threadinfo.himc = himc;
453     hThread = CreateThread(NULL, 0, ImmGetContextThreadFunc, &threadinfo, 0, &dwThreadId );
454     WaitForSingleObject(threadinfo.event, INFINITE);
455
456     otherHimc = ImmGetContext(threadinfo.hwnd);
457
458     todo_wine ok(himc != otherHimc, "Windows from other threads should have different himc\n");
459     todo_wine ok(otherHimc == threadinfo.himc, "Context from other thread should not change in main thread\n");
460
461     if (0) /* FIXME: Causes wine to hang */
462     {
463     h1 = ImmAssociateContext(hwnd,otherHimc);
464     ok(h1 == NULL, "Should fail to be able to Associate a default context from a different thread\n");
465     h1 = ImmGetContext(hwnd);
466     ok(h1 == himc, "Context for window should remain unchanged\n");
467     ImmReleaseContext(hwnd,h1);
468     }
469
470
471     /* OpenStatus */
472     rc = ImmSetOpenStatus(himc, TRUE);
473     ok(rc != 0, "ImmSetOpenStatus failed\n");
474     rc = ImmGetOpenStatus(himc);
475     ok(rc != 0, "ImmGetOpenStatus failed\n");
476     rc = ImmSetOpenStatus(himc, FALSE);
477     ok(rc != 0, "ImmSetOpenStatus failed\n");
478     rc = ImmGetOpenStatus(himc);
479     ok(rc == 0, "ImmGetOpenStatus failed\n");
480
481     rc = ImmSetOpenStatus(otherHimc, TRUE);
482     todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n");
483     rc = ImmGetOpenStatus(otherHimc);
484     todo_wine ok(rc == 0, "ImmGetOpenStatus failed\n");
485     rc = ImmSetOpenStatus(otherHimc, FALSE);
486     todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n");
487     rc = ImmGetOpenStatus(otherHimc);
488     ok(rc == 0, "ImmGetOpenStatus failed\n");
489
490     /* CompositionFont */
491     rc = ImmGetCompositionFont(himc, &lf);
492     ok(rc != 0, "ImmGetCompositionFont failed\n");
493     rc = ImmSetCompositionFont(himc, &lf);
494     ok(rc != 0, "ImmSetCompositionFont failed\n");
495
496     rc = ImmGetCompositionFont(otherHimc, &lf);
497     ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont failed\n");
498     rc = ImmSetCompositionFont(otherHimc, &lf);
499     todo_wine ok(rc == 0, "ImmSetCompositionFont should fail\n");
500
501     /* CompositionWindow */
502     rc = ImmSetCompositionWindow(himc, &cf);
503     ok(rc != 0, "ImmSetCompositionWindow failed\n");
504     rc = ImmGetCompositionWindow(himc, &cf);
505     ok(rc != 0, "ImmGetCompositionWindow failed\n");
506
507     rc = ImmSetCompositionWindow(otherHimc, &cf);
508     todo_wine ok(rc == 0, "ImmSetCompositionWindow should fail\n");
509     rc = ImmGetCompositionWindow(otherHimc, &cf);
510     ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n");
511
512     /* ConversionStatus */
513     rc = ImmGetConversionStatus(himc, &status, &sentence);
514     ok(rc != 0, "ImmGetConversionStatus failed\n");
515     rc = ImmSetConversionStatus(himc, status, sentence);
516     ok(rc != 0, "ImmSetConversionStatus failed\n");
517
518     rc = ImmGetConversionStatus(otherHimc, &status, &sentence);
519     ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n");
520     rc = ImmSetConversionStatus(otherHimc, status, sentence);
521     todo_wine ok(rc == 0, "ImmSetConversionStatus should fail\n");
522
523     /* StatusWindowPos */
524     rc = ImmSetStatusWindowPos(himc, &pt);
525     ok(rc != 0, "ImmSetStatusWindowPos failed\n");
526     rc = ImmGetStatusWindowPos(himc, &pt);
527     ok(rc != 0, "ImmGetStatusWindowPos failed\n");
528
529     rc = ImmSetStatusWindowPos(otherHimc, &pt);
530     todo_wine ok(rc == 0, "ImmSetStatusWindowPos should fail\n");
531     rc = ImmGetStatusWindowPos(otherHimc, &pt);
532     ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n");
533
534     ImmReleaseContext(threadinfo.hwnd,otherHimc);
535     ImmReleaseContext(hwnd,himc);
536
537     DestroyWindow(threadinfo.hwnd);
538     TerminateThread(hThread, 1);
539
540     himc = ImmGetContext(GetDesktopWindow());
541     todo_wine ok(himc == NULL, "Should not be able to get himc from other process window\n");
542 }
543
544 static void test_ImmIsUIMessage(void)
545 {
546     struct test
547     {
548         UINT msg;
549         BOOL ret;
550     };
551
552     static const struct test tests[] =
553     {
554         { WM_MOUSEMOVE,            FALSE },
555         { WM_IME_STARTCOMPOSITION, TRUE  },
556         { WM_IME_ENDCOMPOSITION,   TRUE  },
557         { WM_IME_COMPOSITION,      TRUE  },
558         { WM_IME_SETCONTEXT,       TRUE  },
559         { WM_IME_NOTIFY,           TRUE  },
560         { WM_IME_CONTROL,          FALSE },
561         { WM_IME_COMPOSITIONFULL,  TRUE  },
562         { WM_IME_SELECT,           TRUE  },
563         { WM_IME_CHAR,             FALSE },
564         { 0x287 /* FIXME */,       TRUE  },
565         { WM_IME_REQUEST,          FALSE },
566         { WM_IME_KEYDOWN,          FALSE },
567         { WM_IME_KEYUP,            FALSE },
568         { 0, FALSE } /* mark the end */
569     };
570
571     const struct test *test;
572     BOOL ret;
573
574     if (!pImmIsUIMessageA) return;
575
576     for (test = tests; test->msg; test++)
577     {
578         msg_spy_flush_msgs();
579         ret = pImmIsUIMessageA(NULL, test->msg, 0, 0);
580         ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
581         ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x for NULL hwnd\n", test->msg);
582
583         ret = pImmIsUIMessageA(hwnd, test->msg, 0, 0);
584         ok(ret == test->ret, "ImmIsUIMessageA returned %x for %x\n", ret, test->msg);
585         if (ret)
586             ok(msg_spy_find_msg(test->msg) != NULL, "Windows does send 0x%x\n", test->msg);
587         else
588             ok(!msg_spy_find_msg(test->msg), "Windows does not send 0x%x\n", test->msg);
589     }
590 }
591
592 static void test_ImmGetContext(void)
593 {
594     HIMC himc;
595     DWORD err;
596
597     SetLastError(0xdeadbeef);
598     himc = ImmGetContext((HWND)0xffffffff);
599     err = GetLastError();
600     ok(himc == NULL, "ImmGetContext succeeded\n");
601     ok(err == ERROR_INVALID_WINDOW_HANDLE, "got %u\n", err);
602
603     himc = ImmGetContext(hwnd);
604     ok(himc != NULL, "ImmGetContext failed\n");
605     ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n");
606 }
607
608 static void test_ImmGetDescription(void)
609 {
610     HKL hkl;
611     WCHAR japime[] = { 'E', '0', '0', '1', '0', '4', '1', '1', 0 };
612     WCHAR descW[100];
613     CHAR descA[100];
614     UINT ret, lret;
615
616     /* FIXME: invalid keyboard layouts should not pass */
617     ret = ImmGetDescriptionW(NULL, NULL, 0);
618     todo_wine ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
619
620     /* load a language with valid IMM descriptions */
621     hkl = LoadKeyboardLayoutW(japime, KLF_ACTIVATE);
622     todo_wine ok(hkl != 0, "LoadKeyboardLayoutW failed, expected != 0.\n");
623
624     ret = ImmGetDescriptionW(hkl, NULL, 0);
625     if(!ret)
626     {
627         win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n");
628         return;
629     }
630
631     ret = ImmGetDescriptionW(hkl, descW, 0);
632     ok(ret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
633
634     lret = ImmGetDescriptionW(hkl, descW, ret + 1);
635     ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
636     ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
637
638     lret = ImmGetDescriptionA(hkl, descA, ret + 1);
639     ok(lret, "ImmGetDescriptionA failed, expected != 0 received 0.\n");
640     todo_wine ok(lret == ret, "ImmGetDescriptionA failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
641
642     ret /= 2; /* try to copy partially */
643     lret = ImmGetDescriptionW(hkl, descW, ret + 1);
644     ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n");
645     ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret);
646
647     ret = ImmGetDescriptionW(hkl, descW, 1);
648     ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret);
649
650     UnloadKeyboardLayout(hkl);
651 }
652
653 static void test_ImmDefaultHwnd(void)
654 {
655     HIMC imc1, imc2, imc3;
656     HWND def1, def3;
657     HWND hwnd;
658
659     hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
660                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
661                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
662
663     ShowWindow(hwnd, SW_SHOWNORMAL);
664
665     imc1 = ImmGetContext(hwnd);
666     if (!imc1)
667     {
668         win_skip("IME support not implemented\n");
669         return;
670     }
671
672     def1 = ImmGetDefaultIMEWnd(hwnd);
673
674     imc2 = ImmCreateContext();
675     ImmSetOpenStatus(imc2, TRUE);
676
677     imc3 = ImmGetContext(hwnd);
678     def3 = ImmGetDefaultIMEWnd(hwnd);
679
680     ok(def3 == def1, "Default IME window should not change\n");
681     ok(imc1 == imc3, "IME context should not change\n");
682     ImmSetOpenStatus(imc2, FALSE);
683
684     ImmReleaseContext(hwnd, imc1);
685     ImmReleaseContext(hwnd, imc3);
686     ImmDestroyContext(imc2);
687     DestroyWindow(hwnd);
688 }
689
690 static void test_ImmMessages(void)
691 {
692     CANDIDATEFORM cf;
693     imm_msgs *msg;
694     HWND defwnd;
695     HIMC imc;
696     UINT idx = 0;
697
698     HWND hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "Wine imm32.dll test",
699                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
700                           240, 120, NULL, NULL, GetModuleHandle(0), NULL);
701
702     ShowWindow(hwnd, SW_SHOWNORMAL);
703     defwnd = ImmGetDefaultIMEWnd(hwnd);
704     imc = ImmGetContext(hwnd);
705
706     ImmSetOpenStatus(imc, TRUE);
707     msg_spy_flush_msgs();
708     SendMessage(defwnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, (LPARAM)&cf );
709     do
710     {
711         msg = msg_spy_find_next_msg(WM_IME_CONTROL,&idx);
712         if (msg) ok(!msg->post, "Message should not be posted\n");
713     } while (msg);
714     msg_spy_flush_msgs();
715     ImmSetOpenStatus(imc, FALSE);
716     ImmReleaseContext(hwnd, imc);
717     DestroyWindow(hwnd);
718 }
719
720 static LRESULT CALLBACK processkey_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
721         LPARAM lParam )
722 {
723     return DefWindowProcW(hWnd, msg, wParam, lParam);
724 }
725
726 static void test_ime_processkey(void)
727 {
728     WCHAR classNameW[] = {'P','r','o','c','e','s','s', 'K','e','y','T','e','s','t','C','l','a','s','s',0};
729     WCHAR windowNameW[] = {'P','r','o','c','e','s','s', 'K','e','y',0};
730
731     MSG msg;
732     WNDCLASSW wclass;
733     HANDLE hInstance = GetModuleHandleW(NULL);
734     TEST_INPUT inputs[2];
735     HIMC imc;
736     INT rc;
737     HWND hWndTest;
738
739     wclass.lpszClassName = classNameW;
740     wclass.style         = CS_HREDRAW | CS_VREDRAW;
741     wclass.lpfnWndProc   = processkey_wnd_proc;
742     wclass.hInstance     = hInstance;
743     wclass.hIcon         = LoadIcon(0, IDI_APPLICATION);
744     wclass.hCursor       = LoadCursor( NULL, IDC_ARROW);
745     wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
746     wclass.lpszMenuName  = 0;
747     wclass.cbClsExtra    = 0;
748     wclass.cbWndExtra    = 0;
749     if(!RegisterClassW(&wclass)){
750         win_skip("Failed to register window.\n");
751         return;
752     }
753
754     /* create the test window that will receive the keystrokes */
755     hWndTest = CreateWindowW(wclass.lpszClassName, windowNameW,
756                              WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
757                              NULL, NULL, hInstance, NULL);
758
759     ShowWindow(hWndTest, SW_SHOW);
760     SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
761     SetForegroundWindow(hWndTest);
762     UpdateWindow(hWndTest);
763
764     imc = ImmGetContext(hWndTest);
765     if (!imc)
766     {
767         win_skip("IME not supported\n");
768         DestroyWindow(hWndTest);
769         return;
770     }
771
772     rc = ImmSetOpenStatus(imc, TRUE);
773     if (rc != TRUE)
774     {
775         win_skip("Unable to open IME\n");
776         ImmReleaseContext(hWndTest, imc);
777         DestroyWindow(hWndTest);
778         return;
779     }
780
781     /* flush pending messages */
782     while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
783
784     SetFocus(hWndTest);
785
786     /* init input data that never changes */
787     inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
788     inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
789     inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
790
791     /* Pressing a key */
792     inputs[0].u.ki.wVk = 0x41;
793     inputs[0].u.ki.wScan = 0x1e;
794     inputs[0].u.ki.dwFlags = 0x0;
795
796     pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
797
798     while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
799         if(msg.message != WM_KEYDOWN)
800             PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
801         else
802         {
803             ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
804             PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
805             if(msg.wParam == VK_PROCESSKEY)
806                 trace("ProcessKey was correctly found\n");
807         }
808         TranslateMessage(&msg);
809         DispatchMessageW(&msg);
810     }
811
812     inputs[0].u.ki.wVk = 0x41;
813     inputs[0].u.ki.wScan = 0x1e;
814     inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
815
816     pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
817
818     while(PeekMessageW(&msg, hWndTest, 0, 0, PM_NOREMOVE)) {
819         if(msg.message != WM_KEYUP)
820             PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
821         else
822         {
823             ok(msg.wParam != VK_PROCESSKEY,"Incorrect ProcessKey Found\n");
824             PeekMessageW(&msg, hWndTest, 0, 0, PM_REMOVE);
825             ok(msg.wParam != VK_PROCESSKEY,"ProcessKey should still not be Found\n");
826         }
827         TranslateMessage(&msg);
828         DispatchMessageW(&msg);
829     }
830
831     ImmReleaseContext(hWndTest, imc);
832     ImmSetOpenStatus(imc, FALSE);
833     DestroyWindow(hWndTest);
834 }
835
836 START_TEST(imm32) {
837     if (init())
838     {
839         test_ImmNotifyIME();
840         test_ImmGetCompositionString();
841         test_ImmSetCompositionString();
842         test_ImmIME();
843         test_ImmAssociateContextEx();
844         test_ImmThreads();
845         test_ImmIsUIMessage();
846         test_ImmGetContext();
847         test_ImmGetDescription();
848         test_ImmDefaultHwnd();
849         msg_spy_cleanup();
850         /* Reinitialize the hooks to capture all windows */
851         msg_spy_init(NULL);
852         test_ImmMessages();
853         msg_spy_cleanup();
854         if (pSendInput)
855             test_ime_processkey();
856         else win_skip("SendInput is not available\n");
857     }
858     cleanup();
859 }