1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         Test for TrackMouseEvent
5  * PROGRAMMERS:     Giannis Adamopoulos
6  */
7 
8 #include <apitest.h>
9 #include <wingdi.h>
10 #include <winuser.h>
11 #include <undocuser.h>
12 
13 #include <msgtrace.h>
14 #include <user32testhelpers.h>
15 
16 HWND hWnd1, hWnd2, hWnd3;
17 HHOOK hMouseHookLL, hMouseHook;
18 int ignore_timer = 0, ignore_mouse = 0, ignore_mousell = 0;
19 
20 static int get_iwnd(HWND hWnd)
21 {
22     if(hWnd == hWnd1) return 1;
23     else if(hWnd == hWnd2) return 2;
24     else if(hWnd == hWnd3) return 3;
25     else return 0;
26 }
27 
28 LRESULT CALLBACK TmeTestProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
29 {
30     int iwnd = get_iwnd(hWnd);
31 
32     if(message == WM_PAINT)
33     {
34         PAINTSTRUCT ps;
35         BeginPaint(hWnd, &ps);
36         Rectangle(ps.hdc,  ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
37         EndPaint(hWnd, &ps);
38     }
39 
40     if(message > WM_USER || !iwnd || IsDWmMsg(message) || IseKeyMsg(message))
41         return DefWindowProc(hWnd, message, wParam, lParam);
42 
43     switch(message)
44     {
45     case WM_IME_SETCONTEXT:
46     case WM_IME_NOTIFY :
47     case WM_GETICON :
48     case WM_GETTEXT:
49         break;
50     case WM_SYSTIMER:
51         ok(0, "Got unexpected WM_SYSTIMER in the winproc. wParam=%d\n", wParam);
52         break;
53     default:
54         RECORD_MESSAGE(iwnd, message, SENT, 0,0);
55     }
56     return DefWindowProc(hWnd, message, wParam, lParam);
57 }
58 
59 static LRESULT CALLBACK MouseLLHookProc(int nCode, WPARAM wParam, LPARAM lParam)
60 {
61     LRESULT ret;
62     RECORD_MESSAGE(0, WH_MOUSE_LL, HOOK, wParam, 0);
63     ret = CallNextHookEx(hMouseHookLL, nCode, wParam, lParam);
64     if(ignore_mousell)
65         return TRUE;
66     return ret;
67 }
68 
69 static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
70 {
71     MOUSEHOOKSTRUCT *hs = (MOUSEHOOKSTRUCT*) lParam;
72     LRESULT ret;
73     RECORD_MESSAGE(get_iwnd(hs->hwnd), WH_MOUSE, HOOK, wParam, hs->wHitTestCode);
74     ret = CallNextHookEx(hMouseHook, nCode, wParam, lParam);
75     if(ignore_mouse)
76         return TRUE;
77     return ret;
78 }
79 
80 static void FlushMessages()
81 {
82     MSG msg;
83 
84     while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
85     {
86         int iwnd = get_iwnd(msg.hwnd);
87         if(iwnd)
88         {
89             if(msg.message == WM_SYSTIMER)
90             {
91                 RECORD_MESSAGE(iwnd, msg.message, POST,msg.wParam,0);
92                 if(ignore_timer)
93                     continue;
94             }
95             else if(!(msg.message > WM_USER || !iwnd || IsDWmMsg(msg.message) || IseKeyMsg(msg.message)))
96                 RECORD_MESSAGE(iwnd, msg.message, POST,0,0);
97         }
98         DispatchMessageA( &msg );
99     }
100 }
101 
102 #define FLUSH_MESSAGES(expected, notexpected) \
103     { EXPECT_QUEUE_STATUS(expected, notexpected);\
104       FlushMessages();\
105     }
106 
107 static void create_test_windows()
108 {
109     hMouseHookLL = SetWindowsHookExW(WH_MOUSE_LL, MouseLLHookProc, GetModuleHandleW( NULL ), 0);
110     hMouseHook = SetWindowsHookExW(WH_MOUSE, MouseHookProc, GetModuleHandleW( NULL ), GetCurrentThreadId());
111     ok(hMouseHook!=NULL,"failed to set hook\n");
112     ok(hMouseHookLL!=NULL,"failed to set hook\n");
113 
114     RegisterSimpleClass(TmeTestProc, L"testClass");
115 
116     hWnd1 = CreateWindowW(L"testClass", L"test", WS_OVERLAPPEDWINDOW,
117                          100, 100, 500, 500, NULL, NULL, 0, NULL);
118     hWnd2 = CreateWindowW(L"testClass", L"test", WS_CHILD,
119                          50, 50, 200, 200, hWnd1, NULL, 0, NULL);
120     hWnd3 = CreateWindowW(L"testClass", L"test", WS_CHILD,
121                          150, 150, 200, 200, hWnd1, NULL, 0, NULL);
122 
123     ShowWindow(hWnd1, SW_SHOW);
124     UpdateWindow(hWnd1);
125     ShowWindow(hWnd2, SW_SHOW);
126     UpdateWindow(hWnd2);
127     ShowWindow(hWnd3, SW_SHOWNORMAL);
128     UpdateWindow(hWnd3);
129     //SetWindowPos (hWnd3, HWND_TOP, 0,0,0,0, SWP_NOMOVE|SWP_NOREDRAW);
130 }
131 
132 static void destroy_test_window()
133 {
134     DestroyWindow(hWnd1);
135     UnregisterClassW(L"testClass", 0);
136     UnhookWindowsHookEx(hMouseHookLL);
137     UnhookWindowsHookEx(hMouseHook);
138 }
139 
140 static void TmeStartTracking(HWND hwnd, DWORD Flags)
141 {
142     TRACKMOUSEEVENT tme;
143     tme.cbSize = sizeof(tme);
144     tme.dwFlags = Flags;
145     tme.hwndTrack  = hwnd;
146     tme.dwHoverTime = 1;
147     if(!TrackMouseEvent(&tme))
148     {
149         trace("failed!\n");
150     }
151 }
152 
153 DWORD TmeQuery(HWND hwnd)
154 {
155     TRACKMOUSEEVENT tme;
156     tme.cbSize = sizeof(tme);
157     tme.dwFlags = TME_QUERY|TME_HOVER|TME_LEAVE;
158     tme.hwndTrack  = hwnd;
159     TrackMouseEvent(&tme);
160     return tme.dwFlags;
161 }
162 
163 #define EXPECT_TME_FLAGS(hWnd, expected)                                                                 \
164     { DWORD flags = TmeQuery(hWnd);                                                                      \
165       ok(flags == (expected),"wrong tme flags. expected %li, and got %li\n", (DWORD)(expected), flags);  \
166     }
167 
168 #define MOVE_CURSOR(x,y) mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE ,                           \
169                                      x*(65535/GetSystemMetrics(SM_CXVIRTUALSCREEN)),                     \
170                                      y*(65535/GetSystemMetrics(SM_CYVIRTUALSCREEN)) , 0,0);
171 
172 /* the mouse moves over hwnd2 */
173 MSG_ENTRY mousemove2_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
174                               {2, WM_NCHITTEST},
175                               {2, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
176                               {2, WM_SETCURSOR},
177                               {1, WM_SETCURSOR},
178                               {2, WM_MOUSEMOVE, POST},
179                                {0,0}};
180 
181 /* the mouse hovers hwnd2 */
182 MSG_ENTRY mousehover2_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
183                                {2, WM_NCHITTEST},
184                                {2, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
185                                {2, WM_SETCURSOR},
186                                {1, WM_SETCURSOR},
187                                {2, WM_MOUSEMOVE, POST},
188                                {2, WM_SYSTIMER, POST, ID_TME_TIMER},
189                                {2, WM_MOUSEHOVER, POST},
190                                 {0,0}};
191 
192 /* the mouse leaves hwnd2 and moves to hwnd1 */
193 MSG_ENTRY mouseleave2to1_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
194                                   {1, WM_NCHITTEST},
195                                   {1, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
196                                   {1, WM_SETCURSOR},
197                                   {1, WM_MOUSEMOVE, POST},
198                                   {2, WM_MOUSELEAVE, POST},
199                                    {0,0}};
200 
201 /* the mouse leaves hwnd2 and moves to hwnd3 */
202 MSG_ENTRY mouseleave2to3_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
203                                   {3, WM_NCHITTEST},
204                                   {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
205                                   {3, WM_SETCURSOR},
206                                   {1, WM_SETCURSOR},
207                                   {3, WM_MOUSEMOVE, POST},
208                                   {2, WM_MOUSELEAVE, POST},
209                                    {0,0}};
210 
211 /* the mouse hovers hwnd3 */
212 MSG_ENTRY mousehover3_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
213                                {3, WM_NCHITTEST},
214                                {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
215                                {3, WM_SETCURSOR},
216                                {1, WM_SETCURSOR},
217                                {3, WM_MOUSEMOVE, POST},
218                                {3, WM_SYSTIMER, POST, ID_TME_TIMER},
219                                {3, WM_MOUSEHOVER, POST},
220                                 {0,0}};
221 
222 /* the mouse hovers hwnd3 without moving */
223 MSG_ENTRY mousehover3_nomove_chain[]={{3, WM_SYSTIMER, POST, ID_TME_TIMER},
224                                       {3, WM_MOUSEHOVER, POST},
225                                       {0,0}};
226 
227 /* the mouse hovers hwnd3 and the timer is not dispatched */
228 MSG_ENTRY mousehover3_droptimer_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
229                                          {3, WM_NCHITTEST},
230                                          {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
231                                          {3, WM_SETCURSOR},
232                                          {1, WM_SETCURSOR},
233                                          {3, WM_MOUSEMOVE, POST},
234                                          {3, WM_SYSTIMER, POST, ID_TME_TIMER},
235                                          {0,0}};
236 
237 /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE */
238 MSG_ENTRY mousehover3_dropmouse_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
239                                          {3, WM_NCHITTEST},
240                                          {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
241                                          {3, WM_SYSTIMER, POST, ID_TME_TIMER},
242                                          {3, WM_MOUSEHOVER, POST},
243                                          {0,0}};
244 
245 /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE_LL */
246 MSG_ENTRY mousehover3_dropmousell_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
247                                          {3, WM_SYSTIMER, POST, ID_TME_TIMER},
248                                          {3, WM_MOUSEHOVER, POST},
249                                          {0,0}};
250 
251 /* the mouse leaves hwnd3 and moves to hwnd2 and mouse message is dropped by WH_MOUSE */
252 MSG_ENTRY mouseleave3to2_dropmouse_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
253                                             {2, WM_NCHITTEST},
254                                             {2, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
255                                             {0,0}};
256 
257 /* the mouse leaves hwnd3 and moves to hwnd2 and mouse message is dropped by WH_MOUSE_LL */
258 MSG_ENTRY mouseleave3to2_dropmousell_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
259                                              {0,0}};
260 
261 /* after WH_MOUSE drops WM_MOUSEMOVE, WM_MOUSELEAVE is still in the queue */
262 MSG_ENTRY mouseleave3_remainging_chain[]={{3, WM_MOUSELEAVE, POST},
263                                           {0,0}};
264 
265 void Test_TrackMouseEvent()
266 {
267     MOVE_CURSOR(0,0);
268     create_test_windows();
269     FlushMessages();
270     EMPTY_CACHE();
271 
272     /* the mouse moves over hwnd2 */
273     MOVE_CURSOR(220,220);
274     FlushMessages();
275     COMPARE_CACHE(mousemove2_chain);
276     EXPECT_TME_FLAGS(hWnd2, 0);
277 
278     /* Begin tracking mouse events for hWnd2 */
279     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
280     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
281     FlushMessages();
282     COMPARE_CACHE(empty_chain);
283 
284     /* the mouse hovers hwnd2 */
285     MOVE_CURSOR(221, 221);
286     Sleep(100);
287     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
288     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);
289     EXPECT_TME_FLAGS(hWnd2, TME_LEAVE);
290     COMPARE_CACHE(mousehover2_chain);
291 
292     /* the mouse leaves hwnd2 and moves to hwnd1 */
293     MOVE_CURSOR(150, 150);
294     EXPECT_TME_FLAGS(hWnd2, TME_LEAVE);
295     FLUSH_MESSAGES(QS_MOUSEMOVE,QS_TIMER );
296     EXPECT_TME_FLAGS(hWnd2, 0);
297     COMPARE_CACHE(mouseleave2to1_chain);
298 
299     FlushMessages();
300     COMPARE_CACHE(empty_chain);
301 
302     /* the mouse moves over hwnd2 */
303     MOVE_CURSOR(220,220);
304     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);
305     COMPARE_CACHE(mousemove2_chain);
306     EXPECT_TME_FLAGS(hWnd2, 0);
307 
308     FlushMessages();
309     COMPARE_CACHE(empty_chain);
310 
311     /* Begin tracking mouse events for hWnd2 */
312     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
313     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
314     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
315     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
316     FLUSH_MESSAGES(0, QS_TIMER|QS_MOUSEMOVE);
317     COMPARE_CACHE(empty_chain);
318     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
319 
320     /* the mouse moves from hwnd2 to the intersection of hwnd2 and hwnd3 */
321     MOVE_CURSOR(300,300);
322     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);
323     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
324     COMPARE_CACHE(mousemove2_chain);
325 
326     FlushMessages();
327     COMPARE_CACHE(empty_chain);
328 
329     /* the mouse moves from hwnd2 to hwnd3 */
330     MOVE_CURSOR(400,400);
331     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);
332     EXPECT_TME_FLAGS(hWnd2, 0);
333     COMPARE_CACHE(mouseleave2to3_chain);
334 
335     FlushMessages();
336     COMPARE_CACHE(empty_chain);
337 
338     /* Begin tracking mouse events for hWnd3 */
339     TmeStartTracking(hWnd3, TME_HOVER|TME_LEAVE);
340     FLUSH_MESSAGES(0, QS_TIMER|QS_MOUSEMOVE);
341     COMPARE_CACHE(empty_chain);
342     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
343 
344     /* the mouse hovers hwnd3 */
345     MOVE_CURSOR(401,401);
346     Sleep(100);
347     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
348     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);
349     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
350     COMPARE_CACHE(mousehover3_chain);
351 
352     FlushMessages();
353     COMPARE_CACHE(empty_chain);
354 
355     /* Begin tracking mouse events for hWnd3 */
356     TmeStartTracking(hWnd3, TME_HOVER );
357     FLUSH_MESSAGES(0, QS_TIMER|QS_MOUSEMOVE);
358     COMPARE_CACHE(empty_chain);
359     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
360 
361     /* make sure that the timer will fire before the mouse moves */
362     Sleep(100);
363     FlushMessages();
364     COMPARE_CACHE(mousehover3_nomove_chain);
365     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
366 
367     Sleep(100);
368     FlushMessages();
369     COMPARE_CACHE(empty_chain);
370 
371     /* the mouse hovers hwnd3 and the timer is not dispatched*/
372     TmeStartTracking(hWnd3, TME_HOVER );
373     ignore_timer = TRUE;
374     MOVE_CURSOR(400,400);
375     Sleep(100);
376     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
377     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);     /* the loop drops WM_SYSTIMER */
378     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE); /* TME_HOVER is still active  */
379     COMPARE_CACHE(mousehover3_droptimer_chain);   /* we get no WM_MOUSEHOVER    */
380     ignore_timer = FALSE;
381 
382     /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE_LL */
383     ignore_mousell = TRUE;
384     MOVE_CURSOR(402,402);
385     Sleep(100);
386     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
387     FLUSH_MESSAGES(QS_TIMER, QS_MOUSEMOVE);         /* WH_MOUSE_LL drops WM_MOUSEMOVE */
388     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
389     COMPARE_CACHE(mousehover3_dropmousell_chain);   /* we get WM_MOUSEHOVER normaly */
390     ignore_mousell = FALSE;
391 
392     FlushMessages();
393     COMPARE_CACHE(empty_chain);
394 
395     /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE */
396     ignore_mouse = TRUE;
397     TmeStartTracking(hWnd3, TME_HOVER );
398     MOVE_CURSOR(401,401);
399     Sleep(100);
400     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
401     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);     /* WH_MOUSE drops WM_MOUSEMOVE */
402     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
403     COMPARE_CACHE(mousehover3_dropmouse_chain);   /* we get WM_MOUSEHOVER normaly */
404     ignore_mouse = FALSE;
405 
406     FlushMessages();
407     COMPARE_CACHE(empty_chain);
408 
409     /* the mouse leaves hwnd3 and moves to hwnd2 and mouse message is dropped by WH_MOUSE_LL */
410     ignore_mousell = TRUE;
411     TmeStartTracking(hWnd3, TME_LEAVE );
412     MOVE_CURSOR(220,220);
413     FLUSH_MESSAGES(0, QS_MOUSEMOVE|QS_TIMER);         /* WH_MOUSE drops WM_MOUSEMOVE */
414     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);               /* all flags are removed */
415     COMPARE_CACHE(mouseleave3to2_dropmousell_chain);  /* we get no WM_MOUSELEAVE */
416     ignore_mousell = FALSE;
417 
418     FlushMessages();
419     COMPARE_CACHE(empty_chain);
420 
421     /* the mouse leaves hwnd3 and moves to hwnd2 and mouse message is dropped by WH_MOUSE */
422     ignore_mouse = TRUE;
423     MOVE_CURSOR(220,220);
424     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);         /* WH_MOUSE drops WM_MOUSEMOVE */
425     EXPECT_TME_FLAGS(hWnd3, 0);                     /* all flags are removed */
426     COMPARE_CACHE(mouseleave3to2_dropmouse_chain);  /* we get no WM_MOUSELEAVE */
427     ignore_mouse = FALSE;
428 
429     /* after WH_MOUSE drops WM_MOUSEMOVE, WM_MOUSELEAVE is still in the queue */
430     FLUSH_MESSAGES(QS_POSTMESSAGE, QS_TIMER|QS_MOUSEMOVE);
431     COMPARE_CACHE(mouseleave3_remainging_chain);
432 
433     FlushMessages();
434     COMPARE_CACHE(empty_chain);
435 
436     destroy_test_window();
437 }
438 
439 START_TEST(TrackMouseEvent)
440 {
441     Test_TrackMouseEvent();
442 }
443