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
get_appbar(HWND hwnd)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
appbar_notify_all(HMONITOR hMon,UINT uMsg,HWND hwndExclude,LPARAM lParam)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 */
send_poschanged(HWND hwnd)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 */
appbar_cliprect(HWND hwnd,RECT * rect)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
handle_appbarmessage(DWORD msg,_AppBarData * abd)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
appbar_message(COPYDATASTRUCT * cds)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