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