1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4 * PURPOSE: Test for SHAppBarMessage
5 * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6 */
7
8 #include "shelltest.h"
9 #include <windowsx.h>
10 #include <shlwapi.h>
11 #include <stdio.h>
12
13 /* Based on https://github.com/katahiromz/AppBarSample */
14
15 //#define VERBOSE
16
17 #define IDT_AUTOHIDE 1
18 #define IDT_AUTOUNHIDE 2
19
20 #define ID_ACTION 100
21
22 #define APPBAR_CALLBACK (WM_USER + 100)
23
24 #define LEFT_DOWN() mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
25 #define LEFT_UP() mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
26 #define MOVE(x, y) SetCursorPos((x), (y))
27
28 static const TCHAR s_szName[] = TEXT("AppBarSample");
29 static RECT s_rcWorkArea;
30 static HWND s_hwnd1 = NULL;
31 static HWND s_hwnd2 = NULL;
32
33 #ifdef VERBOSE
MessageOfAppBar(DWORD dwMessage)34 static LPCSTR MessageOfAppBar(DWORD dwMessage)
35 {
36 static char s_buf[32];
37 switch (dwMessage)
38 {
39 case ABM_NEW: return "ABM_NEW";
40 case ABM_REMOVE: return "ABM_REMOVE";
41 case ABM_QUERYPOS: return "ABM_QUERYPOS";
42 case ABM_SETPOS: return "ABM_SETPOS";
43 case ABM_GETSTATE: return "ABM_GETSTATE";
44 case ABM_GETTASKBARPOS: return "ABM_GETTASKBARPOS";
45 case ABM_ACTIVATE: return "ABM_ACTIVATE";
46 case ABM_GETAUTOHIDEBAR: return "ABM_GETAUTOHIDEBAR";
47 case ABM_SETAUTOHIDEBAR: return "ABM_SETAUTOHIDEBAR";
48 case ABM_WINDOWPOSCHANGED: return "ABM_WINDOWPOSCHANGED";
49 }
50 wsprintfA(s_buf, "%lu", dwMessage);
51 return s_buf;
52 }
53
54 static UINT WINAPI
SHAppBarMessageWrap(DWORD dwMessage,PAPPBARDATA pData)55 SHAppBarMessageWrap(DWORD dwMessage, PAPPBARDATA pData)
56 {
57 trace("SHAppBarMessage entered (dwMessage=%s, rc=(%ld, %ld, %ld, %ld))\n",
58 MessageOfAppBar(dwMessage),
59 pData->rc.left, pData->rc.top, pData->rc.right, pData->rc.bottom);
60 UINT ret = SHAppBarMessage(dwMessage, pData);
61 trace("SHAppBarMessage leaved (dwMessage=%s, rc=(%ld, %ld, %ld, %ld))\n",
62 MessageOfAppBar(dwMessage),
63 pData->rc.left, pData->rc.top, pData->rc.right, pData->rc.bottom);
64 return ret;
65 }
66 #define SHAppBarMessage SHAppBarMessageWrap
67
68 #undef ARRAYSIZE
69 #define ARRAYSIZE _countof
70
appbar_tprintf(const TCHAR * fmt,...)71 void appbar_tprintf(const TCHAR *fmt, ...)
72 {
73 TCHAR szText[512];
74 va_list va;
75 va_start(va, fmt);
76 wvsprintf(szText, fmt, va);
77 #ifdef UNICODE
78 printf("%ls", szText);
79 #else
80 printf("%s", szText);
81 #endif
82 va_end(va);
83 }
84
85 #define MSGDUMP_TPRINTF appbar_tprintf
86 #include "msgdump.h"
87
88 #endif // def VERBOSE
89
SlideWindow(HWND hwnd,LPRECT prc)90 void SlideWindow(HWND hwnd, LPRECT prc)
91 {
92 #define SLIDE_HIDE 400
93 #define SLIDE_SHOW 150
94 RECT rcOld, rcNew = *prc;
95 GetWindowRect(hwnd, &rcOld);
96
97 BOOL fShow = (rcNew.bottom - rcNew.top > rcOld.bottom - rcOld.top) ||
98 (rcNew.right - rcNew.left > rcOld.right - rcOld.left);
99
100 INT dx = (rcNew.right - rcOld.right) + (rcNew.left - rcOld.left);
101 INT dy = (rcNew.bottom - rcOld.bottom) + (rcNew.top - rcOld.top);
102
103 LONG dt = SLIDE_HIDE;
104 if (fShow)
105 {
106 dt = SLIDE_SHOW;
107 rcOld = rcNew;
108 OffsetRect(&rcOld, -dx, -dy);
109 SetWindowPos(hwnd, NULL, rcOld.left, rcOld.top,
110 rcOld.right - rcOld.left, rcOld.bottom - rcOld.top,
111 SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME);
112 }
113
114 HANDLE hThread = GetCurrentThread();
115 INT priority = GetThreadPriority(hThread);
116 SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);
117
118 LONG t, t0 = GetTickCount();
119 while ((t = GetTickCount()) < t0 + dt)
120 {
121 INT x = rcOld.left + dx * (t - t0) / dt;
122 INT y = rcOld.top + dy * (t - t0) / dt;
123 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
124
125 UpdateWindow(hwnd);
126 UpdateWindow(GetDesktopWindow());
127 }
128
129 SetThreadPriority(hThread, priority);
130 SetWindowPos(hwnd, NULL, rcNew.left, rcNew.top,
131 rcNew.right - rcNew.left, rcNew.bottom - rcNew.top,
132 SWP_NOZORDER | SWP_NOACTIVATE | SWP_DRAWFRAME);
133 #undef SLIDE_HIDE
134 #undef SLIDE_SHOW
135 }
136
137 class Window
138 {
139 public:
Window(INT cx,INT cy,BOOL fAutoHide=FALSE)140 Window(INT cx, INT cy, BOOL fAutoHide = FALSE)
141 : m_hwnd(NULL)
142 , m_fAutoHide(fAutoHide)
143 , m_cxWidth(cx)
144 , m_cyHeight(cy)
145 {
146 }
147
~Window()148 virtual ~Window()
149 {
150 }
151
DoRegisterClass(HINSTANCE hInstance)152 static BOOL DoRegisterClass(HINSTANCE hInstance)
153 {
154 WNDCLASS wc;
155 ZeroMemory(&wc, sizeof(wc));
156 wc.lpfnWndProc = Window::WindowProc;
157 wc.hInstance = hInstance;
158 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
159 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
160 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
161 wc.lpszClassName = s_szName;
162 return !!RegisterClass(&wc);
163 }
164
DoCreateMainWnd(HINSTANCE hInstance,LPCTSTR pszText,INT cx,INT cy,DWORD style=WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,DWORD exstyle=WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW|WS_EX_TOPMOST,BOOL fAutoHide=FALSE)165 static HWND DoCreateMainWnd(HINSTANCE hInstance, LPCTSTR pszText, INT cx, INT cy,
166 DWORD style = WS_POPUP | WS_THICKFRAME | WS_CLIPCHILDREN,
167 DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
168 BOOL fAutoHide = FALSE)
169 {
170 Window *this_ = new Window(cx, cy, fAutoHide);
171 HWND hwnd = CreateWindowEx(exstyle, s_szName, pszText, style,
172 CW_USEDEFAULT, CW_USEDEFAULT, 50, 50,
173 NULL, NULL, hInstance, this_);
174 ShowWindow(hwnd, SW_SHOWNORMAL);
175 UpdateWindow(hwnd);
176 return hwnd;
177 }
178
DoMainLoop()179 static INT DoMainLoop()
180 {
181 MSG msg;
182 while (GetMessage(&msg, NULL, 0, 0))
183 {
184 TranslateMessage(&msg);
185 DispatchMessage(&msg);
186 }
187 return (INT)msg.wParam;
188 }
189
GetAppbarData(HWND hwnd)190 static Window *GetAppbarData(HWND hwnd)
191 {
192 return (Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
193 }
194
195 virtual LRESULT CALLBACK
WindowProcDx(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)196 WindowProcDx(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
197 {
198 #ifdef VERBOSE
199 MD_msgdump(hwnd, uMsg, wParam, lParam);
200 #endif
201 switch (uMsg)
202 {
203 HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
204 HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
205 HANDLE_MSG(hwnd, WM_ACTIVATE, OnActivate);
206 HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, OnWindowPosChanged);
207 HANDLE_MSG(hwnd, WM_SIZE, OnSize);
208 HANDLE_MSG(hwnd, WM_MOVE, OnMove);
209 HANDLE_MSG(hwnd, WM_NCDESTROY, OnNCDestroy);
210 HANDLE_MSG(hwnd, WM_TIMER, OnTimer);
211 HANDLE_MSG(hwnd, WM_NCHITTEST, OnNCHitTest);
212 HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown);
213 HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove);
214 HANDLE_MSG(hwnd, WM_LBUTTONUP, OnLButtonUp);
215 HANDLE_MSG(hwnd, WM_RBUTTONDOWN, OnRButtonDown);
216 HANDLE_MSG(hwnd, WM_KEYDOWN, OnKey);
217 HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
218
219 case APPBAR_CALLBACK:
220 OnAppBarCallback(hwnd, uMsg, wParam, lParam);
221 break;
222
223 default:
224 return DefWindowProc(hwnd, uMsg, wParam, lParam);
225 }
226 return 0;
227 }
228
229 static LRESULT CALLBACK
WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)230 WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
231 {
232 Window *this_ = GetAppbarData(hwnd);
233 if (uMsg == WM_CREATE)
234 {
235 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
236 this_ = (Window *)pCS->lpCreateParams;
237 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this_);
238 }
239 if (this_)
240 return this_->WindowProcDx(hwnd, uMsg, wParam, lParam);
241 return DefWindowProc(hwnd, uMsg, wParam, lParam);
242 }
243
244 protected:
245 HWND m_hwnd;
246 BOOL m_fAutoHide;
247 BOOL m_fOnTop;
248 BOOL m_fHiding;
249 UINT m_uSide;
250 LONG m_cxWidth;
251 LONG m_cyHeight;
252 LONG m_cxSave;
253 LONG m_cySave;
254 BOOL m_fAppBarRegd;
255 BOOL m_fMoving;
256 BOOL m_bDragged;
257 POINT m_ptDragOn;
258 RECT m_rcAppBar;
259 RECT m_rcDrag;
260
OnCommand(HWND hwnd,int id,HWND hwndCtl,UINT codeNotify)261 void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
262 {
263 HANDLE hThread;
264 switch (id)
265 {
266 case ID_ACTION:
267 PostMessage(s_hwnd2, WM_COMMAND, ID_ACTION + 1, 0);
268 break;
269 case ID_ACTION + 1:
270 hThread = CreateThread(NULL, 0, ActionThreadFunc, this, 0, NULL);
271 if (!hThread)
272 {
273 skip("failed to create thread\n");
274 PostMessage(s_hwnd1, WM_CLOSE, 0, 0);
275 PostMessage(s_hwnd2, WM_CLOSE, 0, 0);
276 return;
277 }
278 CloseHandle(hThread);
279 }
280 }
281
OnPaint(HWND hwnd)282 void OnPaint(HWND hwnd)
283 {
284 PAINTSTRUCT ps;
285
286 TCHAR szText[64];
287 GetWindowText(hwnd, szText, 64);
288
289 RECT rc;
290 GetClientRect(hwnd, &rc);
291
292 if (HDC hdc = BeginPaint(hwnd, &ps))
293 {
294 DrawText(hdc, szText, -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
295 EndPaint(hwnd, &ps);
296 }
297 }
298
OnRButtonDown(HWND hwnd,BOOL fDoubleClick,int x,int y,UINT keyFlags)299 void OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
300 {
301 m_fAutoHide = !m_fAutoHide;
302 AppBar_SetAutoHide(hwnd, m_fAutoHide);
303 }
304
OnKey(HWND hwnd,UINT vk,BOOL fDown,int cRepeat,UINT flags)305 void OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
306 {
307 if (vk == VK_ESCAPE)
308 DestroyWindow(hwnd);
309 }
310
OnAppBarCallback(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)311 void OnAppBarCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
312 {
313 static HWND s_hwndZOrder = NULL;
314
315 switch (wParam)
316 {
317 case ABN_STATECHANGE:
318 break;
319
320 case ABN_FULLSCREENAPP:
321 if (lParam)
322 {
323 s_hwndZOrder = GetWindow(hwnd, GW_HWNDPREV);
324 SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0,
325 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
326 }
327 else
328 {
329 SetWindowPos(hwnd, m_fOnTop ? HWND_TOPMOST : s_hwndZOrder,
330 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
331 s_hwndZOrder = NULL;
332 }
333 break;
334
335 case ABN_POSCHANGED:
336 {
337 APPBARDATA abd = { sizeof(abd) };
338 abd.hWnd = hwnd;
339 AppBar_PosChanged(&abd);
340 }
341 break;
342 }
343 }
344
AppBar_Register(HWND hwnd)345 BOOL AppBar_Register(HWND hwnd)
346 {
347 APPBARDATA abd = { sizeof(abd) };
348 abd.hWnd = hwnd;
349 abd.uCallbackMessage = APPBAR_CALLBACK;
350
351 m_fAppBarRegd = (BOOL)SHAppBarMessage(ABM_NEW, &abd);
352 return m_fAppBarRegd;
353 }
354
AppBar_UnRegister(HWND hwnd)355 BOOL AppBar_UnRegister(HWND hwnd)
356 {
357 APPBARDATA abd = { sizeof(abd) };
358 abd.hWnd = hwnd;
359
360 m_fAppBarRegd = !SHAppBarMessage(ABM_REMOVE, &abd);
361 return !m_fAppBarRegd;
362 }
363
AppBar_SetAutoHide(HWND hwnd,BOOL fHide)364 BOOL AppBar_SetAutoHide(HWND hwnd, BOOL fHide)
365 {
366 if (fHide)
367 return AppBar_AutoHide(hwnd);
368 else
369 return AppBar_NoAutoHide(hwnd);
370 }
371
AppBar_AutoHide(HWND hwnd)372 BOOL AppBar_AutoHide(HWND hwnd)
373 {
374 APPBARDATA abd = { sizeof(abd) };
375 abd.hWnd = hwnd;
376 abd.uEdge = m_uSide;
377
378 HWND hwndAutoHide = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
379 if (hwndAutoHide)
380 return FALSE;
381
382 abd.lParam = TRUE;
383 if (!(BOOL)SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd))
384 return FALSE;
385
386 m_fAutoHide = TRUE;
387 m_cxSave = m_cxWidth;
388 m_cySave = m_cyHeight;
389
390 RECT rc = m_rcAppBar;
391 switch (m_uSide)
392 {
393 case ABE_TOP:
394 rc.bottom = rc.top + 2;
395 break;
396 case ABE_BOTTOM:
397 rc.top = rc.bottom - 2;
398 break;
399 case ABE_LEFT:
400 rc.right = rc.left + 2;
401 break;
402 case ABE_RIGHT:
403 rc.left = rc.right - 2;
404 break;
405 }
406
407 AppBar_QueryPos(hwnd, &rc);
408 abd.rc = rc;
409 SHAppBarMessage(ABM_SETPOS, &abd);
410 rc = abd.rc;
411
412 m_fHiding = TRUE;
413 SlideWindow(hwnd, &rc);
414
415 AppBar_SetAutoHideTimer(hwnd);
416 return TRUE;
417 }
418
AppBar_NoAutoHide(HWND hwnd)419 BOOL AppBar_NoAutoHide(HWND hwnd)
420 {
421 APPBARDATA abd = { sizeof(abd) };
422 abd.hWnd = hwnd;
423 abd.uEdge = m_uSide;
424 HWND hwndAutoHide = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
425 if (hwndAutoHide != hwnd)
426 return FALSE;
427
428 abd.lParam = FALSE;
429 if (!(BOOL)SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd))
430 return FALSE;
431
432 m_fAutoHide = FALSE;
433 m_cxWidth = m_cxSave;
434 m_cyHeight = m_cySave;
435 AppBar_SetSide(hwnd, m_uSide);
436 return TRUE;
437 }
438
AppBar_SetSide(HWND hwnd,UINT uSide)439 BOOL AppBar_SetSide(HWND hwnd, UINT uSide)
440 {
441 RECT rc;
442 SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
443
444 BOOL fAutoHide = FALSE;
445 if (m_fAutoHide)
446 {
447 fAutoHide = m_fAutoHide;
448 SetWindowRedraw(GetDesktopWindow(), FALSE);
449 AppBar_SetAutoHide(hwnd, FALSE);
450 m_fHiding = FALSE;
451 }
452
453 switch (uSide)
454 {
455 case ABE_TOP:
456 rc.bottom = rc.top + m_cyHeight;
457 break;
458 case ABE_BOTTOM:
459 rc.top = rc.bottom - m_cyHeight;
460 break;
461 case ABE_LEFT:
462 rc.right = rc.left + m_cxWidth;
463 break;
464 case ABE_RIGHT:
465 rc.left = rc.right - m_cxWidth;
466 break;
467 }
468
469 APPBARDATA abd = { sizeof(abd) };
470 abd.hWnd = hwnd;
471 AppBar_QuerySetPos(uSide, &rc, &abd, TRUE);
472
473 if (fAutoHide)
474 {
475 AppBar_SetAutoHide(hwnd, TRUE);
476 m_fHiding = TRUE;
477
478 SetWindowRedraw(GetDesktopWindow(), TRUE);
479 RedrawWindow(GetDesktopWindow(), NULL, NULL,
480 RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
481 }
482
483 return TRUE;
484 }
485
AppBar_SetAlwaysOnTop(HWND hwnd,BOOL fOnTop)486 void AppBar_SetAlwaysOnTop(HWND hwnd, BOOL fOnTop)
487 {
488 SetWindowPos(hwnd, (fOnTop ? HWND_TOPMOST : HWND_NOTOPMOST),
489 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
490 m_fOnTop = fOnTop;
491 }
492
AppBar_Hide(HWND hwnd)493 void AppBar_Hide(HWND hwnd)
494 {
495 if (!m_fAutoHide)
496 return;
497
498 RECT rc = m_rcAppBar;
499 switch (m_uSide)
500 {
501 case ABE_TOP:
502 rc.bottom = rc.top + 2;
503 break;
504 case ABE_BOTTOM:
505 rc.top = rc.bottom - 2;
506 break;
507 case ABE_LEFT:
508 rc.right = rc.left + 2;
509 break;
510 case ABE_RIGHT:
511 rc.left = rc.right - 2;
512 break;
513 }
514
515 m_fHiding = TRUE;
516 SlideWindow(hwnd, &rc);
517 }
518
AppBar_UnHide(HWND hwnd)519 void AppBar_UnHide(HWND hwnd)
520 {
521 SlideWindow(hwnd, &m_rcAppBar);
522 m_fHiding = FALSE;
523
524 AppBar_SetAutoHideTimer(hwnd);
525 }
526
AppBar_SetAutoHideTimer(HWND hwnd)527 void AppBar_SetAutoHideTimer(HWND hwnd)
528 {
529 if (m_fAutoHide)
530 {
531 SetTimer(hwnd, IDT_AUTOHIDE, 500, NULL);
532 }
533 }
534
AppBar_SetAutoUnhideTimer(HWND hwnd)535 void AppBar_SetAutoUnhideTimer(HWND hwnd)
536 {
537 if (m_fAutoHide && m_fHiding)
538 {
539 SetTimer(hwnd, IDT_AUTOUNHIDE, 50, NULL);
540 }
541 }
542
AppBar_Size(HWND hwnd)543 void AppBar_Size(HWND hwnd)
544 {
545 if (m_fAppBarRegd)
546 {
547 APPBARDATA abd = { sizeof(abd) };
548 abd.hWnd = hwnd;
549
550 RECT rc;
551 GetWindowRect(hwnd, &rc);
552 AppBar_QuerySetPos(m_uSide, &rc, &abd, TRUE);
553 }
554 }
555
AppBar_QueryPos(HWND hwnd,LPRECT lprc)556 void AppBar_QueryPos(HWND hwnd, LPRECT lprc)
557 {
558 APPBARDATA abd = { sizeof(abd) };
559 abd.hWnd = hwnd;
560 abd.rc = *lprc;
561 abd.uEdge = m_uSide;
562
563 INT cx = 0, cy = 0;
564 if (ABE_LEFT == abd.uEdge || ABE_RIGHT == abd.uEdge)
565 {
566 cx = abd.rc.right - abd.rc.left;
567 abd.rc.top = 0;
568 abd.rc.bottom = GetSystemMetrics(SM_CYSCREEN);
569 }
570 else
571 {
572 cy = abd.rc.bottom - abd.rc.top;
573 abd.rc.left = 0;
574 abd.rc.right = GetSystemMetrics(SM_CXSCREEN);
575 }
576
577 SHAppBarMessage(ABM_QUERYPOS, &abd);
578
579 switch (abd.uEdge)
580 {
581 case ABE_LEFT:
582 abd.rc.right = abd.rc.left + cx;
583 break;
584 case ABE_RIGHT:
585 abd.rc.left = abd.rc.right - cx;
586 break;
587 case ABE_TOP:
588 abd.rc.bottom = abd.rc.top + cy;
589 break;
590 case ABE_BOTTOM:
591 abd.rc.top = abd.rc.bottom - cy;
592 break;
593 }
594
595 *lprc = abd.rc;
596 }
597
AppBar_QuerySetPos(UINT uEdge,LPRECT lprc,PAPPBARDATA pabd,BOOL fMove)598 void AppBar_QuerySetPos(UINT uEdge, LPRECT lprc, PAPPBARDATA pabd, BOOL fMove)
599 {
600 pabd->rc = *lprc;
601 pabd->uEdge = uEdge;
602 m_uSide = uEdge;
603
604 AppBar_QueryPos(pabd->hWnd, &pabd->rc);
605
606 SHAppBarMessage(ABM_SETPOS, pabd);
607
608 if (fMove)
609 {
610 RECT rc = pabd->rc;
611 MoveWindow(pabd->hWnd, rc.left, rc.top,
612 rc.right - rc.left, rc.bottom - rc.top, TRUE);
613 }
614
615 if (!m_fAutoHide)
616 {
617 m_rcAppBar = pabd->rc;
618 }
619 }
620
AppBar_PosChanged(PAPPBARDATA pabd)621 void AppBar_PosChanged(PAPPBARDATA pabd)
622 {
623 RECT rc;
624 SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
625
626 if (m_fAutoHide)
627 {
628 m_rcAppBar = rc;
629 switch (m_uSide)
630 {
631 case ABE_TOP:
632 m_rcAppBar.bottom = m_rcAppBar.top + m_cySave;
633 break;
634 case ABE_BOTTOM:
635 m_rcAppBar.top = m_rcAppBar.bottom - m_cySave;
636 break;
637 case ABE_LEFT:
638 m_rcAppBar.right = m_rcAppBar.left + m_cxSave;
639 break;
640 case ABE_RIGHT:
641 m_rcAppBar.left = m_rcAppBar.right - m_cxSave;
642 break;
643 }
644 }
645
646 RECT rcWindow;
647 GetWindowRect(pabd->hWnd, &rcWindow);
648 INT cx = rcWindow.right - rcWindow.left;
649 INT cy = rcWindow.bottom - rcWindow.top;
650 switch (m_uSide)
651 {
652 case ABE_TOP:
653 rc.bottom = rc.top + cy;
654 break;
655 case ABE_BOTTOM:
656 rc.top = rc.bottom - cy;
657 break;
658 case ABE_LEFT:
659 rc.right = rc.left + cx;
660 break;
661 case ABE_RIGHT:
662 rc.left = rc.right - cx;
663 break;
664 }
665 AppBar_QuerySetPos(m_uSide, &rc, pabd, TRUE);
666 }
667
OnCreate(HWND hwnd,LPCREATESTRUCT lpCreateStruct)668 BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
669 {
670 m_hwnd = hwnd;
671 m_fOnTop = TRUE;
672 m_uSide = ABE_TOP;
673
674 m_fAppBarRegd = FALSE;
675 m_fMoving = FALSE;
676 m_cxSave = m_cxWidth;
677 m_cySave = m_cyHeight;
678 m_bDragged = FALSE;
679
680 AppBar_Register(hwnd);
681 AppBar_SetSide(hwnd, ABE_TOP);
682
683 return TRUE;
684 }
685
OnActivate(HWND hwnd,UINT state,HWND hwndActDeact,BOOL fMinimized)686 void OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized)
687 {
688 APPBARDATA abd = { sizeof(abd) };
689 abd.hWnd = hwnd;
690 SHAppBarMessage(ABM_ACTIVATE, &abd);
691
692 switch (state)
693 {
694 case WA_ACTIVE:
695 case WA_CLICKACTIVE:
696 AppBar_UnHide(hwnd);
697 KillTimer(hwnd, IDT_AUTOHIDE);
698 break;
699
700 case WA_INACTIVE:
701 AppBar_Hide(hwnd);
702 break;
703 }
704 }
705
OnWindowPosChanged(HWND hwnd,const LPWINDOWPOS lpwpos)706 void OnWindowPosChanged(HWND hwnd, const LPWINDOWPOS lpwpos)
707 {
708 APPBARDATA abd = { sizeof(abd) };
709 abd.hWnd = hwnd;
710 SHAppBarMessage(ABM_WINDOWPOSCHANGED, &abd);
711
712 FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, DefWindowProc);
713 }
714
OnSize(HWND hwnd,UINT state,int cx,int cy)715 void OnSize(HWND hwnd, UINT state, int cx, int cy)
716 {
717 RECT rcWindow;
718
719 if (m_fMoving || (m_fAutoHide && m_fHiding))
720 return;
721
722 if (!m_fHiding)
723 {
724 if (!m_fAutoHide)
725 AppBar_Size(hwnd);
726
727 GetWindowRect(hwnd, &rcWindow);
728 m_rcAppBar = rcWindow;
729
730 if (m_uSide == ABE_TOP || m_uSide == ABE_BOTTOM)
731 {
732 m_cyHeight = m_cySave = rcWindow.bottom - rcWindow.top;
733 }
734 else
735 {
736 m_cxWidth = m_cxSave = rcWindow.right - rcWindow.left;
737 }
738 }
739
740 InvalidateRect(hwnd, NULL, TRUE);
741 }
742
OnMove(HWND hwnd,int x,int y)743 void OnMove(HWND hwnd, int x, int y)
744 {
745 if (m_fMoving || m_fAutoHide)
746 return;
747
748 if (!m_fHiding)
749 AppBar_Size(hwnd);
750 }
751
OnNCDestroy(HWND hwnd)752 void OnNCDestroy(HWND hwnd)
753 {
754 AppBar_UnRegister(hwnd);
755
756 m_hwnd = NULL;
757 SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
758 delete this;
759 }
760
OnTimer(HWND hwnd,UINT id)761 void OnTimer(HWND hwnd, UINT id)
762 {
763 POINT pt;
764 RECT rc;
765 HWND hwndActive;
766
767 switch (id)
768 {
769 case IDT_AUTOHIDE:
770 if (m_fAutoHide && !m_fHiding && !m_fMoving)
771 {
772 GetCursorPos(&pt);
773 GetWindowRect(hwnd, &rc);
774 hwndActive = GetForegroundWindow();
775
776 if (!PtInRect(&rc, pt) &&
777 hwndActive != hwnd &&
778 hwndActive != NULL &&
779 GetWindowOwner(hwndActive) != hwnd)
780 {
781 KillTimer(hwnd, id);
782 AppBar_Hide(hwnd);
783 }
784 }
785 break;
786
787 case IDT_AUTOUNHIDE:
788 KillTimer(hwnd, id);
789
790 if (m_fAutoHide && m_fHiding)
791 {
792 GetCursorPos(&pt);
793 GetWindowRect(hwnd, &rc);
794 if (PtInRect(&rc, pt))
795 {
796 AppBar_UnHide(hwnd);
797 }
798 }
799 break;
800 }
801 }
802
OnNCHitTest(HWND hwnd,int x,int y)803 UINT OnNCHitTest(HWND hwnd, int x, int y)
804 {
805 AppBar_SetAutoUnhideTimer(hwnd);
806
807 UINT uHitTest = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc);
808
809 if (m_uSide == ABE_TOP && uHitTest == HTBOTTOM)
810 return HTBOTTOM;
811
812 if (m_uSide == ABE_BOTTOM && uHitTest == HTTOP)
813 return HTTOP;
814
815 if (m_uSide == ABE_LEFT && uHitTest == HTRIGHT)
816 return HTRIGHT;
817
818 if (m_uSide == ABE_RIGHT && uHitTest == HTLEFT)
819 return HTLEFT;
820
821 return HTCLIENT;
822 }
823
OnLButtonDown(HWND hwnd,BOOL fDoubleClick,int x,int y,UINT keyFlags)824 void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
825 {
826 m_fMoving = TRUE;
827 m_bDragged = FALSE;
828 SetCapture(hwnd);
829 GetCursorPos(&m_ptDragOn);
830 }
831
OnMouseMove(HWND hwnd,int x,int y,UINT keyFlags)832 void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
833 {
834 if (!m_fMoving)
835 return;
836
837 POINT pt;
838 GetCursorPos(&pt);
839 if (labs(pt.x - m_ptDragOn.x) > GetSystemMetrics(SM_CXDRAG) ||
840 labs(pt.y - m_ptDragOn.y) > GetSystemMetrics(SM_CYDRAG))
841 {
842 m_bDragged = TRUE;
843 }
844
845 INT cxScreen = GetSystemMetrics(SM_CXSCREEN);
846 INT cyScreen = GetSystemMetrics(SM_CYSCREEN);
847
848 DWORD dx, dy;
849 UINT ix, iy;
850 if (pt.x < cxScreen / 2)
851 {
852 dx = pt.x;
853 ix = ABE_LEFT;
854 }
855 else
856 {
857 dx = cxScreen - pt.x;
858 ix = ABE_RIGHT;
859 }
860
861 if (pt.y < cyScreen / 2)
862 {
863 dy = pt.y;
864 iy = ABE_TOP;
865 }
866 else
867 {
868 dy = cyScreen - pt.y;
869 iy = ABE_BOTTOM;
870 }
871
872 if (cxScreen * dy > cyScreen * dx)
873 {
874 m_rcDrag.top = 0;
875 m_rcDrag.bottom = cyScreen;
876 if (ix == ABE_LEFT)
877 {
878 m_uSide = ABE_LEFT;
879 m_rcDrag.left = 0;
880 m_rcDrag.right = m_rcDrag.left + m_cxWidth;
881 }
882 else
883 {
884 m_uSide = ABE_RIGHT;
885 m_rcDrag.right = cxScreen;
886 m_rcDrag.left = m_rcDrag.right - m_cxWidth;
887 }
888 }
889 else
890 {
891 m_rcDrag.left = 0;
892 m_rcDrag.right = cxScreen;
893 if (iy == ABE_TOP)
894 {
895 m_uSide = ABE_TOP;
896 m_rcDrag.top = 0;
897 m_rcDrag.bottom = m_rcDrag.top + m_cyHeight;
898 }
899 else
900 {
901 m_uSide = ABE_BOTTOM;
902 m_rcDrag.bottom = cyScreen;
903 m_rcDrag.top = m_rcDrag.bottom - m_cyHeight;
904 }
905 }
906
907 AppBar_QueryPos(hwnd, &m_rcDrag);
908
909 if (m_bDragged)
910 {
911 MoveWindow(hwnd, m_rcDrag.left, m_rcDrag.top,
912 m_rcDrag.right - m_rcDrag.left,
913 m_rcDrag.bottom - m_rcDrag.top,
914 TRUE);
915 }
916 }
917
OnLButtonUp(HWND hwnd,int x,int y,UINT keyFlags)918 void OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
919 {
920 if (!m_fMoving)
921 return;
922
923 OnMouseMove(hwnd, x, y, keyFlags);
924
925 m_rcAppBar = m_rcDrag;
926
927 ReleaseCapture();
928
929 if (m_fAutoHide)
930 {
931 switch (m_uSide)
932 {
933 case ABE_TOP:
934 m_rcDrag.bottom = m_rcDrag.top + 2;
935 break;
936 case ABE_BOTTOM:
937 m_rcDrag.top = m_rcDrag.bottom - 2;
938 break;
939 case ABE_LEFT:
940 m_rcDrag.right = m_rcDrag.left + 2;
941 break;
942 case ABE_RIGHT:
943 m_rcDrag.left = m_rcDrag.right - 2;
944 break;
945 }
946 }
947
948 if (m_bDragged)
949 {
950 if (m_fAutoHide)
951 {
952 AppBar_AutoHide(hwnd);
953 }
954 else
955 {
956 APPBARDATA abd = { sizeof(abd) };
957 abd.hWnd = hwnd;
958 AppBar_QuerySetPos(m_uSide, &m_rcDrag, &abd, FALSE);
959 }
960 }
961
962 m_fMoving = FALSE;
963 }
964
GetWorkArea(LPRECT prc) const965 void GetWorkArea(LPRECT prc) const
966 {
967 SystemParametersInfoW(SPI_GETWORKAREA, 0, prc, 0);
968 }
969
970 public:
DoAction()971 void DoAction()
972 {
973 #define INTERVAL 250
974 POINT pt;
975 RECT rc1, rc2, rcWork;
976 DWORD dwTID = GetWindowThreadProcessId(s_hwnd1, NULL);
977
978 GetWindowRect(s_hwnd1, &rc1);
979 GetWindowRect(s_hwnd2, &rc2);
980 GetWorkArea(&rcWork);
981 ok_long(rc1.left, s_rcWorkArea.left);
982 ok_long(rc1.top, s_rcWorkArea.top);
983 ok_long(rc1.right, s_rcWorkArea.right);
984 ok_long(rc1.bottom, s_rcWorkArea.top + 80);
985 ok_long(rc2.left, s_rcWorkArea.left);
986 ok_long(rc2.top, s_rcWorkArea.top + 80);
987 ok_long(rc2.right, s_rcWorkArea.right);
988 ok_long(rc2.bottom, s_rcWorkArea.top + 110);
989 ok_long(rcWork.left, s_rcWorkArea.left);
990 ok_long(rcWork.top, s_rcWorkArea.top + 110);
991 ok_long(rcWork.right, s_rcWorkArea.right);
992 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
993 PostMessage(s_hwnd1, WM_CLOSE, 0, 0);
994 Sleep(INTERVAL);
995
996 GetWindowRect(s_hwnd2, &rc2);
997 GetWorkArea(&rcWork);
998 ok_long(rc2.left, s_rcWorkArea.left);
999 ok_long(rc2.top, s_rcWorkArea.top);
1000 ok_long(rc2.right, s_rcWorkArea.right);
1001 ok_long(rc2.bottom, s_rcWorkArea.top + 30);
1002 ok_long(rcWork.left, s_rcWorkArea.left);
1003 ok_long(rcWork.top, s_rcWorkArea.top + 30);
1004 ok_long(rcWork.right, s_rcWorkArea.right);
1005 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
1006 AppBar_SetSide(s_hwnd2, ABE_LEFT);
1007 Sleep(INTERVAL);
1008
1009 GetWindowRect(s_hwnd2, &rc2);
1010 GetWorkArea(&rcWork);
1011 ok_long(rc2.left, s_rcWorkArea.left);
1012 ok_long(rc2.top, s_rcWorkArea.top);
1013 ok_long(rc2.right, s_rcWorkArea.left + 30);
1014 ok_long(rcWork.left, s_rcWorkArea.left + 30);
1015 ok_long(rcWork.top, s_rcWorkArea.top);
1016 ok_long(rcWork.right, s_rcWorkArea.right);
1017 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
1018 AppBar_SetSide(s_hwnd2, ABE_TOP);
1019 Sleep(INTERVAL);
1020
1021 GetWindowRect(s_hwnd2, &rc2);
1022 GetWorkArea(&rcWork);
1023 ok_long(rc2.left, s_rcWorkArea.left);
1024 ok_long(rc2.top, s_rcWorkArea.top);
1025 ok_long(rc2.right, s_rcWorkArea.right);
1026 ok_long(rc2.bottom, s_rcWorkArea.top + 30);
1027 ok_long(rcWork.left, s_rcWorkArea.left);
1028 ok_long(rcWork.top, s_rcWorkArea.top + 30);
1029 ok_long(rcWork.right, s_rcWorkArea.right);
1030 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
1031 AppBar_SetSide(s_hwnd2, ABE_RIGHT);
1032 Sleep(INTERVAL);
1033
1034 GetWindowRect(s_hwnd2, &rc2);
1035 GetWorkArea(&rcWork);
1036 ok_long(rc2.left, s_rcWorkArea.right - 30);
1037 ok_long(rc2.top, s_rcWorkArea.top);
1038 ok_long(rc2.right, s_rcWorkArea.right);
1039 ok_long(rcWork.left, s_rcWorkArea.left);
1040 ok_long(rcWork.top, s_rcWorkArea.top);
1041 ok_long(rcWork.right, s_rcWorkArea.right - 30);
1042 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
1043 Sleep(INTERVAL);
1044
1045 GetWindowRect(s_hwnd2, &rc2);
1046 pt.x = (rc2.left + rc2.right) / 2;
1047 pt.y = (rc2.top + rc2.bottom) / 2;
1048 MOVE(pt.x, pt.y);
1049 LEFT_DOWN();
1050 MOVE(pt.x + 64, pt.y + 64);
1051 Sleep(INTERVAL);
1052
1053 pt.x = s_rcWorkArea.left + 80;
1054 pt.y = (s_rcWorkArea.top + s_rcWorkArea.bottom) / 2;
1055 MOVE(pt.x, pt.y);
1056 LEFT_UP();
1057 Sleep(INTERVAL);
1058
1059 GetWindowRect(s_hwnd2, &rc2);
1060 GetWorkArea(&rcWork);
1061 ok_long(rc2.left, s_rcWorkArea.left);
1062 ok_long(rc2.top, s_rcWorkArea.top);
1063 ok_long(rc2.right, s_rcWorkArea.left + 30);
1064 ok_long(rcWork.left, s_rcWorkArea.left + 30);
1065 ok_long(rcWork.top, s_rcWorkArea.top);
1066 ok_long(rcWork.right, s_rcWorkArea.right);
1067 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
1068 Sleep(INTERVAL);
1069
1070 GetWindowRect(s_hwnd2, &rc2);
1071 pt.x = (rc2.left + rc2.right) / 2;
1072 pt.y = (rc2.top + rc2.bottom) / 2;
1073 MOVE(pt.x, pt.y);
1074 LEFT_DOWN();
1075 MOVE(pt.x + 64, pt.y + 64);
1076 Sleep(INTERVAL);
1077
1078 pt.x = s_rcWorkArea.right - 80;
1079 pt.y = (s_rcWorkArea.top + s_rcWorkArea.bottom) / 2;
1080 MOVE(pt.x, pt.y);
1081 LEFT_UP();
1082 Sleep(INTERVAL);
1083
1084 GetWindowRect(s_hwnd2, &rc2);
1085 GetWorkArea(&rcWork);
1086 ok_long(rc2.left, s_rcWorkArea.right - 30);
1087 ok_long(rc2.top, s_rcWorkArea.top);
1088 ok_long(rc2.right, s_rcWorkArea.right);
1089 ok_long(rcWork.left, s_rcWorkArea.left);
1090 ok_long(rcWork.top, s_rcWorkArea.top);
1091 ok_long(rcWork.right, s_rcWorkArea.right - 30);
1092 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
1093 Sleep(INTERVAL);
1094
1095 SendMessage(s_hwnd2, WM_CLOSE, 0, 0);
1096 Sleep(INTERVAL);
1097
1098 GetWorkArea(&rcWork);
1099 ok_long(rcWork.left, s_rcWorkArea.left);
1100 ok_long(rcWork.top, s_rcWorkArea.top);
1101 ok_long(rcWork.right, s_rcWorkArea.right);
1102 ok_long(rcWork.bottom, s_rcWorkArea.bottom);
1103
1104 PostMessage(s_hwnd2, WM_QUIT, 0, 0);
1105 PostThreadMessage(dwTID, WM_QUIT, 0, 0);
1106 #undef INTERVAL
1107 }
1108
ActionThreadFunc(LPVOID args)1109 static DWORD WINAPI ActionThreadFunc(LPVOID args)
1110 {
1111 Window *this_ = (Window *)args;
1112 this_->DoAction();
1113 return 0;
1114 }
1115 };
1116
START_TEST(SHAppBarMessage)1117 START_TEST(SHAppBarMessage)
1118 {
1119 HINSTANCE hInstance = GetModuleHandle(NULL);
1120
1121 if (!Window::DoRegisterClass(hInstance))
1122 {
1123 skip("Window::DoRegisterClass failed\n");
1124 return;
1125 }
1126
1127 SystemParametersInfo(SPI_GETWORKAREA, 0, &s_rcWorkArea, FALSE);
1128
1129 HWND hwnd1 = Window::DoCreateMainWnd(hInstance, TEXT("Test1"), 80, 80,
1130 WS_POPUP | WS_THICKFRAME | WS_CLIPCHILDREN);
1131 if (!hwnd1)
1132 {
1133 skip("CreateWindowExW failed\n");
1134 return;
1135 }
1136
1137 HWND hwnd2 = Window::DoCreateMainWnd(hInstance, TEXT("Test2"), 30, 30,
1138 WS_POPUP | WS_BORDER | WS_CLIPCHILDREN);
1139 if (!hwnd2)
1140 {
1141 skip("CreateWindowExW failed\n");
1142 return;
1143 }
1144
1145 s_hwnd1 = hwnd1;
1146 s_hwnd2 = hwnd2;
1147
1148 PostMessage(hwnd1, WM_COMMAND, ID_ACTION, 0);
1149
1150 Window::DoMainLoop();
1151 }
1152