/* * SHAppBarMessage implementation * * Copyright 2008 Vincent Povirk for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * TODO: freedesktop _NET_WM_STRUT integration * * TODO: find when a fullscreen app is in the foreground and send FULLSCREENAPP * notifications * * TODO: detect changes in the screen size and send ABN_POSCHANGED ? * * TODO: multiple monitor support */ #include "windows.h" #include "shellapi.h" #include "wine/debug.h" #include "explorer_private.h" #include "wine/list.h" WINE_DEFAULT_DEBUG_CHANNEL(appbar); struct appbar_data_msg /* platform-independent data */ { LONG hWnd; UINT uCallbackMessage; UINT uEdge; RECT rc; ULONGLONG lParam; }; struct appbar_cmd { ULONG return_map; DWORD return_process; struct appbar_data_msg abd; }; struct appbar_response { ULONGLONG result; struct appbar_data_msg abd; }; static HWND appbarmsg_window = NULL; struct appbar_data { struct list entry; HWND hwnd; UINT callback_msg; UINT edge; RECT rc; BOOL space_reserved; /* BOOL autohide; */ }; static struct list appbars = LIST_INIT(appbars); static struct appbar_data* get_appbar(HWND hwnd) { struct appbar_data* data; LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) { if (data->hwnd == hwnd) return data; } return NULL; } /* send_poschanged: send ABN_POSCHANGED to every appbar except one */ static void send_poschanged(HWND hwnd) { struct appbar_data* data; LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) { if (data->hwnd != hwnd) { PostMessageW(data->hwnd, data->callback_msg, ABN_POSCHANGED, 0); } } } /* appbar_cliprect: cut out parts of the rectangle that interfere with existing appbars */ static void appbar_cliprect( HWND hwnd, RECT *rect ) { struct appbar_data* data; LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) { if (data->hwnd == hwnd) { /* we only care about appbars that were added before this one */ return; } if (data->space_reserved) { /* move in the side that corresponds to the other appbar's edge */ switch (data->edge) { case ABE_BOTTOM: rect->bottom = min(rect->bottom, data->rc.top); break; case ABE_LEFT: rect->left = max(rect->left, data->rc.right); break; case ABE_RIGHT: rect->right = min(rect->right, data->rc.left); break; case ABE_TOP: rect->top = max(rect->top, data->rc.bottom); break; } } } } static UINT_PTR handle_appbarmessage(DWORD msg, struct appbar_data_msg *abd) { struct appbar_data* data; HWND hwnd = LongToHandle( abd->hWnd ); switch (msg) { case ABM_NEW: if (get_appbar(hwnd)) { /* fail when adding an hwnd the second time */ return FALSE; } data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct appbar_data)); if (!data) { WINE_ERR("out of memory\n"); return FALSE; } data->hwnd = hwnd; data->callback_msg = abd->uCallbackMessage; list_add_tail(&appbars, &data->entry); return TRUE; case ABM_REMOVE: if ((data = get_appbar(hwnd))) { list_remove(&data->entry); send_poschanged(hwnd); HeapFree(GetProcessHeap(), 0, data); } else WINE_WARN("removing hwnd %p not on the list\n", hwnd); return TRUE; case ABM_QUERYPOS: if (abd->uEdge > ABE_BOTTOM) WINE_WARN("invalid edge %i for %p\n", abd->uEdge, hwnd); appbar_cliprect( hwnd, &abd->rc ); return TRUE; case ABM_SETPOS: if (abd->uEdge > ABE_BOTTOM) { WINE_WARN("invalid edge %i for %p\n", abd->uEdge, hwnd); return TRUE; } if ((data = get_appbar(hwnd))) { /* calculate acceptable space */ appbar_cliprect( hwnd, &abd->rc ); if (!EqualRect(&abd->rc, &data->rc)) send_poschanged(hwnd); /* reserve that space for this appbar */ data->edge = abd->uEdge; data->rc = abd->rc; data->space_reserved = TRUE; } else { WINE_WARN("app sent ABM_SETPOS message for %p without ABM_ADD\n", hwnd); } return TRUE; case ABM_GETSTATE: WINE_FIXME("SHAppBarMessage(ABM_GETSTATE): stub\n"); return ABS_ALWAYSONTOP | ABS_AUTOHIDE; case ABM_GETTASKBARPOS: WINE_FIXME("SHAppBarMessage(ABM_GETTASKBARPOS, hwnd=%p): stub\n", hwnd); /* Report the taskbar is at the bottom of the screen. */ abd->rc.left = 0; abd->rc.right = GetSystemMetrics(SM_CXSCREEN); abd->rc.bottom = GetSystemMetrics(SM_CYSCREEN); abd->rc.top = abd->rc.bottom-1; abd->uEdge = ABE_BOTTOM; return TRUE; case ABM_ACTIVATE: return TRUE; case ABM_GETAUTOHIDEBAR: WINE_FIXME("SHAppBarMessage(ABM_GETAUTOHIDEBAR, hwnd=%p, edge=%x): stub\n", hwnd, abd->uEdge); return 0; case ABM_SETAUTOHIDEBAR: WINE_FIXME("SHAppBarMessage(ABM_SETAUTOHIDEBAR, hwnd=%p, edge=%x, lparam=%s): stub\n", hwnd, abd->uEdge, wine_dbgstr_longlong(abd->lParam)); return TRUE; case ABM_WINDOWPOSCHANGED: return TRUE; default: WINE_FIXME("SHAppBarMessage(%lx) unimplemented\n", msg); return FALSE; } } static LRESULT CALLBACK appbar_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_COPYDATA: { COPYDATASTRUCT* cds; struct appbar_cmd cmd; UINT_PTR result; HANDLE return_hproc; HANDLE return_map; LPVOID return_view; struct appbar_response* response; cds = (COPYDATASTRUCT*)lparam; if (cds->cbData != sizeof(struct appbar_cmd)) return TRUE; CopyMemory(&cmd, cds->lpData, cds->cbData); result = handle_appbarmessage(cds->dwData, &cmd.abd); return_hproc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, cmd.return_process); if (return_hproc == NULL) { WINE_ERR("couldn't open calling process\n"); return TRUE; } if (!DuplicateHandle(return_hproc, UlongToHandle(cmd.return_map), GetCurrentProcess(), &return_map, 0, FALSE, DUPLICATE_SAME_ACCESS)) { WINE_ERR("couldn't duplicate handle\n"); CloseHandle(return_hproc); return TRUE; } CloseHandle(return_hproc); return_view = MapViewOfFile(return_map, FILE_MAP_WRITE, 0, 0, sizeof(struct appbar_response)); if (return_view) { response = (struct appbar_response*)return_view; response->result = result; response->abd = cmd.abd; UnmapViewOfFile(return_view); } else WINE_ERR("couldn't map view of file\n"); CloseHandle(return_map); return TRUE; } default: break; } return DefWindowProcW(hwnd, msg, wparam, lparam); } void initialize_appbar(void) { WNDCLASSEXW class; static const WCHAR classname[] = {'W','i','n','e','A','p','p','B','a','r',0}; /* register the appbar window class */ ZeroMemory(&class, sizeof(class)); class.cbSize = sizeof(class); class.lpfnWndProc = appbar_wndproc; class.hInstance = NULL; class.lpszClassName = classname; if (!RegisterClassExW(&class)) { WINE_ERR("Could not register appbar message window class\n"); return; } appbarmsg_window = CreateWindowW(classname, classname, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); if (!appbarmsg_window) { WINE_ERR("Could not create appbar message window\n"); return; } }