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