xref: /reactos/base/shell/explorer/appbar.cpp (revision 40462c92)
1 /*
2  * SHAppBarMessage implementation
3  *
4  * Copyright 2008 Vincent Povirk for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * TODO: freedesktop _NET_WM_STRUT integration
21  *
22  * TODO: find when a fullscreen app is in the foreground and send FULLSCREENAPP
23  *  notifications
24  *
25  * TODO: detect changes in the screen size and send ABN_POSCHANGED ?
26  *
27  * TODO: multiple monitor support
28  */
29 
30 //
31 // Adapted from Wine appbar.c .
32 //
33 
34 #include "precomp.h"
35 
36 #include <wine/list.h>
37 
38 struct appbar_cmd
39 {
40     DWORD  dwMsg;
41     ULONG  return_map;
42     DWORD  return_process;
43     struct _AppBarData abd;
44 };
45 
46 struct appbar_response
47 {
48     ULONGLONG result;
49     struct _AppBarData abd;
50 };
51 
52 struct appbar_data
53 {
54     struct list entry;
55     HWND hwnd;
56     UINT callback_msg;
57     UINT edge;
58     RECT rc;
59     BOOL space_reserved;
60     /* BOOL autohide; */
61 };
62 
63 static struct list appbars = LIST_INIT(appbars);
64 
65 static struct appbar_data* get_appbar(HWND hwnd)
66 {
67     struct appbar_data* data;
68 
69     LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry)
70     {
71         if (data->hwnd == hwnd)
72             return data;
73     }
74 
75     return NULL;
76 }
77 
78 void appbar_notify_all(HMONITOR hMon, UINT uMsg, HWND hwndExclude, LPARAM lParam)
79 {
80     struct appbar_data* data;
81 
82     LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry)
83     {
84         if (data->hwnd == hwndExclude)
85             continue;
86 
87         if (hMon && hMon != MonitorFromWindow(data->hwnd, MONITOR_DEFAULTTONULL))
88             continue;
89 
90         SendMessageW(data->hwnd, data->callback_msg, uMsg, lParam);
91     }
92 }
93 
94 /* send_poschanged: send ABN_POSCHANGED to every appbar except one */
95 static void send_poschanged(HWND hwnd)
96 {
97     appbar_notify_all(NULL, ABN_POSCHANGED, hwnd, 0);
98 }
99 
100 /* appbar_cliprect: cut out parts of the rectangle that interfere with existing appbars */
101 static void appbar_cliprect( HWND hwnd, RECT *rect )
102 {
103     struct appbar_data* data;
104     LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry)
105     {
106         if (data->hwnd == hwnd)
107         {
108             /* we only care about appbars that were added before this one */
109             return;
110         }
111         if (data->space_reserved)
112         {
113             /* move in the side that corresponds to the other appbar's edge */
114             switch (data->edge)
115             {
116             case ABE_BOTTOM:
117                 rect->bottom = min(rect->bottom, data->rc.top);
118                 break;
119             case ABE_LEFT:
120                 rect->left = max(rect->left, data->rc.right);
121                 break;
122             case ABE_RIGHT:
123                 rect->right = min(rect->right, data->rc.left);
124                 break;
125             case ABE_TOP:
126                 rect->top = max(rect->top, data->rc.bottom);
127                 break;
128             }
129         }
130     }
131 }
132 
133 static UINT_PTR handle_appbarmessage(DWORD msg, _AppBarData *abd)
134 {
135     struct appbar_data* data;
136     HWND hwnd = abd->hWnd;
137 
138     switch (msg)
139     {
140     case ABM_NEW:
141         if (get_appbar(hwnd))
142         {
143             /* fail when adding an hwnd the second time */
144             return FALSE;
145         }
146 
147         data = (struct appbar_data*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct appbar_data));
148         if (!data)
149         {
150             ERR("out of memory\n");
151             return FALSE;
152         }
153         data->hwnd = hwnd;
154         data->callback_msg = abd->uCallbackMessage;
155 
156         list_add_tail(&appbars, &data->entry);
157 
158         return TRUE;
159     case ABM_REMOVE:
160         if ((data = get_appbar(hwnd)))
161         {
162             list_remove(&data->entry);
163 
164             send_poschanged(hwnd);
165 
166             HeapFree(GetProcessHeap(), 0, data);
167         }
168         else
169             WARN("removing hwnd %p not on the list\n", hwnd);
170         return TRUE;
171     case ABM_QUERYPOS:
172         if (abd->uEdge > ABE_BOTTOM)
173             WARN("invalid edge %i for %p\n", abd->uEdge, hwnd);
174         appbar_cliprect( hwnd, &abd->rc );
175         return TRUE;
176     case ABM_SETPOS:
177         if (abd->uEdge > ABE_BOTTOM)
178         {
179             WARN("invalid edge %i for %p\n", abd->uEdge, hwnd);
180             return TRUE;
181         }
182         if ((data = get_appbar(hwnd)))
183         {
184             /* calculate acceptable space */
185             appbar_cliprect( hwnd, &abd->rc );
186 
187             if (!EqualRect(&abd->rc, &data->rc))
188                 send_poschanged(hwnd);
189 
190             /* reserve that space for this appbar */
191             data->edge = abd->uEdge;
192             data->rc = abd->rc;
193             data->space_reserved = TRUE;
194         }
195         else
196         {
197             WARN("app sent ABM_SETPOS message for %p without ABM_ADD\n", hwnd);
198         }
199         return TRUE;
200     case ABM_GETSTATE:
201         FIXME("SHAppBarMessage(ABM_GETSTATE): stub\n");
202         return ABS_ALWAYSONTOP | ABS_AUTOHIDE;
203     case ABM_GETTASKBARPOS:
204         FIXME("SHAppBarMessage(ABM_GETTASKBARPOS, hwnd=%p): stub\n", hwnd);
205         /* Report the taskbar is at the bottom of the screen. */
206         abd->rc.left = 0;
207         abd->rc.right = GetSystemMetrics(SM_CXSCREEN);
208         abd->rc.bottom = GetSystemMetrics(SM_CYSCREEN);
209         abd->rc.top = abd->rc.bottom-1;
210         abd->uEdge = ABE_BOTTOM;
211         return TRUE;
212     case ABM_ACTIVATE:
213         return TRUE;
214     case ABM_GETAUTOHIDEBAR:
215         FIXME("SHAppBarMessage(ABM_GETAUTOHIDEBAR, hwnd=%p, edge=%x): stub\n", hwnd, abd->uEdge);
216         return 0;
217     case ABM_SETAUTOHIDEBAR:
218         FIXME("SHAppBarMessage(ABM_SETAUTOHIDEBAR, hwnd=%p, edge=%x, lparam=%s): stub\n",
219                    hwnd, abd->uEdge, wine_dbgstr_longlong(abd->lParam));
220         return TRUE;
221     case ABM_WINDOWPOSCHANGED:
222         return TRUE;
223     default:
224         FIXME("SHAppBarMessage(%x) unimplemented\n", msg);
225         return FALSE;
226     }
227 }
228 
229 LRESULT appbar_message ( COPYDATASTRUCT* cds )
230 {
231     struct appbar_cmd cmd;
232     UINT_PTR result;
233     HANDLE return_hproc;
234     HANDLE return_map;
235     LPVOID return_view;
236     struct appbar_response* response;
237 
238     if (cds->cbData != sizeof(struct appbar_cmd))
239         return TRUE;
240 
241     RtlCopyMemory(&cmd, cds->lpData, cds->cbData);
242 
243     result = handle_appbarmessage(cmd.dwMsg, &cmd.abd);
244 
245     return_hproc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, cmd.return_process);
246     if (return_hproc == NULL)
247     {
248         ERR("couldn't open calling process\n");
249         return TRUE;
250     }
251 
252     if (!DuplicateHandle(return_hproc, UlongToHandle(cmd.return_map), GetCurrentProcess(), &return_map, 0, FALSE, DUPLICATE_SAME_ACCESS))
253     {
254         ERR("couldn't duplicate handle\n");
255         CloseHandle(return_hproc);
256         return TRUE;
257     }
258     CloseHandle(return_hproc);
259 
260     return_view = MapViewOfFile(return_map, FILE_MAP_WRITE, 0, 0, sizeof(struct appbar_response));
261 
262     if (return_view)
263     {
264         response = (struct appbar_response*)return_view;
265         response->result = result;
266         response->abd = cmd.abd;
267 
268         UnmapViewOfFile(return_view);
269     }
270     else
271     {
272         ERR("couldn't map view of file\n");
273     }
274 
275     CloseHandle(return_map);
276     return TRUE;
277 }
278