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