73dc0b2d81ad512c7b51a3a79a5f69e5e47284cc
[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
209 screen_width = GetSystemMetrics(SM_CXSCREEN);
210 screen_height = GetSystemMetrics(SM_CYSCREEN);
211
212 /* create and register windows[0] */
213 windows[0].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
214 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
215 NULL, NULL, NULL, NULL);
216 ok(windows[0].hwnd != NULL, "couldn't create window\n");
217 do_events();
218 abd.cbSize = sizeof(abd);
219 abd.hWnd = windows[0].hwnd;
220 abd.uCallbackMessage = MSG_APPBAR;
221 ret = SHAppBarMessage(ABM_NEW, &abd);
222 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
223
224 /* ABM_NEW should return FALSE if the window is already registered */
225 ret = SHAppBarMessage(ABM_NEW, &abd);
226 ok(ret == FALSE, "SHAppBarMessage returned %i\n", ret);
227 do_events();
228
229 /* dock windows[0] to the bottom of the screen */
230 windows[0].registered = TRUE;
231 windows[0].to_be_deleted = FALSE;
232 windows[0].edge = ABE_BOTTOM;
233 windows[0].desired_rect.left = 0;
234 windows[0].desired_rect.right = screen_width;
235 windows[0].desired_rect.top = screen_height - 15;
236 windows[0].desired_rect.bottom = screen_height;
237 SetWindowLongPtr(windows[0].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[0]);
238 testwindow_setpos(windows[0].hwnd);
239 do_events();
240
241 /* create and register windows[1] */
242 windows[1].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
243 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
244 NULL, NULL, NULL, NULL);
245 ok(windows[1].hwnd != NULL, "couldn't create window\n");
246 abd.hWnd = windows[1].hwnd;
247 ret = SHAppBarMessage(ABM_NEW, &abd);
248 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
249
250 /* dock windows[1] to the bottom of the screen */
251 windows[1].registered = TRUE;
252 windows[1].to_be_deleted = FALSE;
253 windows[1].edge = ABE_BOTTOM;
254 windows[1].desired_rect.left = 0;
255 windows[1].desired_rect.right = screen_width;
256 windows[1].desired_rect.top = screen_height - 10;
257 windows[1].desired_rect.bottom = screen_height;
258 SetWindowLongPtr(windows[1].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[1]);
259 testwindow_setpos(windows[1].hwnd);
260
261 /* the windows are adjusted to they don't overlap */
262 do_events_until(no_appbars_intersect);
263 test_window_rects(0, 1);
264
265 /* make windows[0] larger, forcing windows[1] to move out of its way */
266 windows[0].desired_rect.top = screen_height - 20;
267 testwindow_setpos(windows[0].hwnd);
268 do_events_until(no_appbars_intersect);
269 test_window_rects(0, 1);
270
271 /* create and register windows[2] */
272 windows[2].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
273 testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
274 NULL, NULL, NULL, NULL);
275 ok(windows[2].hwnd != NULL, "couldn't create window\n");
276 do_events();
277
278 abd.hWnd = windows[2].hwnd;
279 ret = SHAppBarMessage(ABM_NEW, &abd);
280 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
281
282 /* dock windows[2] to the bottom of the screen */
283 windows[2].registered = TRUE;
284 windows[2].to_be_deleted = FALSE;
285 windows[2].edge = ABE_BOTTOM;
286 windows[2].desired_rect.left = 0;
287 windows[2].desired_rect.right = screen_width;
288 windows[2].desired_rect.top = screen_height - 10;
289 windows[2].desired_rect.bottom = screen_height;
290 SetWindowLongPtr(windows[2].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[2]);
291 testwindow_setpos(windows[2].hwnd);
292
293 do_events_until(no_appbars_intersect);
294 test_window_rects(0, 1);
295 test_window_rects(0, 2);
296 test_window_rects(1, 2);
297
298 /* move windows[2] to the right side of the screen */
299 windows[2].edge = ABE_RIGHT;
300 windows[2].desired_rect.left = screen_width - 15;
301 windows[2].desired_rect.right = screen_width;
302 windows[2].desired_rect.top = 0;
303 windows[2].desired_rect.bottom = screen_height;
304 testwindow_setpos(windows[2].hwnd);
305
306 do_events_until(no_appbars_intersect);
307 test_window_rects(0, 1);
308 test_window_rects(0, 2);
309 test_window_rects(1, 2);
310
311 /* move windows[1] to the top of the screen */
312 windows[1].edge = ABE_TOP;
313 windows[1].desired_rect.left = 0;
314 windows[1].desired_rect.right = screen_width;
315 windows[1].desired_rect.top = 0;
316 windows[1].desired_rect.bottom = 15;
317 testwindow_setpos(windows[1].hwnd);
318
319 do_events_until(no_appbars_intersect);
320 test_window_rects(0, 1);
321 test_window_rects(0, 2);
322 test_window_rects(1, 2);
323
324 /* move windows[1] back to the bottom of the screen */
325 windows[1].edge = ABE_BOTTOM;
326 windows[1].desired_rect.left = 0;
327 windows[1].desired_rect.right = screen_width;
328 windows[1].desired_rect.top = screen_height - 10;
329 windows[1].desired_rect.bottom = screen_height;
330 testwindow_setpos(windows[1].hwnd);
331
332 do_events_until(no_appbars_intersect);
333 test_window_rects(0, 1);
334 test_window_rects(0, 2);
335 test_window_rects(1, 2);
336
337 /* removing windows[0] will cause windows[1] to move down into its space */
338 expected_bottom = max(windows[0].allocated_rect.bottom, windows[1].allocated_rect.bottom);
339
340 abd.hWnd = windows[0].hwnd;
341 windows[0].to_be_deleted = TRUE;
342 ret = SHAppBarMessage(ABM_REMOVE, &abd);
343 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
344 windows[0].registered = FALSE;
345 DestroyWindow(windows[0].hwnd);
346
347 do_events_until(got_expected_bottom);
348
349 ok(windows[1].allocated_rect.bottom == expected_bottom, "windows[1]'s bottom is %i, expected %i\n", windows[1].allocated_rect.bottom, expected_bottom);
350
351 test_window_rects(1, 2);
352
353 /* remove the other windows */
354 abd.hWnd = windows[1].hwnd;
355 windows[1].to_be_deleted = TRUE;
356 ret = SHAppBarMessage(ABM_REMOVE, &abd);
357 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
358 windows[1].registered = FALSE;
359 DestroyWindow(windows[1].hwnd);
360
361 abd.hWnd = windows[2].hwnd;
362 windows[2].to_be_deleted = TRUE;
363 ret = SHAppBarMessage(ABM_REMOVE, &abd);
364 ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
365 windows[2].registered = FALSE;
366 DestroyWindow(windows[2].hwnd);
367 }
368
369 static void test_appbarget(void)
370 {
371 APPBARDATA abd;
372 HWND hwnd, foregnd, unset_hwnd;
373 UINT_PTR ret;
374
375 memset(&abd, 0xcc, sizeof(abd));
376 memset(&unset_hwnd, 0xcc, sizeof(unset_hwnd));
377 abd.cbSize = sizeof(abd);
378 abd.uEdge = ABE_BOTTOM;
379
380 hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
381 ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd);
382 ok(abd.hWnd == unset_hwnd, "hWnd overwritten %p\n",abd.hWnd);
383
384 if (!pMonitorFromWindow)
385 {
386 win_skip("MonitorFromWindow is not available\n");
387 }
388 else
389 {
390 /* Presumably one can pass a hwnd with ABM_GETAUTOHIDEBAR to specify a monitor.
391 Pass the foreground window and check */
392 foregnd = GetForegroundWindow();
393 if(foregnd)
394 {
395 abd.hWnd = foregnd;
396 hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
397 ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd);
398 ok(abd.hWnd == foregnd, "hWnd overwritten\n");
399 if(hwnd)
400 {
401 HMONITOR appbar_mon, foregnd_mon;
402 appbar_mon = pMonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
403 foregnd_mon = pMonitorFromWindow(foregnd, MONITOR_DEFAULTTONEAREST);
404 ok(appbar_mon == foregnd_mon, "Windows on different monitors\n");
405 }
406 }
407 }
408
409 memset(&abd, 0xcc, sizeof(abd));
410 abd.cbSize = sizeof(abd);
411 ret = SHAppBarMessage(ABM_GETTASKBARPOS, &abd);
412 if(ret)
413 {
414 ok(abd.hWnd == (HWND)0xcccccccc, "hWnd overwritten\n");
415 ok(abd.uEdge <= ABE_BOTTOM ||
416 broken(abd.uEdge == 0xcccccccc), /* Some Win95 and NT4 */
417 "uEdge not returned\n");
418 ok(abd.rc.left != 0xcccccccc, "rc not updated\n");
419 }
420
421 return;
422 }
423
424 START_TEST(appbar)
425 {
426 HMODULE huser32;
427
428 huser32 = GetModuleHandleA("user32.dll");
429 pMonitorFromWindow = (void*)GetProcAddress(huser32, "MonitorFromWindow");
430
431 register_testwindow_class();
432
433 test_setpos();
434 test_appbarget();
435 }