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