This implements the user32 and server-side of keeping track of cloaking, without actually doing the cloaking, which is done by the driver. The driver function SetWindowCompositionAttribute must return whether it supports cloaking, and whether it's a manual cloaking done by the app, or the shell cloaking a window.
Manually cloaked windows can be activated and set to foreground. They also remain on the taskbar and can be activated from the taskbar too (and function like uncloaked windows), but are otherwise visibly hidden. WindowFromPoint doesn't work to retrieve them, though.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This patchset will attempt to implement both manual cloaking done by applications, and shell cloaking using the WM (for virtual desktops/workspaces for example).
It uses the undocumented user32 API SetWindowCompositionAttribute to do so. Of course, the documented DwmSetWindowAttribute will just be a wrapper around this one.
dlls/user32/driver.c | 9 +++ dlls/user32/message.c | 2 + dlls/user32/user_private.h | 41 +++++++++++++ dlls/user32/win.c | 121 ++++++++++++++++++++++++++++++++++--- server/protocol.def | 13 ++++ server/window.c | 80 +++++++++++++++++++++--- 6 files changed, 250 insertions(+), 16 deletions(-)
diff --git a/dlls/user32/driver.c b/dlls/user32/driver.c index 7ac7714..6dad5dc 100644 --- a/dlls/user32/driver.c +++ b/dlls/user32/driver.c @@ -140,6 +140,7 @@ static const USER_DRIVER *load_driver(void) GET_USER_FUNC(SetFocus); GET_USER_FUNC(SetLayeredWindowAttributes); GET_USER_FUNC(SetParent); + GET_USER_FUNC(SetWindowCompositionAttribute); GET_USER_FUNC(SetWindowRgn); GET_USER_FUNC(SetWindowIcon); GET_USER_FUNC(SetWindowStyle); @@ -421,6 +422,12 @@ static void CDECL nulldrv_SetParent( HWND hwnd, HWND parent, HWND old_parent ) { }
+static DWORD CDECL nulldrv_SetWindowCompositionAttribute( HWND hwnd, DWORD attribute, void *data ) +{ + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return ~0; +} + static void CDECL nulldrv_SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw ) { } @@ -522,6 +529,7 @@ static USER_DRIVER null_driver = nulldrv_SetFocus, nulldrv_SetLayeredWindowAttributes, nulldrv_SetParent, + nulldrv_SetWindowCompositionAttribute, nulldrv_SetWindowRgn, nulldrv_SetWindowIcon, nulldrv_SetWindowStyle, @@ -737,6 +745,7 @@ static USER_DRIVER lazy_load_driver = nulldrv_SetFocus, loaderdrv_SetLayeredWindowAttributes, nulldrv_SetParent, + nulldrv_SetWindowCompositionAttribute, loaderdrv_SetWindowRgn, nulldrv_SetWindowIcon, nulldrv_SetWindowStyle, diff --git a/dlls/user32/message.c b/dlls/user32/message.c index 4434f4b..67f89bb 100644 --- a/dlls/user32/message.c +++ b/dlls/user32/message.c @@ -1887,6 +1887,8 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR case WM_WINE_UPDATEWINDOWSTATE: update_window_state( hwnd ); return 0; + case WM_WINE_SETWINDOWCLOAKED: + return USER_Driver->pSetWindowCompositionAttribute( hwnd, WCA_CLOAK, &wparam ); default: if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_MSG) return USER_Driver->pWindowMessage( hwnd, msg, wparam, lparam ); diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 7761a1c..3baf39c 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -56,6 +56,7 @@ enum wine_internal_message WM_WINE_MOUSE_LL_HOOK, WM_WINE_CLIPCURSOR, WM_WINE_UPDATEWINDOWSTATE, + WM_WINE_SETWINDOWCLOAKED, WM_WINE_FIRST_DRIVER_MSG = 0x80001000, /* range of messages reserved for the USER driver */ WM_WINE_LAST_DRIVER_MSG = 0x80001fff }; @@ -101,6 +102,7 @@ typedef struct tagUSER_DRIVER { void (CDECL *pSetFocus)(HWND); void (CDECL *pSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD); void (CDECL *pSetParent)(HWND,HWND,HWND); + DWORD (CDECL *pSetWindowCompositionAttribute)(HWND,DWORD,void*); void (CDECL *pSetWindowRgn)(HWND,HRGN,BOOL); void (CDECL *pSetWindowIcon)(HWND,UINT,HICON); void (CDECL *pSetWindowStyle)(HWND,INT,STYLESTRUCT*); @@ -366,6 +368,45 @@ typedef struct
#include "poppack.h"
+/* Undocumented structure for (Get|Set)WindowCompositionAttribute */ +struct WINCOMPATTRDATA +{ + DWORD attribute; + void *pData; + ULONG dataSize; +}; +enum +{ + WCA_UNDEFINED = 0, + WCA_NCRENDERING_ENABLED = 1, + WCA_NCRENDERING_POLICY = 2, + WCA_TRANSITIONS_FORCEDISABLED = 3, + WCA_ALLOW_NCPAINT = 4, + WCA_CAPTION_BUTTON_BOUNDS = 5, + WCA_NONCLIENT_RTL_LAYOUT = 6, + WCA_FORCE_ICONIC_REPRESENTATION = 7, + WCA_EXTENDED_FRAME_BOUNDS = 8, + WCA_HAS_ICONIC_BITMAP = 9, + WCA_THEME_ATTRIBUTES = 10, + WCA_NCRENDERING_EXILED = 11, + WCA_NCADORNMENTINFO = 12, + WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, + WCA_VIDEO_OVERLAY_ACTIVE = 14, + WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, + WCA_DISALLOW_PEEK = 16, + WCA_CLOAK = 17, + WCA_CLOAKED = 18, + WCA_ACCENT_POLICY = 19, + WCA_FREEZE_REPRESENTATION = 20, + WCA_EVER_UNCLOAKED = 21, + WCA_VISUAL_OWNER = 22, + WCA_HOLOGRAPHIC = 23, + WCA_EXCLUDED_FROM_DDA = 24, + WCA_PASSIVEUPDATEMODE = 25, + WCA_USEDARKMODECOLORS = 26, + WCA_LAST +}; + extern int bitmap_info_size( const BITMAPINFO * info, WORD coloruse ) DECLSPEC_HIDDEN; extern BOOL get_icon_size( HICON handle, SIZE *size ) DECLSPEC_HIDDEN;
diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 680defc..9840a76 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -27,6 +27,7 @@ #include "winbase.h" #include "winnls.h" #include "winver.h" +#include "dwmapi.h" #include "wine/server.h" #include "wine/asm.h" #include "win.h" @@ -54,6 +55,8 @@ static CRITICAL_SECTION_DEBUG critsect_debug = }; static CRITICAL_SECTION surfaces_section = { &critsect_debug, -1, 0, 0, 0, 0 };
+BOOL WINAPI SetWindowCompositionAttribute(HWND, const struct WINCOMPATTRDATA*); + /**********************************************************************/
/* helper for Get/SetWindowLong */ @@ -191,7 +194,7 @@ void *free_user_handle( HANDLE handle, enum user_obj_type type ) * Create a window handle with the server. */ static WND *create_window_handle( HWND parent, HWND owner, LPCWSTR name, - HINSTANCE instance, BOOL unicode ) + HINSTANCE instance, BOOL unicode, BOOL *needs_cloak ) { WORD index; WND *win; @@ -219,6 +222,7 @@ static WND *create_window_handle( HWND parent, HWND owner, LPCWSTR name, dpi = reply->dpi; awareness = reply->awareness; class = wine_server_get_ptr( reply->class_ptr ); + *needs_cloak = reply->needs_cloak; } } SERVER_END_REQ; @@ -749,6 +753,7 @@ HWND WIN_GetFullHandle( HWND hwnd ) static HWND WIN_SetOwner( HWND hwnd, HWND owner ) { WND *win = WIN_GetPtr( hwnd ); + BOOL needs_cloak = FALSE; HWND ret = 0;
if (!win || win == WND_DESKTOP) return 0; @@ -765,10 +770,19 @@ static HWND WIN_SetOwner( HWND hwnd, HWND owner ) { win->owner = wine_server_ptr_handle( reply->full_owner ); ret = wine_server_ptr_handle( reply->prev_owner ); + needs_cloak = reply->needs_cloak; } } SERVER_END_REQ; WIN_ReleasePtr( win ); + if (needs_cloak) + { + struct WINCOMPATTRDATA data; + data.attribute = WCA_CLOAK; + data.pData = &needs_cloak; + data.dataSize = sizeof(needs_cloak); + SetWindowCompositionAttribute( hwnd, &data ); + } return ret; }
@@ -1349,6 +1363,7 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module, MDICREATESTRUCTW mdi_cs; CBT_CREATEWNDW cbtc; CREATESTRUCTW cbcs; + BOOL needs_cloak;
className = CLASS_GetVersionedName(className, NULL, NULL, TRUE);
@@ -1476,13 +1491,13 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module,
/* Create the window structure */
- if (!(wndPtr = create_window_handle( parent, owner, className, module, unicode ))) + if (!(wndPtr = create_window_handle( parent, owner, className, module, unicode, &needs_cloak ))) { WNDCLASSW wc; /* if it's a comctl32 class, GetClassInfo will load it, then we can retry */ if (GetLastError() != ERROR_INVALID_HANDLE || !GetClassInfoW( 0, className, &wc ) || - !(wndPtr = create_window_handle( parent, owner, className, module, unicode ))) + !(wndPtr = create_window_handle( parent, owner, className, module, unicode, &needs_cloak ))) return 0; } hwnd = wndPtr->obj.handle; @@ -1663,6 +1678,8 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module, /* call the driver */
if (!USER_Driver->pCreateWindow( hwnd )) goto failed; + if (needs_cloak) + USER_Driver->pSetWindowCompositionAttribute( hwnd, WCA_CLOAK, &needs_cloak );
NotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0);
@@ -4233,9 +4250,99 @@ BOOL WINAPI SetWindowDisplayAffinity(HWND hwnd, DWORD affinity) /********************************************************************** * SetWindowCompositionAttribute (USER32.@) */ -BOOL WINAPI SetWindowCompositionAttribute(HWND hwnd, void *data) +BOOL WINAPI SetWindowCompositionAttribute(HWND hwnd, const struct WINCOMPATTRDATA *data) { - FIXME("(%p, %p): stub\n", hwnd, data); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + user_handle_t *list; + unsigned i, size; + NTSTATUS status; + HWND parent; + DWORD ret; + WND *win; + + TRACE("(%p, %p)\n", hwnd, data); + + if (!data || !data->pData) + { + SetLastError( ERROR_NOACCESS ); + return FALSE; + } + if (!hwnd || is_broadcast(hwnd) || !(win = WIN_GetPtr(hwnd))) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + if (win == WND_DESKTOP || win == WND_OTHER_PROCESS) + { + SetLastError( ERROR_ACCESS_DENIED ); + return FALSE; + } + parent = win->parent; + WIN_ReleasePtr(win); + if (parent && parent != GetDesktopWindow()) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + switch (data->attribute) + { + case WCA_CLOAK: + if (data->dataSize < sizeof(BOOL)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + ret = USER_Driver->pSetWindowCompositionAttribute( hwnd, WCA_CLOAK, data->pData ); + if (ret == ~0) return FALSE; + + size = 128; + for (;;) + { + unsigned count = 0; + + if (!(list = HeapAlloc( GetProcessHeap(), 0, size * sizeof(user_handle_t) ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + SERVER_START_REQ( set_window_cloaked ) + { + req->handle = wine_server_user_handle( hwnd ); + req->cloaked = ret; + wine_server_set_reply( req, list, size * sizeof(user_handle_t) ); + if (!(status = wine_server_call( req ))) count = reply->count; + } + SERVER_END_REQ; + if (count < size) + { + /* Go through the list to cloak the windows that inherit it */ + for (i = 0; i < count; i++) + { + HWND full_handle, handle = wine_server_ptr_handle( list[i] ); + + if ((full_handle = WIN_IsCurrentProcess( handle ))) + USER_Driver->pSetWindowCompositionAttribute( full_handle, WCA_CLOAK, data->pData ); + else + SendMessageW( handle, WM_WINE_SETWINDOWCLOAKED, *(BOOL*)(data->pData), 0 ); + } + count = 0; + } + HeapFree( GetProcessHeap(), 0, list ); + if (!count) break; + size = count; /* restart with a large enough buffer */ + } + if (status) + { + SetLastError( RtlNtStatusToDosError( status )); + return FALSE; + } + return TRUE; + + default: + FIXME("unimplemented attribute %d, size %u, for hwnd %p.\n", data->attribute, data->dataSize, hwnd); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; + } + return TRUE; } diff --git a/server/protocol.def b/server/protocol.def index 846d2e1..d96d2e7 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2226,6 +2226,7 @@ enum message_type client_ptr_t class_ptr; /* pointer to class in client address space */ int dpi; /* window DPI if not per-monitor aware */ int awareness; /* window DPI awareness */ + int needs_cloak; /* the window needs to be cloaked by the driver */ @END
@@ -2251,6 +2252,7 @@ enum message_type @REPLY user_handle_t full_owner; /* full handle of new owner */ user_handle_t prev_owner; /* full handle of previous owner */ + int needs_cloak; /* the owned window needs to be cloaked */ @END
@@ -2299,6 +2301,17 @@ enum message_type #define SET_WIN_UNICODE 0x40
+/* Set the window's cloaked attribute */ +@REQ(set_window_cloaked) + user_handle_t handle; /* handle to the window */ + unsigned int cloaked; /* cloaked attribute to set (see below) */ +@REPLY + unsigned int count; /* total count of windows inheriting it */ + VARARG(windows,user_handles); /* window handles that inherit it */ +@END +#define SET_WINDOW_CLOAKED_ON 0x01 + + /* Set the parent of a window */ @REQ(set_parent) user_handle_t handle; /* handle to the window */ diff --git a/server/window.c b/server/window.c index 3a88b7f..e6fc90c 100644 --- a/server/window.c +++ b/server/window.c @@ -81,6 +81,7 @@ struct window unsigned int is_unicode : 1; /* ANSI or unicode */ unsigned int is_linked : 1; /* is it linked into the parent z-order list? */ unsigned int is_layered : 1; /* has layered info been set? */ + unsigned int is_cloaked : 1; /* is the window cloaked by the app? */ unsigned int color_key; /* color key for a layered window */ unsigned int alpha; /* alpha value for a layered window */ unsigned int layered_flags; /* flags for a layered window */ @@ -503,6 +504,7 @@ static struct window *create_window( struct window *parent, struct window *owner win->is_unicode = 1; win->is_linked = 0; win->is_layered = 0; + win->is_cloaked = 0; win->dpi_awareness = DPI_AWARENESS_PER_MONITOR_AWARE; win->dpi = 0; win->user_data = 0; @@ -784,6 +786,7 @@ static int get_window_children_from_point( struct window *parent, int x, int y, { int x_child = x, y_child = y;
+ if (is_desktop_window( parent ) && ptr->is_cloaked) continue; if (!is_point_in_window( ptr, &x_child, &y_child, parent->dpi )) continue; /* skip it */
/* if point is in client area, and window is not minimized or disabled, check children */ @@ -856,6 +859,21 @@ static int all_windows_from_point( struct window *top, int x, int y, unsigned in return 1; }
+/* fill an array with the handles of all the owned windows, recursively */ +static unsigned int get_owned_windows( struct window *win, user_handle_t *handles ) +{ + struct window *ptr, *parent = win->parent; + unsigned int count = 0; + + LIST_FOR_EACH_ENTRY( ptr, &parent->children, struct window, entry ) + { + if (ptr->owner != win->handle) continue; + if (handles) handles[count] = ptr->handle; + count++; + count += get_owned_windows( ptr, handles ? handles + count : NULL ); + } + return count; +}
/* return the thread owning a window */ struct thread *get_window_thread( user_handle_t handle ) @@ -1965,14 +1983,16 @@ DECL_HANDLER(create_window) win->dpi_awareness = req->awareness; win->dpi = req->dpi; } + win->is_cloaked = owner ? owner->is_cloaked : 0;
- reply->handle = win->handle; - reply->parent = win->parent ? win->parent->handle : 0; - reply->owner = win->owner; - reply->extra = win->nb_extra_bytes; - reply->dpi = win->dpi; - reply->awareness = win->dpi_awareness; - reply->class_ptr = get_class_client_ptr( win->class ); + reply->handle = win->handle; + reply->parent = win->parent ? win->parent->handle : 0; + reply->owner = win->owner; + reply->extra = win->nb_extra_bytes; + reply->dpi = win->dpi; + reply->awareness = win->dpi_awareness; + reply->class_ptr = get_class_client_ptr( win->class ); + reply->needs_cloak = win->is_cloaked; }
@@ -2072,8 +2092,9 @@ DECL_HANDLER(set_window_owner) } }
- reply->prev_owner = win->owner; - reply->full_owner = win->owner = owner ? owner->handle : 0; + reply->prev_owner = win->owner; + reply->full_owner = win->owner = owner ? owner->handle : 0; + reply->needs_cloak = owner ? owner->is_cloaked : 0; }
@@ -2151,6 +2172,47 @@ DECL_HANDLER(set_window_info) }
+/* set the window's cloaked attribute */ +DECL_HANDLER(set_window_cloaked) +{ + struct window *win = get_window( req->handle ); + unsigned int i, total; + user_handle_t *data; + + if (!win) return; + if (is_desktop_window( win )) + { + set_error( STATUS_ACCESS_DENIED ); + return; + } + if (!is_desktop_window( win->parent )) + { + set_error( STATUS_INVALID_HANDLE ); + return; + } + + reply->count = total = get_owned_windows( win, NULL ); + if (get_reply_max_size() < total * sizeof(user_handle_t)) + return; + + if (total) + { + if (!(data = set_reply_data_size( total * sizeof(user_handle_t) ))) + { + set_error( STATUS_NO_MEMORY ); + return; + } + get_owned_windows( win, data ); + } + + win->is_cloaked = req->cloaked; + + for (i = 0; i < total; i++) + if ((win = get_window( data[i] ))) + win->is_cloaked = req->cloaked; +} + + /* get a list of the window parents, up to the root of the tree */ DECL_HANDLER(get_window_parents) {