xref: /reactos/base/shell/explorer/appbar.cpp (revision cc3672cb)
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 #define GetPrimaryTaskbar() FindWindowW(L"Shell_TrayWnd", NULL)
39 
40 struct appbar_cmd
41 {
42     DWORD  dwMsg;
43     ULONG  return_map;
44     DWORD  return_process;
45     struct _AppBarData abd;
46 };
47 
48 struct appbar_response
49 {
50     ULONGLONG result;
51     struct _AppBarData abd;
52 };
53 
54 struct appbar_data
55 {
56     struct list entry;
57     HWND hwnd;
58     UINT callback_msg;
59     UINT edge;
60     RECT rc;
61     BOOL space_reserved;
62     /* BOOL autohide; */
63 };
64 
65 static struct list appbars = LIST_INIT(appbars);
66 
67 static struct appbar_data* get_appbar(HWND hwnd)
68 {
69     struct appbar_data* data;
70 
71     LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry)
72     {
73         if (data->hwnd == hwnd)
74             return data;
75     }
76 
77     return NULL;
78 }
79 
80 void appbar_notify_all(HMONITOR hMon, UINT uMsg, HWND hwndExclude, LPARAM lParam)
81 {
82     struct appbar_data* data;
83 
84     LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry)
85     {
86         if (data->hwnd == hwndExclude)
87             continue;
88 
89         if (hMon && hMon != MonitorFromWindow(data->hwnd, MONITOR_DEFAULTTONULL))
90             continue;
91 
92         SendMessageW(data->hwnd, data->callback_msg, uMsg, lParam);
93     }
94 }
95 
96 /* send_poschanged: send ABN_POSCHANGED to every appbar except one */
97 static void send_poschanged(HWND hwnd)
98 {
99     appbar_notify_all(NULL, ABN_POSCHANGED, hwnd, 0);
100 }
101 
102 /* appbar_cliprect: cut out parts of the rectangle that interfere with existing appbars */
103 static void appbar_cliprect( HWND hwnd, RECT *rect )
104 {
105     struct appbar_data* data;
106     LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry)
107     {
108         if (data->hwnd == hwnd)
109         {
110             /* we only care about appbars that were added before this one */
111             return;
112         }
113         if (data->space_reserved)
114         {
115             /* move in the side that corresponds to the other appbar's edge */
116             switch (data->edge)
117             {
118             case ABE_BOTTOM:
119                 rect->bottom = min(rect->bottom, data->rc.top);
120                 break;
121             case ABE_LEFT:
122                 rect->left = max(rect->left, data->rc.right);
123                 break;
124             case ABE_RIGHT:
125                 rect->right = min(rect->right, data->rc.left);
126                 break;
127             case ABE_TOP:
128                 rect->top = max(rect->top, data->rc.bottom);
129                 break;
130             }
131         }
132     }
133 }
134 
135 static UINT_PTR handle_appbarmessage(DWORD msg, _AppBarData *abd)
136 {
137     struct appbar_data* data;
138     HWND hwnd = abd->hWnd;
139 
140     switch (msg)
141     {
142     case ABM_NEW:
143         if (get_appbar(hwnd))
144         {
145             /* fail when adding an hwnd the second time */
146             return FALSE;
147         }
148 
149         data = (struct appbar_data*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct appbar_data));
150         if (!data)
151         {
152             ERR("out of memory\n");
153             return FALSE;
154         }
155         data->hwnd = hwnd;
156         data->callback_msg = abd->uCallbackMessage;
157 
158         list_add_tail(&appbars, &data->entry);
159 
160         return TRUE;
161     case ABM_REMOVE:
162         if ((data = get_appbar(hwnd)))
163         {
164             list_remove(&data->entry);
165 
166             send_poschanged(hwnd);
167 
168             HeapFree(GetProcessHeap(), 0, data);
169         }
170         else
171             WARN("removing hwnd %p not on the list\n", hwnd);
172         return TRUE;
173     case ABM_QUERYPOS:
174         if (abd->uEdge > ABE_BOTTOM)
175             WARN("invalid edge %i for %p\n", abd->uEdge, hwnd);
176         appbar_cliprect( hwnd, &abd->rc );
177         return TRUE;
178     case ABM_SETPOS:
179         if (abd->uEdge > ABE_BOTTOM)
180         {
181             WARN("invalid edge %i for %p\n", abd->uEdge, hwnd);
182             return TRUE;
183         }
184         if ((data = get_appbar(hwnd)))
185         {
186             /* calculate acceptable space */
187             appbar_cliprect( hwnd, &abd->rc );
188 
189             if (!EqualRect(&abd->rc, &data->rc))
190                 send_poschanged(hwnd);
191 
192             /* reserve that space for this appbar */
193             data->edge = abd->uEdge;
194             data->rc = abd->rc;
195             data->space_reserved = TRUE;
196         }
197         else
198         {
199             WARN("app sent ABM_SETPOS message for %p without ABM_ADD\n", hwnd);
200         }
201         return TRUE;
202     case ABM_GETSTATE:
203         TRACE("SHAppBarMessage(ABM_GETSTATE)\n");
204         return (g_TaskbarSettings.sr.AutoHide ? ABS_AUTOHIDE : 0) |
205                (g_TaskbarSettings.sr.AlwaysOnTop ? ABS_ALWAYSONTOP : 0);
206     case ABM_SETSTATE:
207         TRACE("SHAppBarMessage(ABM_SETSTATE lparam=%s)\n", wine_dbgstr_longlong(abd->lParam));
208         hwnd = GetPrimaryTaskbar();
209         if (hwnd)
210         {
211             TaskbarSettings settings = g_TaskbarSettings;
212             settings.sr.AutoHide = (abd->lParam & ABS_AUTOHIDE) != 0;
213             settings.sr.AlwaysOnTop = (abd->lParam & ABS_ALWAYSONTOP) != 0;
214             SendMessageW(hwnd, TWM_SETTINGSCHANGED, 0, (LPARAM)&settings);
215             return TRUE;
216         }
217         return FALSE;
218     case ABM_GETTASKBARPOS:
219         TRACE("SHAppBarMessage(ABM_GETTASKBARPOS, hwnd=%p)\n", hwnd);
220         abd->uEdge = g_TaskbarSettings.sr.Position;
221         abd->hWnd = GetPrimaryTaskbar();
222         return abd->hWnd && GetWindowRect(abd->hWnd, &abd->rc);
223     case ABM_ACTIVATE:
224         return TRUE;
225     case ABM_GETAUTOHIDEBAR:
226         FIXME("SHAppBarMessage(ABM_GETAUTOHIDEBAR, hwnd=%p, edge=%x): stub\n", hwnd, abd->uEdge);
227         if (abd->uEdge == g_TaskbarSettings.sr.Position && g_TaskbarSettings.sr.AutoHide)
228             return (SIZE_T)GetPrimaryTaskbar();
229         return NULL;
230     case ABM_SETAUTOHIDEBAR:
231         FIXME("SHAppBarMessage(ABM_SETAUTOHIDEBAR, hwnd=%p, edge=%x, lparam=%s): stub\n",
232                    hwnd, abd->uEdge, wine_dbgstr_longlong(abd->lParam));
233         return TRUE;
234     case ABM_WINDOWPOSCHANGED:
235         return TRUE;
236     default:
237         FIXME("SHAppBarMessage(%x) unimplemented\n", msg);
238         return FALSE;
239     }
240 }
241 
242 LRESULT appbar_message ( COPYDATASTRUCT* cds )
243 {
244     struct appbar_cmd cmd;
245     UINT_PTR result;
246     HANDLE return_hproc;
247     HANDLE return_map;
248     LPVOID return_view;
249     struct appbar_response* response;
250 
251     if (cds->cbData != sizeof(struct appbar_cmd))
252         return TRUE;
253 
254     RtlCopyMemory(&cmd, cds->lpData, cds->cbData);
255 
256     result = handle_appbarmessage(cmd.dwMsg, &cmd.abd);
257 
258     return_hproc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, cmd.return_process);
259     if (return_hproc == NULL)
260     {
261         ERR("couldn't open calling process\n");
262         return TRUE;
263     }
264 
265     if (!DuplicateHandle(return_hproc, UlongToHandle(cmd.return_map), GetCurrentProcess(), &return_map, 0, FALSE, DUPLICATE_SAME_ACCESS))
266     {
267         ERR("couldn't duplicate handle\n");
268         CloseHandle(return_hproc);
269         return TRUE;
270     }
271     CloseHandle(return_hproc);
272 
273     return_view = MapViewOfFile(return_map, FILE_MAP_WRITE, 0, 0, sizeof(struct appbar_response));
274 
275     if (return_view)
276     {
277         response = (struct appbar_response*)return_view;
278         response->result = result;
279         response->abd = cmd.abd;
280 
281         UnmapViewOfFile(return_view);
282     }
283     else
284     {
285         ERR("couldn't map view of file\n");
286     }
287 
288     CloseHandle(return_map);
289     return TRUE;
290 }
291