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