[GDI32_WINETEST]
[reactos.git] / rostests / winetests / shell32 / appbar.c
1 /* Unit tests for appbars
2 *
3 * Copyright 2008 Vincent Povirk for CodeWeavers
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include <assert.h>
21 #include <stdarg.h>
22
23 #include <windows.h>
24
25 #include "wine/test.h"
26
27 #define MSG_APPBAR WM_APP
28
29 static const CHAR testwindow_class[] = "testwindow";
30
31 static HMONITOR (WINAPI *pMonitorFromWindow)(HWND, DWORD);
32
33 typedef BOOL (*boolean_function)(void);
34
35 struct testwindow_info
36 {
37 HWND hwnd;
38 BOOL registered;
39 BOOL to_be_deleted;
40 RECT desired_rect;
41 UINT edge;
42 RECT allocated_rect;
43 };
44
45 static struct testwindow_info windows[3];
46
47 static int expected_bottom;
48
49 static void testwindow_setpos(HWND hwnd)
50 {
51 struct testwindow_info* info = (struct testwindow_info*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
52 APPBARDATA abd;
53 BOOL ret;
54
55 ok(info != NULL, "got unexpected ABN_POSCHANGED notification\n");
56
57 if (!info || !info->registered)
58 {
59 return;
60 }
61
62 if (info->to_be_deleted)
63 {
64 win_skip("Some Win95 and NT4 systems send messages to removed taskbars\n");
65 return;
66 }
67
68 abd.cbSize = sizeof(abd);
69 abd.hWnd = hwnd;
70 abd.uEdge = info->edge;
71 abd.rc = info->desired_rect;
72 ret = SHAppBarMessage(ABM_QUERYPOS, &abd);
73 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
74 switch (info->edge)
75 {
76 case ABE_BOTTOM:
77 ok(info->desired_rect.top == abd.rc.top, "ABM_QUERYPOS changed top of rect from %i to %i\n", info->desired_rect.top, abd.rc.top);
78 abd.rc.top = abd.rc.bottom - (info->desired_rect.bottom - info->desired_rect.top);
79 break;
80 case ABE_LEFT:
81 ok(info->desired_rect.right == abd.rc.right, "ABM_QUERYPOS changed right of rect from %i to %i\n", info->desired_rect.right, abd.rc.right);
82 abd.rc.right = abd.rc.left + (info->desired_rect.right - info->desired_rect.left);
83 break;
84 case ABE_RIGHT:
85 ok(info->desired_rect.left == abd.rc.left, "ABM_QUERYPOS changed left of rect from %i to %i\n", info->desired_rect.left, abd.rc.left);
86 abd.rc.left = abd.rc.right - (info->desired_rect.right - info->desired_rect.left);
87 break;
88 case ABE_TOP:
89 ok(info->desired_rect.bottom == abd.rc.bottom, "ABM_QUERYPOS changed bottom of rect from %i to %i\n", info->desired_rect.bottom, abd.rc.bottom);
90 abd.rc.bottom = abd.rc.top + (info->desired_rect.bottom - info->desired_rect.top);
91 break;
92 }
93
94 ret = SHAppBarMessage(ABM_SETPOS, &abd);
95 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
96
97 info->allocated_rect = abd.rc;
98 MoveWindow(hwnd, abd.rc.left, abd.rc.top, abd.rc.right-abd.rc.left, abd.rc.bottom-abd.rc.top, TRUE);
99 }
100
101 static LRESULT CALLBACK testwindow_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
102 {
103 switch(msg)
104 {
105 case MSG_APPBAR:
106 {
107 switch(wparam)
108 {
109 case ABN_POSCHANGED:
110 testwindow_setpos(hwnd);
111 break;
112 }
113 return 0;
114 }
115 }
116
117 return DefWindowProc(hwnd, msg, wparam, lparam);
118 }
119
120 /* process pending messages until a condition is true or 3 seconds pass */
121 static void do_events_until(boolean_function test)
122 {
123 MSG msg;
124 UINT_PTR timerid;
125 BOOL timedout=FALSE;
126
127 timerid = SetTimer(0, 0, 3000, NULL);
128
129 while (1)
130 {
131 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
132 {
133 if (msg.hwnd == 0 && msg.message == WM_TIMER && msg.wParam == timerid)
134 timedout = TRUE;
135 TranslateMessage(&msg);
136 DispatchMessageA(&msg);
137 }
138 if (timedout || test())
139 break;
140 WaitMessage();
141 }
142
143 KillTimer(0, timerid);
144 }
145
146 /* process any pending messages */
147 static void do_events(void)
148 {
149 MSG msg;
150
151 while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
152 {
153 TranslateMessage(&msg);
154 DispatchMessageA(&msg);
155 }
156 }
157
158 static BOOL no_appbars_intersect(void)
159 {
160 int i, j;
161 RECT rc;
162
163 for (i=0; i<2; i++)
164 {
165 for (j=i+1; j<3; j++)
166 {
167 if (windows[i].registered && windows[j].registered &&
168 IntersectRect(&rc, &windows[i].allocated_rect, &windows[j].allocated_rect))
169 return FALSE;
170 }
171 }
172 return TRUE;
173 }
174
175 static BOOL got_expected_bottom(void)
176 {
177 return (no_appbars_intersect() && windows[1].allocated_rect.bottom == expected_bottom);
178 }
179
180 static void register_testwindow_class(void)
181 {
182 WNDCLASSEXA cls;
183
184 ZeroMemory(&cls, sizeof(cls));
185 cls.cbSize = sizeof(cls);
186 cls.style = 0;
187 cls.lpfnWndProc = testwindow_wndproc;
188 cls.hInstance = NULL;
189 cls.hCursor = LoadCursor(0, IDC_ARROW);
190 cls.hbrBackground = (HBRUSH) COLOR_WINDOW;
191 cls.lpszClassName = testwindow_class;
192
193 RegisterClassExA(&cls);
194 }
195
196 #define test_window_rects(a, b) \
197 ok(!IntersectRect(&rc, &windows[a].allocated_rect, &windows[b].allocated_rect), \
198 "rectangles intersect (%i,%i,%i,%i)/(%i,%i,%i,%i)\n", \
199 windows[a].allocated_rect.left, windows[a].allocated_rect.top, windows[a].allocated_rect.right, windows[a].allocated_rect.bottom, \
200 windows[b].allocated_rect.left, windows[b].allocated_rect.top, windows[b].allocated_rect.right, windows[b].allocated_rect.bottom)
201
202 static void test_setpos(void)
203 {
204 APPBARDATA abd;
205 RECT rc;
206 int screen_width, screen_height;
207 BOOL ret;
208 int org_bottom1;
209
210 screen_width = GetSystemMetrics(SM_CXSCREEN);
211 screen_height = GetSystemMetrics(SM_CYSCREEN);
212
213 /* create and register windows[0] */
214 windows[0].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
215 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
216 NULL, NULL, NULL, NULL);
217 ok(windows[0].hwnd != NULL, "couldn't create window\n");
218 do_events();
219 abd.cbSize = sizeof(abd);
220 abd.hWnd = windows[0].hwnd;
221 abd.uCallbackMessage = MSG_APPBAR;
222 ret = SHAppBarMessage(ABM_NEW, &abd);
223 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
224
225 /* ABM_NEW should return FALSE if the window is already registered */
226 ret = SHAppBarMessage(ABM_NEW, &abd);
227 ok(ret == FALSE, "SHAppBarMessage returned %i\n", ret);
228 do_events();
229
230 /* dock windows[0] to the bottom of the screen */
231 windows[0].registered = TRUE;
232 windows[0].to_be_deleted = FALSE;
233 windows[0].edge = ABE_BOTTOM;
234 windows[0].desired_rect.left = 0;
235 windows[0].desired_rect.right = screen_width;
236 windows[0].desired_rect.top = screen_height - 15;
237 windows[0].desired_rect.bottom = screen_height;
238 SetWindowLongPtr(windows[0].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[0]);
239 testwindow_setpos(windows[0].hwnd);
240 do_events();
241
242 /* create and register windows[1] */
243 windows[1].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
244 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
245 NULL, NULL, NULL, NULL);
246 ok(windows[1].hwnd != NULL, "couldn't create window\n");
247 abd.hWnd = windows[1].hwnd;
248 ret = SHAppBarMessage(ABM_NEW, &abd);
249 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
250
251 /* dock windows[1] to the bottom of the screen */
252 windows[1].registered = TRUE;
253 windows[1].to_be_deleted = FALSE;
254 windows[1].edge = ABE_BOTTOM;
255 windows[1].desired_rect.left = 0;
256 windows[1].desired_rect.right = screen_width;
257 windows[1].desired_rect.top = screen_height - 10;
258 windows[1].desired_rect.bottom = screen_height;
259 SetWindowLongPtr(windows[1].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[1]);
260 testwindow_setpos(windows[1].hwnd);
261
262 /* the windows are adjusted to they don't overlap */
263 do_events_until(no_appbars_intersect);
264 test_window_rects(0, 1);
265
266 /* make windows[0] larger, forcing windows[1] to move out of its way */
267 windows[0].desired_rect.top = screen_height - 20;
268 testwindow_setpos(windows[0].hwnd);
269 do_events_until(no_appbars_intersect);
270 test_window_rects(0, 1);
271
272 /* create and register windows[2] */
273 windows[2].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
274 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
275 NULL, NULL, NULL, NULL);
276 ok(windows[2].hwnd != NULL, "couldn't create window\n");
277 do_events();
278
279 abd.hWnd = windows[2].hwnd;
280 ret = SHAppBarMessage(ABM_NEW, &abd);
281 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
282
283 /* dock windows[2] to the bottom of the screen */
284 windows[2].registered = TRUE;
285 windows[2].to_be_deleted = FALSE;
286 windows[2].edge = ABE_BOTTOM;
287 windows[2].desired_rect.left = 0;
288 windows[2].desired_rect.right = screen_width;
289 windows[2].desired_rect.top = screen_height - 10;
290 windows[2].desired_rect.bottom = screen_height;
291 SetWindowLongPtr(windows[2].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[2]);
292 testwindow_setpos(windows[2].hwnd);
293
294 do_events_until(no_appbars_intersect);
295 test_window_rects(0, 1);
296 test_window_rects(0, 2);
297 test_window_rects(1, 2);
298
299 /* move windows[2] to the right side of the screen */
300 windows[2].edge = ABE_RIGHT;
301 windows[2].desired_rect.left = screen_width - 15;
302 windows[2].desired_rect.right = screen_width;
303 windows[2].desired_rect.top = 0;
304 windows[2].desired_rect.bottom = screen_height;
305 testwindow_setpos(windows[2].hwnd);
306
307 do_events_until(no_appbars_intersect);
308 test_window_rects(0, 1);
309 test_window_rects(0, 2);
310 test_window_rects(1, 2);
311
312 /* move windows[1] to the top of the screen */
313 windows[1].edge = ABE_TOP;
314 windows[1].desired_rect.left = 0;
315 windows[1].desired_rect.right = screen_width;
316 windows[1].desired_rect.top = 0;
317 windows[1].desired_rect.bottom = 15;
318 testwindow_setpos(windows[1].hwnd);
319
320 do_events_until(no_appbars_intersect);
321 test_window_rects(0, 1);
322 test_window_rects(0, 2);
323 test_window_rects(1, 2);
324
325 /* move windows[1] back to the bottom of the screen */
326 windows[1].edge = ABE_BOTTOM;
327 windows[1].desired_rect.left = 0;
328 windows[1].desired_rect.right = screen_width;
329 windows[1].desired_rect.top = screen_height - 10;
330 windows[1].desired_rect.bottom = screen_height;
331 testwindow_setpos(windows[1].hwnd);
332
333 do_events_until(no_appbars_intersect);
334 test_window_rects(0, 1);
335 test_window_rects(0, 2);
336 test_window_rects(1, 2);
337
338 /* removing windows[0] will cause windows[1] to move down into its space */
339 expected_bottom = max(windows[0].allocated_rect.bottom, windows[1].allocated_rect.bottom);
340 org_bottom1 = windows[1].allocated_rect.bottom;
341 ok(windows[0].allocated_rect.bottom > windows[1].allocated_rect.bottom,
342 "Expected windows[0] to be lower than windows[1]\n");
343
344 abd.hWnd = windows[0].hwnd;
345 windows[0].to_be_deleted = TRUE;
346 ret = SHAppBarMessage(ABM_REMOVE, &abd);
347 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
348 windows[0].registered = FALSE;
349 DestroyWindow(windows[0].hwnd);
350
351 do_events_until(got_expected_bottom);
352
353 if (windows[1].allocated_rect.bottom == org_bottom1)
354 win_skip("Some broken Vista boxes don't move the higher appbar down\n");
355 else
356 ok(windows[1].allocated_rect.bottom == expected_bottom,
357 "windows[1]'s bottom is %i, expected %i\n",
358 windows[1].allocated_rect.bottom, expected_bottom);
359
360 test_window_rects(1, 2);
361
362 /* remove the other windows */
363 abd.hWnd = windows[1].hwnd;
364 windows[1].to_be_deleted = TRUE;
365 ret = SHAppBarMessage(ABM_REMOVE, &abd);
366 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
367 windows[1].registered = FALSE;
368 DestroyWindow(windows[1].hwnd);
369
370 abd.hWnd = windows[2].hwnd;
371 windows[2].to_be_deleted = TRUE;
372 ret = SHAppBarMessage(ABM_REMOVE, &abd);
373 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
374 windows[2].registered = FALSE;
375 DestroyWindow(windows[2].hwnd);
376 }
377
378 static void test_appbarget(void)
379 {
380 APPBARDATA abd;
381 HWND hwnd, foregnd, unset_hwnd;
382 UINT_PTR ret;
383
384 memset(&abd, 0xcc, sizeof(abd));
385 memset(&unset_hwnd, 0xcc, sizeof(unset_hwnd));
386 abd.cbSize = sizeof(abd);
387 abd.uEdge = ABE_BOTTOM;
388
389 hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
390 ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd);
391 ok(abd.hWnd == unset_hwnd, "hWnd overwritten %p\n",abd.hWnd);
392
393 if (!pMonitorFromWindow)
394 {
395 win_skip("MonitorFromWindow is not available\n");
396 }
397 else
398 {
399 /* Presumably one can pass a hwnd with ABM_GETAUTOHIDEBAR to specify a monitor.
400 Pass the foreground window and check */
401 foregnd = GetForegroundWindow();
402 if(foregnd)
403 {
404 abd.hWnd = foregnd;
405 hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
406 ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd);
407 ok(abd.hWnd == foregnd, "hWnd overwritten\n");
408 if(hwnd)
409 {
410 HMONITOR appbar_mon, foregnd_mon;
411 appbar_mon = pMonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
412 foregnd_mon = pMonitorFromWindow(foregnd, MONITOR_DEFAULTTONEAREST);
413 ok(appbar_mon == foregnd_mon, "Windows on different monitors\n");
414 }
415 }
416 }
417
418 memset(&abd, 0xcc, sizeof(abd));
419 abd.cbSize = sizeof(abd);
420 ret = SHAppBarMessage(ABM_GETTASKBARPOS, &abd);
421 if(ret)
422 {
423 ok(abd.hWnd == (HWND)0xcccccccc, "hWnd overwritten\n");
424 ok(abd.uEdge <= ABE_BOTTOM ||
425 broken(abd.uEdge == 0xcccccccc), /* Some Win95 and NT4 */
426 "uEdge not returned\n");
427 ok(abd.rc.left != 0xcccccccc, "rc not updated\n");
428 }
429
430 return;
431 }
432
433 START_TEST(appbar)
434 {
435 HMODULE huser32;
436
437 huser32 = GetModuleHandleA("user32.dll");
438 pMonitorFromWindow = (void*)GetProcAddress(huser32, "MonitorFromWindow");
439
440 register_testwindow_class();
441
442 test_setpos();
443 test_appbarget();
444 }