1 /* Unit tests for appbars
3 * Copyright 2008 Vincent Povirk for CodeWeavers
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.
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.
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
25 #include "wine/test.h"
27 #define MSG_APPBAR WM_APP
29 static const CHAR testwindow_class
[] = "testwindow";
31 static HMONITOR (WINAPI
*pMonitorFromWindow
)(HWND
, DWORD
);
32 static HRESULT (WINAPI
*pGetCurrentProcessExplicitAppUserModelID
)(PWSTR
*);
34 typedef BOOL (*boolean_function
)(void);
36 struct testwindow_info
46 static struct testwindow_info windows
[3];
48 static int expected_bottom
;
50 static void testwindow_setpos(HWND hwnd
)
52 struct testwindow_info
* info
= (struct testwindow_info
*)GetWindowLongPtrA(hwnd
, GWLP_USERDATA
);
56 ok(info
!= NULL
, "got unexpected ABN_POSCHANGED notification\n");
58 if (!info
|| !info
->registered
)
63 if (info
->to_be_deleted
)
65 win_skip("Some Win95 and NT4 systems send messages to removed taskbars\n");
69 abd
.cbSize
= sizeof(abd
);
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
);
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
);
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
);
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
);
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
);
95 ret
= SHAppBarMessage(ABM_SETPOS
, &abd
);
96 ok(ret
== TRUE
, "SHAppBarMessage returned %i\n", ret
);
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
);
102 static LRESULT CALLBACK
testwindow_wndproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
111 testwindow_setpos(hwnd
);
118 return DefWindowProcA(hwnd
, msg
, wparam
, lparam
);
121 /* process pending messages until a condition is true or 3 seconds pass */
122 static void do_events_until(boolean_function test
)
128 timerid
= SetTimer(0, 0, 3000, NULL
);
132 while (PeekMessageA(&msg
, NULL
, 0, 0, PM_REMOVE
))
134 if (msg
.hwnd
== 0 && msg
.message
== WM_TIMER
&& msg
.wParam
== timerid
)
136 TranslateMessage(&msg
);
137 DispatchMessageA(&msg
);
139 if (timedout
|| test())
144 KillTimer(0, timerid
);
147 /* process any pending messages */
148 static void do_events(void)
152 while (PeekMessageA(&msg
, NULL
, 0, 0, PM_REMOVE
))
154 TranslateMessage(&msg
);
155 DispatchMessageA(&msg
);
159 static BOOL
no_appbars_intersect(void)
166 for (j
=i
+1; j
<3; j
++)
168 if (windows
[i
].registered
&& windows
[j
].registered
&&
169 IntersectRect(&rc
, &windows
[i
].allocated_rect
, &windows
[j
].allocated_rect
))
176 static BOOL
got_expected_bottom(void)
178 return (no_appbars_intersect() && windows
[1].allocated_rect
.bottom
== expected_bottom
);
181 static void register_testwindow_class(void)
185 ZeroMemory(&cls
, sizeof(cls
));
186 cls
.cbSize
= sizeof(cls
);
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
;
194 RegisterClassExA(&cls
);
197 #define test_window_rects(a, b) \
198 ok(!IntersectRect(&rc, &windows[a].allocated_rect, &windows[b].allocated_rect), \
199 "rectangles intersect %s / %s\n", wine_dbgstr_rect(&windows[a].allocated_rect), \
200 wine_dbgstr_rect(&windows[b].allocated_rect))
202 static void test_setpos(void)
206 int screen_width
, screen_height
;
210 screen_width
= GetSystemMetrics(SM_CXSCREEN
);
211 screen_height
= GetSystemMetrics(SM_CYSCREEN
);
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");
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
);
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
);
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 SetRect(&windows
[0].desired_rect
, 0, screen_height
- 15, screen_width
, screen_height
);
235 SetWindowLongPtrA(windows
[0].hwnd
, GWLP_USERDATA
, (LONG_PTR
)&windows
[0]);
236 testwindow_setpos(windows
[0].hwnd
);
239 /* create and register windows[1] */
240 windows
[1].hwnd
= CreateWindowExA(WS_EX_TOOLWINDOW
|WS_EX_TOPMOST
,
241 testwindow_class
, testwindow_class
, WS_POPUP
|WS_VISIBLE
, 0, 0, 0, 0,
242 NULL
, NULL
, NULL
, NULL
);
243 ok(windows
[1].hwnd
!= NULL
, "couldn't create window\n");
244 abd
.hWnd
= windows
[1].hwnd
;
245 ret
= SHAppBarMessage(ABM_NEW
, &abd
);
246 ok(ret
== TRUE
, "SHAppBarMessage returned %i\n", ret
);
248 /* dock windows[1] to the bottom of the screen */
249 windows
[1].registered
= TRUE
;
250 windows
[1].to_be_deleted
= FALSE
;
251 windows
[1].edge
= ABE_BOTTOM
;
252 SetRect(&windows
[1].desired_rect
, 0, screen_height
- 10, screen_width
, screen_height
);
253 SetWindowLongPtrA(windows
[1].hwnd
, GWLP_USERDATA
, (LONG_PTR
)&windows
[1]);
254 testwindow_setpos(windows
[1].hwnd
);
256 /* the windows are adjusted to they don't overlap */
257 do_events_until(no_appbars_intersect
);
258 test_window_rects(0, 1);
260 /* make windows[0] larger, forcing windows[1] to move out of its way */
261 windows
[0].desired_rect
.top
= screen_height
- 20;
262 testwindow_setpos(windows
[0].hwnd
);
263 do_events_until(no_appbars_intersect
);
264 test_window_rects(0, 1);
266 /* create and register windows[2] */
267 windows
[2].hwnd
= CreateWindowExA(WS_EX_TOOLWINDOW
|WS_EX_TOPMOST
,
268 testwindow_class
, testwindow_class
, WS_POPUP
|WS_VISIBLE
, 0, 0, 0, 0,
269 NULL
, NULL
, NULL
, NULL
);
270 ok(windows
[2].hwnd
!= NULL
, "couldn't create window\n");
273 abd
.hWnd
= windows
[2].hwnd
;
274 ret
= SHAppBarMessage(ABM_NEW
, &abd
);
275 ok(ret
== TRUE
, "SHAppBarMessage returned %i\n", ret
);
277 /* dock windows[2] to the bottom of the screen */
278 windows
[2].registered
= TRUE
;
279 windows
[2].to_be_deleted
= FALSE
;
280 windows
[2].edge
= ABE_BOTTOM
;
281 SetRect(&windows
[2].desired_rect
, 0, screen_height
- 10, screen_width
, screen_height
);
282 SetWindowLongPtrA(windows
[2].hwnd
, GWLP_USERDATA
, (LONG_PTR
)&windows
[2]);
283 testwindow_setpos(windows
[2].hwnd
);
285 do_events_until(no_appbars_intersect
);
286 test_window_rects(0, 1);
287 test_window_rects(0, 2);
288 test_window_rects(1, 2);
290 /* move windows[2] to the right side of the screen */
291 windows
[2].edge
= ABE_RIGHT
;
292 SetRect(&windows
[2].desired_rect
, screen_width
- 15, 0, screen_width
, screen_height
);
293 testwindow_setpos(windows
[2].hwnd
);
295 do_events_until(no_appbars_intersect
);
296 test_window_rects(0, 1);
297 test_window_rects(0, 2);
298 test_window_rects(1, 2);
300 /* move windows[1] to the top of the screen */
301 windows
[1].edge
= ABE_TOP
;
302 SetRect(&windows
[1].desired_rect
, 0, 0, screen_width
, 15);
303 testwindow_setpos(windows
[1].hwnd
);
305 do_events_until(no_appbars_intersect
);
306 test_window_rects(0, 1);
307 test_window_rects(0, 2);
308 test_window_rects(1, 2);
310 /* move windows[1] back to the bottom of the screen */
311 windows
[1].edge
= ABE_BOTTOM
;
312 SetRect(&windows
[1].desired_rect
, 0, screen_height
- 10, screen_width
, screen_height
);
313 testwindow_setpos(windows
[1].hwnd
);
315 do_events_until(no_appbars_intersect
);
316 test_window_rects(0, 1);
317 test_window_rects(0, 2);
318 test_window_rects(1, 2);
320 /* removing windows[0] will cause windows[1] to move down into its space */
321 expected_bottom
= max(windows
[0].allocated_rect
.bottom
, windows
[1].allocated_rect
.bottom
);
322 org_bottom1
= windows
[1].allocated_rect
.bottom
;
323 ok(windows
[0].allocated_rect
.bottom
> windows
[1].allocated_rect
.bottom
,
324 "Expected windows[0] to be lower than windows[1]\n");
326 abd
.hWnd
= windows
[0].hwnd
;
327 windows
[0].to_be_deleted
= TRUE
;
328 ret
= SHAppBarMessage(ABM_REMOVE
, &abd
);
329 ok(ret
== TRUE
, "SHAppBarMessage returned %i\n", ret
);
330 windows
[0].registered
= FALSE
;
331 DestroyWindow(windows
[0].hwnd
);
333 do_events_until(got_expected_bottom
);
335 if (windows
[1].allocated_rect
.bottom
== org_bottom1
)
336 win_skip("Some broken Vista boxes don't move the higher appbar down\n");
338 ok(windows
[1].allocated_rect
.bottom
== expected_bottom
,
339 "windows[1]'s bottom is %i, expected %i\n",
340 windows
[1].allocated_rect
.bottom
, expected_bottom
);
342 test_window_rects(1, 2);
344 /* remove the other windows */
345 abd
.hWnd
= windows
[1].hwnd
;
346 windows
[1].to_be_deleted
= TRUE
;
347 ret
= SHAppBarMessage(ABM_REMOVE
, &abd
);
348 ok(ret
== TRUE
, "SHAppBarMessage returned %i\n", ret
);
349 windows
[1].registered
= FALSE
;
350 DestroyWindow(windows
[1].hwnd
);
352 abd
.hWnd
= windows
[2].hwnd
;
353 windows
[2].to_be_deleted
= TRUE
;
354 ret
= SHAppBarMessage(ABM_REMOVE
, &abd
);
355 ok(ret
== TRUE
, "SHAppBarMessage returned %i\n", ret
);
356 windows
[2].registered
= FALSE
;
357 DestroyWindow(windows
[2].hwnd
);
360 static void test_appbarget(void)
363 HWND hwnd
, foregnd
, unset_hwnd
;
366 memset(&abd
, 0xcc, sizeof(abd
));
367 memset(&unset_hwnd
, 0xcc, sizeof(unset_hwnd
));
368 abd
.cbSize
= sizeof(abd
);
369 abd
.uEdge
= ABE_BOTTOM
;
371 hwnd
= (HWND
)SHAppBarMessage(ABM_GETAUTOHIDEBAR
, &abd
);
372 ok(hwnd
== NULL
|| IsWindow(hwnd
), "ret %p which is not a window\n", hwnd
);
373 ok(abd
.hWnd
== unset_hwnd
, "hWnd overwritten %p\n",abd
.hWnd
);
375 if (!pMonitorFromWindow
)
377 win_skip("MonitorFromWindow is not available\n");
381 /* Presumably one can pass a hwnd with ABM_GETAUTOHIDEBAR to specify a monitor.
382 Pass the foreground window and check */
383 foregnd
= GetForegroundWindow();
387 hwnd
= (HWND
)SHAppBarMessage(ABM_GETAUTOHIDEBAR
, &abd
);
388 ok(hwnd
== NULL
|| IsWindow(hwnd
), "ret %p which is not a window\n", hwnd
);
389 ok(abd
.hWnd
== foregnd
, "hWnd overwritten\n");
392 HMONITOR appbar_mon
, foregnd_mon
;
393 appbar_mon
= pMonitorFromWindow(hwnd
, MONITOR_DEFAULTTONEAREST
);
394 foregnd_mon
= pMonitorFromWindow(foregnd
, MONITOR_DEFAULTTONEAREST
);
395 ok(appbar_mon
== foregnd_mon
, "Windows on different monitors\n");
400 memset(&abd
, 0xcc, sizeof(abd
));
401 abd
.cbSize
= sizeof(abd
);
402 ret
= SHAppBarMessage(ABM_GETTASKBARPOS
, &abd
);
405 ok(abd
.hWnd
== (HWND
)0xcccccccc, "hWnd overwritten\n");
406 ok(abd
.uEdge
<= ABE_BOTTOM
||
407 broken(abd
.uEdge
== 0xcccccccc), /* Some Win95 and NT4 */
408 "uEdge not returned\n");
409 ok(abd
.rc
.left
!= 0xcccccccc, "rc not updated\n");
415 static void test_GetCurrentProcessExplicitAppUserModelID(void)
420 if (!pGetCurrentProcessExplicitAppUserModelID
)
422 win_skip("GetCurrentProcessExplicitAppUserModelID() is not supported.\n");
426 if (0) /* crashes on native */
427 hr
= pGetCurrentProcessExplicitAppUserModelID(NULL
);
429 appid
= (void*)0xdeadbeef;
430 hr
= pGetCurrentProcessExplicitAppUserModelID(&appid
);
432 ok(hr
== E_FAIL
, "got 0x%08x\n", hr
);
433 ok(appid
== NULL
, "got %p\n", appid
);
438 HMODULE huser32
, hshell32
;
440 huser32
= GetModuleHandleA("user32.dll");
441 hshell32
= GetModuleHandleA("shell32.dll");
442 pMonitorFromWindow
= (void*)GetProcAddress(huser32
, "MonitorFromWindow");
443 pGetCurrentProcessExplicitAppUserModelID
= (void*)GetProcAddress(hshell32
, "GetCurrentProcessExplicitAppUserModelID");
445 register_testwindow_class();
449 test_GetCurrentProcessExplicitAppUserModelID();