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 HWND hWnd1, hWnd2, hWnd3;
11 HHOOK hMouseHookLL, hMouseHook;
12 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 MSG_ENTRY mousemove2_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
168                               {2, WM_NCHITTEST},
169                               {2, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
170                               {2, WM_SETCURSOR},
171                               {1, WM_SETCURSOR},
172                               {2, WM_MOUSEMOVE, POST},
173                                {0,0}};
174 
175 /* the mouse hovers hwnd2 */
176 MSG_ENTRY mousehover2_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
177                                {2, WM_NCHITTEST},
178                                {2, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
179                                {2, WM_SETCURSOR},
180                                {1, WM_SETCURSOR},
181                                {2, WM_MOUSEMOVE, POST},
182                                {2, WM_SYSTIMER, POST, ID_TME_TIMER},
183                                {2, WM_MOUSEHOVER, POST},
184                                 {0,0}};
185 
186 /* the mouse leaves hwnd2 and moves to hwnd1 */
187 MSG_ENTRY mouseleave2to1_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
188                                   {1, WM_NCHITTEST},
189                                   {1, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
190                                   {1, WM_SETCURSOR},
191                                   {1, WM_MOUSEMOVE, POST},
192                                   {2, WM_MOUSELEAVE, POST},
193                                    {0,0}};
194 
195 /* the mouse leaves hwnd2 and moves to hwnd3 */
196 MSG_ENTRY mouseleave2to3_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
197                                   {3, WM_NCHITTEST},
198                                   {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
199                                   {3, WM_SETCURSOR},
200                                   {1, WM_SETCURSOR},
201                                   {3, WM_MOUSEMOVE, POST},
202                                   {2, WM_MOUSELEAVE, POST},
203                                    {0,0}};
204 
205 /* the mouse hovers hwnd3 */
206 MSG_ENTRY mousehover3_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
207                                {3, WM_NCHITTEST},
208                                {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
209                                {3, WM_SETCURSOR},
210                                {1, WM_SETCURSOR},
211                                {3, WM_MOUSEMOVE, POST},
212                                {3, WM_SYSTIMER, POST, ID_TME_TIMER},
213                                {3, WM_MOUSEHOVER, POST},
214                                 {0,0}};
215 
216 /* the mouse hovers hwnd3 without moving */
217 MSG_ENTRY mousehover3_nomove_chain[]={{3, WM_SYSTIMER, POST, ID_TME_TIMER},
218                                       {3, WM_MOUSEHOVER, POST},
219                                       {0,0}};
220 
221 /* the mouse hovers hwnd3 and the timer is not dispatched */
222 MSG_ENTRY mousehover3_droptimer_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
223                                          {3, WM_NCHITTEST},
224                                          {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
225                                          {3, WM_SETCURSOR},
226                                          {1, WM_SETCURSOR},
227                                          {3, WM_MOUSEMOVE, POST},
228                                          {3, WM_SYSTIMER, POST, ID_TME_TIMER},
229                                          {0,0}};
230 
231 /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE */
232 MSG_ENTRY mousehover3_dropmouse_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
233                                          {3, WM_NCHITTEST},
234                                          {3, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
235                                          {3, WM_SYSTIMER, POST, ID_TME_TIMER},
236                                          {3, WM_MOUSEHOVER, POST},
237                                          {0,0}};
238 
239 /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE_LL */
240 MSG_ENTRY mousehover3_dropmousell_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
241                                          {3, WM_SYSTIMER, POST, ID_TME_TIMER},
242                                          {3, WM_MOUSEHOVER, POST},
243                                          {0,0}};
244 
245 /* the mouse leaves hwnd3 and moves to hwnd2 and mouse message is dropped by WH_MOUSE */
246 MSG_ENTRY mouseleave3to2_dropmouse_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
247                                             {2, WM_NCHITTEST},
248                                             {2, WH_MOUSE,HOOK, WM_MOUSEMOVE, HTCLIENT},
249                                             {0,0}};
250 
251 /* the mouse leaves hwnd3 and moves to hwnd2 and mouse message is dropped by WH_MOUSE_LL */
252 MSG_ENTRY mouseleave3to2_dropmousell_chain[]={{0, WH_MOUSE_LL, HOOK, WM_MOUSEMOVE},
253                                              {0,0}};
254 
255 /* after WH_MOUSE drops WM_MOUSEMOVE, WM_MOUSELEAVE is still in the queue */
256 MSG_ENTRY mouseleave3_remainging_chain[]={{3, WM_MOUSELEAVE, POST},
257                                           {0,0}};
258 
259 void Test_TrackMouseEvent()
260 {
261     MOVE_CURSOR(0,0);
262     create_test_windows();
263     FlushMessages();
264     EMPTY_CACHE();
265 
266     /* the mouse moves over hwnd2 */
267     MOVE_CURSOR(220,220);
268     FlushMessages();
269     COMPARE_CACHE(mousemove2_chain);
270     EXPECT_TME_FLAGS(hWnd2, 0);
271 
272     /* Begin tracking mouse events for hWnd2 */
273     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
274     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
275     FlushMessages();
276     COMPARE_CACHE(empty_chain);
277 
278     /* the mouse hovers hwnd2 */
279     MOVE_CURSOR(221, 221);
280     Sleep(100);
281     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
282     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);
283     EXPECT_TME_FLAGS(hWnd2, TME_LEAVE);
284     COMPARE_CACHE(mousehover2_chain);
285 
286     /* the mouse leaves hwnd2 and moves to hwnd1 */
287     MOVE_CURSOR(150, 150);
288     EXPECT_TME_FLAGS(hWnd2, TME_LEAVE);
289     FLUSH_MESSAGES(QS_MOUSEMOVE,QS_TIMER );
290     EXPECT_TME_FLAGS(hWnd2, 0);
291     COMPARE_CACHE(mouseleave2to1_chain);
292 
293     FlushMessages();
294     COMPARE_CACHE(empty_chain);
295 
296     /* the mouse moves over hwnd2 */
297     MOVE_CURSOR(220,220);
298     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);
299     COMPARE_CACHE(mousemove2_chain);
300     EXPECT_TME_FLAGS(hWnd2, 0);
301 
302     FlushMessages();
303     COMPARE_CACHE(empty_chain);
304 
305     /* Begin tracking mouse events for hWnd2 */
306     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
307     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
308     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
309     TmeStartTracking(hWnd2, TME_HOVER|TME_LEAVE);
310     FLUSH_MESSAGES(0, QS_TIMER|QS_MOUSEMOVE);
311     COMPARE_CACHE(empty_chain);
312     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
313 
314     /* the mouse moves from hwnd2 to the intersection of hwnd2 and hwnd3 */
315     MOVE_CURSOR(300,300);
316     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);
317     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
318     COMPARE_CACHE(mousemove2_chain);
319 
320     FlushMessages();
321     COMPARE_CACHE(empty_chain);
322 
323     /* the mouse moves from hwnd2 to hwnd3 */
324     MOVE_CURSOR(400,400);
325     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);
326     EXPECT_TME_FLAGS(hWnd2, 0);
327     COMPARE_CACHE(mouseleave2to3_chain);
328 
329     FlushMessages();
330     COMPARE_CACHE(empty_chain);
331 
332     /* Begin tracking mouse events for hWnd3 */
333     TmeStartTracking(hWnd3, TME_HOVER|TME_LEAVE);
334     FLUSH_MESSAGES(0, QS_TIMER|QS_MOUSEMOVE);
335     COMPARE_CACHE(empty_chain);
336     EXPECT_TME_FLAGS(hWnd2, TME_HOVER|TME_LEAVE);
337 
338     /* the mouse hovers hwnd3 */
339     MOVE_CURSOR(401,401);
340     Sleep(100);
341     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
342     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);
343     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
344     COMPARE_CACHE(mousehover3_chain);
345 
346     FlushMessages();
347     COMPARE_CACHE(empty_chain);
348 
349     /* Begin tracking mouse events for hWnd3 */
350     TmeStartTracking(hWnd3, TME_HOVER );
351     FLUSH_MESSAGES(0, QS_TIMER|QS_MOUSEMOVE);
352     COMPARE_CACHE(empty_chain);
353     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
354 
355     /* make sure that the timer will fire before the mouse moves */
356     Sleep(100);
357     FlushMessages();
358     COMPARE_CACHE(mousehover3_nomove_chain);
359     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
360 
361     Sleep(100);
362     FlushMessages();
363     COMPARE_CACHE(empty_chain);
364 
365     /* the mouse hovers hwnd3 and the timer is not dispatched*/
366     TmeStartTracking(hWnd3, TME_HOVER );
367     ignore_timer = TRUE;
368     MOVE_CURSOR(400,400);
369     Sleep(100);
370     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
371     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);     /* the loop drops WM_SYSTIMER */
372     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE); /* TME_HOVER is still active  */
373     COMPARE_CACHE(mousehover3_droptimer_chain);   /* we get no WM_MOUSEHOVER    */
374     ignore_timer = FALSE;
375 
376     /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE_LL */
377     ignore_mousell = TRUE;
378     MOVE_CURSOR(402,402);
379     Sleep(100);
380     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
381     FLUSH_MESSAGES(QS_TIMER, QS_MOUSEMOVE);         /* WH_MOUSE_LL drops WM_MOUSEMOVE */
382     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
383     COMPARE_CACHE(mousehover3_dropmousell_chain);   /* we get WM_MOUSEHOVER normaly */
384     ignore_mousell = FALSE;
385 
386     FlushMessages();
387     COMPARE_CACHE(empty_chain);
388 
389     /* the mouse hovers hwnd3 and mouse message is dropped by WH_MOUSE */
390     ignore_mouse = TRUE;
391     TmeStartTracking(hWnd3, TME_HOVER );
392     MOVE_CURSOR(401,401);
393     Sleep(100);
394     EXPECT_TME_FLAGS(hWnd3, TME_HOVER|TME_LEAVE);
395     FLUSH_MESSAGES(QS_TIMER|QS_MOUSEMOVE, 0);     /* WH_MOUSE drops WM_MOUSEMOVE */
396     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);
397     COMPARE_CACHE(mousehover3_dropmouse_chain);   /* we get WM_MOUSEHOVER normaly */
398     ignore_mouse = FALSE;
399 
400     FlushMessages();
401     COMPARE_CACHE(empty_chain);
402 
403     /* the mouse leaves hwnd3 and moves to hwnd2 and mouse message is dropped by WH_MOUSE_LL */
404     ignore_mousell = TRUE;
405     TmeStartTracking(hWnd3, TME_LEAVE );
406     MOVE_CURSOR(220,220);
407     FLUSH_MESSAGES(0, QS_MOUSEMOVE|QS_TIMER);         /* WH_MOUSE drops WM_MOUSEMOVE */
408     EXPECT_TME_FLAGS(hWnd3, TME_LEAVE);               /* all flags are removed */
409     COMPARE_CACHE(mouseleave3to2_dropmousell_chain);  /* we get no WM_MOUSELEAVE */
410     ignore_mousell = 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 */
416     ignore_mouse = TRUE;
417     MOVE_CURSOR(220,220);
418     FLUSH_MESSAGES(QS_MOUSEMOVE, QS_TIMER);         /* WH_MOUSE drops WM_MOUSEMOVE */
419     EXPECT_TME_FLAGS(hWnd3, 0);                     /* all flags are removed */
420     COMPARE_CACHE(mouseleave3to2_dropmouse_chain);  /* we get no WM_MOUSELEAVE */
421     ignore_mouse = FALSE;
422 
423     /* after WH_MOUSE drops WM_MOUSEMOVE, WM_MOUSELEAVE is still in the queue */
424     FLUSH_MESSAGES(QS_POSTMESSAGE, QS_TIMER|QS_MOUSEMOVE);
425     COMPARE_CACHE(mouseleave3_remainging_chain);
426 
427     FlushMessages();
428     COMPARE_CACHE(empty_chain);
429 
430     destroy_test_window();
431 }
432 
433 START_TEST(TrackMouseEvent)
434 {
435     Test_TrackMouseEvent();
436 }
437