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) {
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/user32/user32.spec | 1 + dlls/user32/win.c | 43 +++++++++++++++++++++++++++++++++++++++++ server/protocol.def | 8 ++++++++ server/window.c | 26 +++++++++++++++++++++++++ 4 files changed, 78 insertions(+)
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index 8051ae3..1d87065 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -400,6 +400,7 @@ @ stdcall GetUserObjectSecurity (long ptr ptr long ptr) # @ stub GetWinStationInfo @ stdcall GetWindow(long long) +@ stdcall GetWindowCompositionAttribute(ptr ptr) @ stdcall GetWindowContextHelpId(long) @ stdcall GetWindowDC(long) @ stdcall GetWindowDisplayAffinity(long ptr) diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 9840a76..6725c14 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -4247,6 +4247,49 @@ BOOL WINAPI SetWindowDisplayAffinity(HWND hwnd, DWORD affinity) return FALSE; }
+/********************************************************************** + * GetWindowCompositionAttribute (USER32.@) + */ +BOOL WINAPI GetWindowCompositionAttribute(HWND hwnd, const struct WINCOMPATTRDATA *data) +{ + TRACE("(%p, %p)\n", hwnd, data); + + if (!data || !data->pData) + { + SetLastError(ERROR_NOACCESS); + return FALSE; + } + if (!hwnd || is_broadcast(hwnd)) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + switch (data->attribute) + { + case WCA_CLOAKED: + if (data->dataSize < sizeof(DWORD)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + SERVER_START_REQ( get_window_cloaked ) + { + req->handle = wine_server_user_handle( hwnd ); + if (wine_server_call_err( req )) return FALSE; + *(DWORD*)(data->pData) = reply->cloaked; + } + SERVER_END_REQ; + break; + + 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; +} + /********************************************************************** * SetWindowCompositionAttribute (USER32.@) */ diff --git a/server/protocol.def b/server/protocol.def index d96d2e7..1bb4dcb 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2301,6 +2301,14 @@ enum message_type #define SET_WIN_UNICODE 0x40
+/* Get the window's cloaked attribute */ +@REQ(get_window_cloaked) + user_handle_t handle; /* handle to the window */ +@REPLY + unsigned int cloaked; /* cloaked attribute (DMW_CLOAKED_*) */ +@END + + /* Set the window's cloaked attribute */ @REQ(set_window_cloaked) user_handle_t handle; /* handle to the window */ diff --git a/server/window.c b/server/window.c index e6fc90c..356995a 100644 --- a/server/window.c +++ b/server/window.c @@ -38,6 +38,7 @@ #include "process.h" #include "user.h" #include "unicode.h" +#include "dwmapi.h"
/* a window property */ struct property @@ -2172,6 +2173,31 @@ DECL_HANDLER(set_window_info) }
+/* get the window's cloaked attribute as DWM_CLOAKED_* value */ +DECL_HANDLER(get_window_cloaked) +{ + struct window *win = get_window( req->handle ); + unsigned int cloaked = 0; + + if (!win) + { + set_error( STATUS_INVALID_HANDLE ); + return; + } + if (!is_desktop_window( win->parent )) + { + while (!is_desktop_window( win->parent )) + win = win->parent; + cloaked |= DWM_CLOAKED_INHERITED; + } + + if (win->is_cloaked) cloaked |= DWM_CLOAKED_APP; + else cloaked = 0; + + reply->cloaked = cloaked; +} + + /* set the window's cloaked attribute */ DECL_HANDLER(set_window_cloaked) {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=81536
Your paranoid android.
=== debiant (32 bit report) ===
user32: monitor: Timeout
=== debiant (32 bit Chinese:China report) ===
user32: monitor: Timeout
=== debiant (32 bit WoW report) ===
user32: monitor: Timeout
=== debiant (64 bit WoW report) ===
user32: win.c:10149: Test failed: Expected foreground window 000E013E, got 00E10102 win.c:10152: Test failed: Received WM_ACTIVATEAPP(0), did not expect it. win.c:10159: Test failed: Expected foreground window 000E013E, got 00000000 win.c:10169: Test failed: Received WM_ACTIVATEAPP(1), did not expect it.
Cloaking is implemented by setting the opacity of a window to zero, and setting an empty shape using libXshape. This makes it transparent to mouse input and completely invisible, while otherwise still processing the exact same messages/events as a visible window would, to match what Windows does.
While a cloaked window still has a taskbar visible, it also has the same traits as a normal window. For example, a minimized cloaked window will show the "Restore" or "Unminimize" function when right-clicking on its taskbar icon. So that means unmapping the window would *not* be the same behavior as Windows, since it even removes the taskbar item in the first place.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
The reason the GetWindow( hwnd, GW_OWNER ) check is done in this patch, rather than in user32, is because it must not be checked for shell-cloaked windows (last patch in the series). The shell has full control over what windows it hides and doesn't abide by that, at least on Wine, because of how virtual desktops work.
dlls/winex11.drv/window.c | 106 ++++++++++++++++++++++++++++-- dlls/winex11.drv/winex11.drv.spec | 1 + dlls/winex11.drv/x11drv.h | 41 ++++++++++++ 3 files changed, 141 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 4571739..3e4f9eb 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -44,6 +44,7 @@ #include "wine/unicode.h"
#include "x11drv.h" +#include "dwmapi.h" #include "wine/debug.h" #include "wine/server.h" #include "mwm.h" @@ -390,7 +391,7 @@ static void sync_window_region( struct x11drv_win_data *data, HRGN win_region ) if (!data->whole_window) return; data->shaped = FALSE;
- if (IsRectEmpty( &data->window_rect )) /* set an empty shape */ + if (IsRectEmpty( &data->window_rect ) || data->cloaked) /* set an empty shape */ { static XRectangle empty_rect; XShapeCombineRectangles( data->display, data->whole_window, ShapeBounding, 0, 0, @@ -1581,7 +1582,13 @@ static void create_whole_window( struct x11drv_win_data *data ) if (win_rgn || IsRectEmpty( &data->window_rect )) sync_window_region( data, win_rgn );
/* set the window opacity */ - if (!GetLayeredWindowAttributes( data->hwnd, &key, &alpha, &layered_flags )) layered_flags = 0; + if (data->cloaked) + { + key = 0; + alpha = 0; + layered_flags = LWA_ALPHA; + } + else if (!GetLayeredWindowAttributes( data->hwnd, &key, &alpha, &layered_flags )) layered_flags = 0; sync_window_opacity( data->display, data->whole_window, key, alpha, layered_flags );
XFlush( data->display ); /* make sure the window exists before we start painting to it */ @@ -1714,7 +1721,7 @@ void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) { data->layered = FALSE; set_window_visual( data, &default_visual, FALSE ); - sync_window_opacity( data->display, data->whole_window, 0, 0, 0 ); + if (!data->cloaked) sync_window_opacity( data->display, data->whole_window, 0, 0, 0 ); if (data->surface) set_surface_color_key( data->surface, CLR_INVALID ); } done: @@ -2572,6 +2579,80 @@ done: }
+/********************************************************************** + * SetWindowCompositionAttribute (X11DRV.@) + */ +DWORD CDECL X11DRV_SetWindowCompositionAttribute( HWND hwnd, DWORD attribute, void *attr_data ) +{ + struct x11drv_win_data *data; + DWORD ret = 0; + HWND owner; + + switch (attribute) + { + case WCA_CLOAK: + if (!(data = get_win_data( hwnd ))) + { + SetLastError( ERROR_INVALID_HANDLE ); + return ~0; + } + ret = *(BOOL*)attr_data ? SET_WINDOW_CLOAKED_ON : 0; + + /* If the owner is cloaked, manual uncloaking is not allowed */ + if (!ret && (owner = GetWindow( hwnd, GW_OWNER ))) + { + struct x11drv_win_data *owner_data = get_win_data( owner ); + DWORD cloaked = 0; + if (owner_data) + { + cloaked = owner_data->cloaked ? DWM_CLOAKED_APP : 0; + release_win_data( owner_data ); + } + else + { + SERVER_START_REQ( get_window_cloaked ) + { + req->handle = wine_server_user_handle( owner ); + if (!wine_server_call( req )) cloaked = reply->cloaked; + } + SERVER_END_REQ; + } + if (cloaked) + { + release_win_data( data ); + SetLastError( ERROR_INVALID_PARAMETER ); + return ~0; + } + } + + if (!data->cloaked != !ret) + { +#ifdef HAVE_LIBXSHAPE + DWORD layered_flags = LWA_ALPHA; + COLORREF key = 0; + BYTE alpha = 0; + + data->cloaked = ret; + if (!ret && !GetLayeredWindowAttributes( hwnd, &key, &alpha, &layered_flags )) + layered_flags = 0; + + sync_window_opacity( data->display, data->whole_window, key, alpha, layered_flags ); + sync_window_region( data, (HRGN)1 ); +#else + FIXME("libXshape is not available, but cloaking requires it.\n"); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + ret = ~0; +#endif + } + release_win_data( data ); + break; + default: + break; + } + return ret; +} + + /********************************************************************** * SetWindowIcon (X11DRV.@) * @@ -2631,7 +2712,7 @@ void CDECL X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alph { set_window_visual( data, &default_visual, FALSE );
- if (data->whole_window) + if (data->whole_window && !data->cloaked) sync_window_opacity( data->display, data->whole_window, key, alpha, flags ); if (data->surface) set_surface_color_key( data->surface, (flags & LWA_COLORKEY) ? key : CLR_INVALID ); @@ -2656,9 +2737,20 @@ void CDECL X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alph Window win = X11DRV_get_whole_window( hwnd ); if (win) { - sync_window_opacity( gdi_display, win, key, alpha, flags ); - if (flags & LWA_COLORKEY) - FIXME( "LWA_COLORKEY not supported on foreign process window %p\n", hwnd ); + DWORD cloaked = 0; + + SERVER_START_REQ( get_window_cloaked ) + { + req->handle = wine_server_user_handle( hwnd ); + if (!wine_server_call( req )) cloaked = reply->cloaked; + } + SERVER_END_REQ; + if (!cloaked) + { + sync_window_opacity( gdi_display, win, key, alpha, flags ); + if (flags & LWA_COLORKEY) + FIXME( "LWA_COLORKEY not supported on foreign process window %p\n", hwnd ); + } } } } diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index c0e24d8..3d8c267 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -33,6 +33,7 @@ @ cdecl SetFocus(long) X11DRV_SetFocus @ cdecl SetLayeredWindowAttributes(long long long long) X11DRV_SetLayeredWindowAttributes @ cdecl SetParent(long long long) X11DRV_SetParent +@ cdecl SetWindowCompositionAttribute(long long ptr) X11DRV_SetWindowCompositionAttribute @ cdecl SetWindowIcon(long long long) X11DRV_SetWindowIcon @ cdecl SetWindowRgn(long long long) X11DRV_SetWindowRgn @ cdecl SetWindowStyle(ptr long ptr) X11DRV_SetWindowStyle diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 173d94b..d62fcfd 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -570,6 +570,7 @@ struct x11drv_win_data BOOL managed : 1; /* is window managed? */ BOOL mapped : 1; /* is window mapped? (in either normal or iconic state) */ BOOL iconic : 1; /* is window in iconic state? */ + BOOL cloaked : 1; /* is window cloaked manually? */ BOOL embedded : 1; /* is window an XEMBED client? */ BOOL shaped : 1; /* is window using a custom region shape? */ BOOL layered : 1; /* is window layered and with valid attributes? */ @@ -824,4 +825,44 @@ static inline BOOL is_window_rect_mapped( const RECT *rect ) max( rect->bottom, rect->top + 1 ) > virtual_rect.top); }
+/* 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 +}; +BOOL WINAPI SetWindowCompositionAttribute(HWND, const struct WINCOMPATTRDATA*); + #endif /* __WINE_X11DRV_H */
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=81537
Your paranoid android.
=== debiant (32 bit report) ===
user32: monitor: Timeout
=== debiant (32 bit Chinese:China report) ===
user32: monitor: Timeout
=== debiant (32 bit WoW report) ===
user32: menu.c:2337: Test failed: test 25 monitor: Timeout win.c:10149: Test failed: Expected foreground window 000E013E, got 00E10102
=== debiant (64 bit WoW report) ===
user32: monitor: Timeout
While manually cloaked windows can be set to foreground, they shouldn't be considered as activation candidates when trying to find a window to activate (after hiding a window for example).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/user32/winpos.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/winpos.c b/dlls/user32/winpos.c index 9e5a0c2..24f6208 100644 --- a/dlls/user32/winpos.c +++ b/dlls/user32/winpos.c @@ -1521,13 +1521,21 @@ void WINAPI SetInternalWindowPos( HWND hwnd, UINT showCmd, */ static BOOL can_activate_window( HWND hwnd ) { + DWORD cloaked = 0; LONG style;
if (!hwnd) return FALSE; style = GetWindowLongW( hwnd, GWL_STYLE ); if (!(style & WS_VISIBLE)) return FALSE; if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE; - return !(style & WS_DISABLED); + if (style & WS_DISABLED) return FALSE; + SERVER_START_REQ( get_window_cloaked ) + { + req->handle = wine_server_user_handle( hwnd ); + if (!wine_server_call( req )) cloaked = reply->cloaked; + } + SERVER_END_REQ; + return !cloaked; }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=81538
Your paranoid android.
=== debiant (32 bit report) ===
user32: monitor: Timeout
=== debiant (32 bit Chinese:China report) ===
user32: monitor: Timeout
=== debiant (32 bit WoW report) ===
user32: monitor: Timeout
=== debiant (64 bit WoW report) ===
user32: monitor: Timeout
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/user32/tests/win.c | 595 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 595 insertions(+)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 843da89..2bfde85 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -31,6 +31,7 @@ #include "wingdi.h" #include "winuser.h" #include "winreg.h" +#include "dwmapi.h"
#include "wine/test.h"
@@ -45,10 +46,51 @@ #define LONG_PTR INT_PTR #define ULONG_PTR UINT_PTR
+/* 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 +}; + void dump_region(HRGN hrgn);
static BOOL (WINAPI *pGetWindowInfo)(HWND,WINDOWINFO*); static UINT (WINAPI *pGetWindowModuleFileNameA)(HWND,LPSTR,UINT); +static BOOL (WINAPI *pGetWindowCompositionAttribute)(HWND, struct WINCOMPATTRDATA*); +static BOOL (WINAPI *pSetWindowCompositionAttribute)(HWND, struct WINCOMPATTRDATA*); static BOOL (WINAPI *pGetLayeredWindowAttributes)(HWND,COLORREF*,BYTE*,DWORD*); static BOOL (WINAPI *pSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD); static BOOL (WINAPI *pUpdateLayeredWindow)(HWND,HDC,POINT*,SIZE*,HDC,POINT*,COLORREF,BLENDFUNCTION*,DWORD); @@ -8269,6 +8311,551 @@ static void test_layered_window(void) DeleteObject( hbm ); }
+static void cloaked_attribute_proc(HWND hwndOtherProc) +{ + HWND hwnd; + MSG msg; + + hwnd = CreateWindowExA(0, "static", "static", WS_OVERLAPPED | WS_VISIBLE, + 0, 0, 100, 100, hwndOtherProc, 0, NULL, NULL); + ok(hwnd != 0, "CreateWindowEx failed\n"); + + if (!PostMessageA(hwndOtherProc, WM_APP, 0, (LPARAM)hwnd)) + { + ok(0, "PostMessage failed, error %u\n", GetLastError()); + DestroyWindow(hwnd); + return; + } + + while (GetMessageA(&msg, NULL, 0, 0) > 0 && (msg.hwnd != hwnd || msg.message != WM_APP)) + DispatchMessageA(&msg); + ok(msg.hwnd == hwnd && msg.message == WM_APP, "didn't get WM_APP message\n"); + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, 0); + ok(PostMessageA(hwndOtherProc, WM_APP, 0, 0), "PostMessage failed, error %u\n", GetLastError()); + + while (GetMessageA(&msg, NULL, 0, 0) > 0 && (msg.hwnd != hwnd || msg.message != WM_APP)) + DispatchMessageW(&msg); + DestroyWindow(hwnd); + ok(PostMessageA(hwndOtherProc, WM_APP, 0, 0), "PostMessage failed, error %u\n", GetLastError()); +} + +static void test_cloaked_attribute(const char *argv0) +{ + struct WINCOMPATTRDATA data, set_data; + HWND hwnd, hwnd2, hwndOwner; + PROCESS_INFORMATION info; + BOOL ret, cloak = FALSE; + DWORD cloaked, version; + STARTUPINFOA startup; + char cmd[MAX_PATH]; + RECT rect; + POINT pt; + MSG msg; + + if (!pGetWindowCompositionAttribute || !pSetWindowCompositionAttribute) + { + win_skip("window composition APIs not supported\n"); + return; + } + + version = GetVersion(); + if (broken(LOBYTE(LOWORD(version)) < 6 || (LOBYTE(LOWORD(version)) == 6 && HIBYTE(LOWORD(version)) < 2))) + { + win_skip("cloak attribute is not supported\n"); + return; + } + + hwnd = CreateWindowA("MainWindowClass", NULL, WS_OVERLAPPED | WS_VISIBLE, + 200, 200, 200, 200, 0, 0, 0, NULL); + assert(hwnd); + + SetLastError(0xdeadbeef); + ret = pGetWindowCompositionAttribute(hwnd, NULL); + ok(!ret, "GetWindowCompositionAttribute should fail with NULL pointer\n"); + ok(GetLastError() == ERROR_NOACCESS, "expected ERROR_NOACCESS, got %u\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd, NULL); + ok(!ret, "SetWindowCompositionAttribute should fail with NULL pointer\n"); + ok(GetLastError() == ERROR_NOACCESS, "expected ERROR_NOACCESS, got %u\n", GetLastError()); + + data.attribute = WCA_CLOAKED; + data.pData = NULL; + data.dataSize = sizeof(cloaked); + SetLastError(0xdeadbeef); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(!ret, "GetWindowCompositionAttribute should fail with NULL pData\n"); + ok(GetLastError() == ERROR_NOACCESS, "expected ERROR_NOACCESS, got %u\n", GetLastError()); + set_data.attribute = WCA_CLOAK; + set_data.pData = NULL; + set_data.dataSize = sizeof(cloak); + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(!ret, "SetWindowCompositionAttribute should fail with NULL pData\n"); + ok(GetLastError() == ERROR_NOACCESS, "expected ERROR_NOACCESS, got %u\n", GetLastError()); + + data.pData = &cloaked; + data.dataSize = sizeof(cloaked) - 1; + SetLastError(0xdeadbeef); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(!ret, "GetWindowCompositionAttribute should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "expected ERROR_INSUFFICIENT_BUFFER, got %u\n", GetLastError()); + set_data.pData = &cloak; + set_data.dataSize = sizeof(cloak) - 1; + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(!ret, "SetWindowCompositionAttribute should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "expected ERROR_INSUFFICIENT_BUFFER, got %u\n", GetLastError()); + + data.dataSize = sizeof(cloaked); + SetLastError(0xdeadbeef); + ret = pGetWindowCompositionAttribute(NULL, &data); + ok(!ret, "GetWindowCompositionAttribute should have failed with NULL hwnd\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + set_data.dataSize = sizeof(cloak); + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(NULL, &set_data); + ok(!ret, "SetWindowCompositionAttribute should have failed with NULL hwnd\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = pGetWindowCompositionAttribute(HWND_BROADCAST, &data); + ok(!ret, "GetWindowCompositionAttribute should have failed with broadcast hwnd\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = pGetWindowCompositionAttribute(HWND_TOPMOST, &data); + ok(!ret, "GetWindowCompositionAttribute should have failed with broadcast hwnd\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(HWND_BROADCAST, &set_data); + ok(!ret, "SetWindowCompositionAttribute should have failed with broadcast hwnd\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(HWND_TOPMOST, &set_data); + ok(!ret, "SetWindowCompositionAttribute should have failed with broadcast hwnd\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + + data.dataSize = sizeof(cloaked) + 1; + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + data.dataSize = sizeof(cloaked); + + set_data.dataSize = sizeof(cloak) + 1; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + set_data.dataSize = sizeof(cloak); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + /* cloaking and visibility are unrelated */ + ret = ShowWindow(hwnd, SW_HIDE); + ok(ret, "ShowWindow returned %u\n", ret); + ok(!(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE), "window has the WS_VISIBLE style\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ok(GetActiveWindow() == hwndMain, "wrong active window\n"); + + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + ok(GetActiveWindow() == hwndMain, "wrong active window\n"); + + ret = ShowWindow(hwnd, SW_SHOW); + ok(!ret, "ShowWindow returned %u\n", ret); + ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE, "window does not have the WS_VISIBLE style\n"); + ok(GetActiveWindow() == hwnd, "wrong active window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + /* cloaking and minimization are unrelated */ + ret = ShowWindow(hwnd, SW_MINIMIZE); + ok(ret, "ShowWindow returned %u\n", ret); + ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_MINIMIZE, "window does not have the WS_MINIMIZE style\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_MINIMIZE, "window does not have the WS_MINIMIZE style\n"); + + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_MINIMIZE, "window does not have the WS_MINIMIZE style\n"); + + ret = ShowWindow(hwnd, SW_RESTORE); + ok(ret, "ShowWindow returned %u\n", ret); + ok(!(GetWindowLongA(hwnd, GWL_STYLE) & WS_MINIMIZE), "window has the WS_MINIMIZE style\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + /* cloaked windows can be activated and have valid rects */ + GetWindowRect(hwnd, &rect); + ok(rect.left == 200 && rect.top == 200 && rect.right == 400 && rect.bottom == 400, + "wrong window rect %s\n", wine_dbgstr_rect(&rect)); + ok(SetForegroundWindow(hwndMain), "SetForegroundWindow failed\n"); + ok(GetActiveWindow() == hwndMain, "wrong active window\n"); + ok(SetForegroundWindow(hwnd), "SetForegroundWindow failed\n"); + ok(GetActiveWindow() == hwnd, "wrong active window\n"); + + /* cloaked windows can't be retrieved via WindowFromPoint */ + pt.x = pt.y = 350; + hwnd2 = WindowFromPoint(pt); + ok(hwnd2 != hwnd, "cloaked window returned from WindowFromPoint\n"); + pt.x = pt.y = 250; + hwnd2 = WindowFromPoint(pt); + ok(hwnd2 == hwndMain, "unexpected window returned %p (expected %p)\n", hwnd2, hwndMain); + pt.x = pt.y = 10; + hwnd2 = ChildWindowFromPointEx(hwnd, pt, CWP_SKIPDISABLED | CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT); + ok(hwnd2 == hwnd, "unexpected window returned %p (expected %p)\n", hwnd2, hwnd); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + pt.x = pt.y = 350; + hwnd2 = WindowFromPoint(pt); + ok(hwnd2 == hwnd, "unexpected window returned %p (expected %p)\n", hwnd2, hwnd); + + /* child windows can't be cloaked, but remember their cloaked status */ + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + hwndOwner = CreateWindowA("MainWindowClass", NULL, WS_OVERLAPPED | WS_VISIBLE, + 100, 100, 300, 300, 0, 0, 0, NULL); + assert(hwndOwner); + + ok(SetParent(hwnd, hwndOwner) == GetDesktopWindow(), "SetParent failed, error %u\n", GetLastError()); + ok(GetAncestor(hwnd, GA_PARENT) == hwndOwner, "unexpected parent window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + ok(SetParent(hwnd, NULL) == hwndOwner, "SetParent failed, error %u\n", GetLastError()); + ok(GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(), "unexpected parent window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + ok(SetParent(hwnd, hwndOwner) == GetDesktopWindow(), "SetParent failed, error %u\n", GetLastError()); + ok(GetAncestor(hwnd, GA_PARENT) == hwndOwner, "unexpected parent window\n"); + cloak = TRUE; + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(!ret, "SetWindowCompositionAttribute should have failed on a child window\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ok(SetParent(hwnd, NULL) == hwndOwner, "SetParent failed, error %u\n", GetLastError()); + ok(GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(), "unexpected parent window\n"); + + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + ok(SetParent(hwnd, hwndOwner) == GetDesktopWindow(), "SetParent failed, error %u\n", GetLastError()); + ok(GetAncestor(hwnd, GA_PARENT) == hwndOwner, "unexpected parent window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == (DWM_CLOAKED_APP | DWM_CLOAKED_INHERITED), + "window was not cloaked as DWM_CLOAKED_APP | DWM_CLOAKED_INHERITED, got %u\n", cloaked); + pt.x = pt.y = 205; + hwnd2 = ChildWindowFromPointEx(hwndOwner, pt, CWP_SKIPDISABLED | CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT); + ok(hwnd2 == hwnd, "unexpected window returned %p (expected %p)\n", hwnd2, hwnd); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + ok(SetParent(hwnd, NULL) == hwndOwner, "SetParent failed, error %u\n", GetLastError()); + ok(GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(), "unexpected parent window\n"); + + /* owned windows inherit the cloaked status from the owner */ + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndOwner); + ok(GetWindow(hwnd, GW_OWNER) == hwndOwner, "unexpected owner window\n"); + + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + /* owned windows can't be uncloaked if their owner is cloaked, but they can be cloaked */ + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + cloak = FALSE; + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(!ret, "SetWindowCompositionAttribute should have failed on an owned window\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "SetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + /* owned windows remain cloaked after inheriting it, even if they get disowned */ + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwndOwner, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, 0); + ok(GetWindow(hwnd, GW_OWNER) == 0, "unexpected owner window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + /* owned windows inherit the cloak attribute when setting new owner */ + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndOwner); + ok(GetWindow(hwnd, GW_OWNER) == hwndOwner, "unexpected owner window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, 0); + ok(GetWindow(hwnd, GW_OWNER) == 0, "unexpected owner window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + /* DWM_CLOAKED_INHERITED is not used even if the window is created as owned */ + DestroyWindow(hwnd); + ret = pGetWindowCompositionAttribute(hwndOwner, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + hwnd = CreateWindowA("MainWindowClass", NULL, WS_POPUP | WS_VISIBLE, + 200, 200, 200, 200, hwndOwner, 0, 0, NULL); + assert(hwnd); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, 0); + ok(GetWindow(hwnd, GW_OWNER) == 0, "unexpected owner window\n"); + DestroyWindow(hwndOwner); + + /* windows belonging to another process can't be cloaked directly */ + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + sprintf(cmd, "%s win test_cloaked_attribute %p\n", argv0, hwnd); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + ok(CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, + &startup, &info), "CreateProcess failed.\n"); + CloseHandle(info.hThread); + + while (GetMessageA(&msg, NULL, 0, 0) > 0 && (msg.hwnd != hwnd || msg.message != WM_APP)) + DispatchMessageA(&msg); + ok(msg.hwnd == hwnd && msg.message == WM_APP, "didn't get WM_APP message with hwnd\n"); + hwnd2 = (HWND)msg.lParam; + flush_events(TRUE); + + ok(GetWindow(hwnd2, GW_OWNER) == hwnd, "unexpected owner window\n"); + ret = pGetWindowCompositionAttribute(hwnd2, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + cloak = TRUE; + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd2, &set_data); + ok(!ret, "SetWindowCompositionAttribute should fail with NULL pData\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %u\n", GetLastError()); + + /* but they can be cloaked by inheriting the owner's cloaked status */ + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd2, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + ok(PostMessageA(hwnd2, WM_APP, 0, 0), "PostMessage failed, error %u\n", GetLastError()); + while (GetMessageA(&msg, NULL, 0, 0) > 0 && (msg.hwnd != hwnd || msg.message != WM_APP)) + DispatchMessageA(&msg); + ok(msg.hwnd == hwnd && msg.message == WM_APP, "didn't get WM_APP message\n"); + flush_events(TRUE); + ok(GetWindow(hwnd2, GW_OWNER) == 0, "unexpected owner window\n"); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd2, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd2, &set_data); + ok(!ret, "SetWindowCompositionAttribute should fail with NULL pData\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %u\n", GetLastError()); + + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwnd2); + ok(GetWindow(hwnd, GW_OWNER) == hwnd2, "unexpected owner window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + cloak = FALSE; + SetLastError(0xdeadbeef); + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(!ret, "SetWindowCompositionAttribute should have failed on an owned window\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + + SetWindowLongPtrA(hwnd, GWLP_HWNDPARENT, 0); + ok(GetWindow(hwnd, GW_OWNER) == 0, "unexpected owner window\n"); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + /* child windows behave identically whether they're on different process or not */ + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ok(SetParent(hwnd2, hwnd) == GetDesktopWindow(), "SetParent failed, error %u\n", GetLastError()); + flush_events(TRUE); + ok(GetAncestor(hwnd2, GA_PARENT) == hwnd, "unexpected parent window\n"); + ret = pGetWindowCompositionAttribute(hwnd2, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + cloak = TRUE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd2, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == (DWM_CLOAKED_APP | DWM_CLOAKED_INHERITED), + "window was not cloaked as DWM_CLOAKED_APP | DWM_CLOAKED_INHERITED, got %u\n", cloaked); + + cloak = FALSE; + ret = pSetWindowCompositionAttribute(hwnd, &set_data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ret = pGetWindowCompositionAttribute(hwnd, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + ret = pGetWindowCompositionAttribute(hwnd2, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(!cloaked, "window was not supposed to be cloaked: %u\n", cloaked); + + ok(SetParent(hwnd2, NULL) == hwnd, "SetParent failed, error %u\n", GetLastError()); + flush_events(TRUE); + ok(GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(), "unexpected parent window\n"); + ret = pGetWindowCompositionAttribute(hwnd2, &data); + ok(ret, "GetWindowCompositionAttribute failed, error %u\n", GetLastError()); + ok(cloaked == DWM_CLOAKED_APP, "window was not cloaked as DWM_CLOAKED_APP, got %u\n", cloaked); + + ok(PostMessageA(hwnd2, WM_APP, 0, 0), "PostMessage failed, error %u\n", GetLastError()); + while (GetMessageA(&msg, NULL, 0, 0) > 0 && (msg.hwnd != hwnd || msg.message != WM_APP)) + DispatchMessageA(&msg); + ok(msg.hwnd == hwnd && msg.message == WM_APP, "didn't get WM_APP message\n"); + flush_events(TRUE); + wait_child_process(info.hProcess); + CloseHandle(info.hProcess); + + DestroyWindow(hwnd); +} + static MONITORINFO mi;
static LRESULT CALLBACK fullscreen_wnd_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) @@ -11904,6 +12491,8 @@ START_TEST(win) HMODULE gdi32 = GetModuleHandleA("gdi32.dll"); pGetWindowInfo = (void *)GetProcAddress( user32, "GetWindowInfo" ); pGetWindowModuleFileNameA = (void *)GetProcAddress( user32, "GetWindowModuleFileNameA" ); + pGetWindowCompositionAttribute = (void *)GetProcAddress( user32, "GetWindowCompositionAttribute" ); + pSetWindowCompositionAttribute = (void *)GetProcAddress( user32, "SetWindowCompositionAttribute" ); pGetLayeredWindowAttributes = (void *)GetProcAddress( user32, "GetLayeredWindowAttributes" ); pSetLayeredWindowAttributes = (void *)GetProcAddress( user32, "SetLayeredWindowAttributes" ); pUpdateLayeredWindow = (void *)GetProcAddress( user32, "UpdateLayeredWindow" ); @@ -11935,6 +12524,11 @@ START_TEST(win) other_process_proc(hwnd); return; } + else if (!strcmp(argv[2], "test_cloaked_attribute")) + { + cloaked_attribute_proc(hwnd); + return; + } }
if (argc == 3 && !strcmp(argv[2], "winproc_limit")) @@ -12039,6 +12633,7 @@ START_TEST(win) test_GetUpdateRect(); test_Expose(); test_layered_window(); + test_cloaked_attribute(argv[0]);
test_SetForegroundWindow(hwndMain); test_handles( hwndMain );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=81539
Your paranoid android.
=== w10pro64_ar (32 bit report) ===
user32: win.c:3796: Test failed: message 0200 available
=== w10pro64_he (32 bit report) ===
user32: win.c:10065: Test failed: Timed out waiting for the child process
=== w10pro64_zh_CN (32 bit report) ===
user32: win.c:10050: Test failed: didn't get start_event win.c:10065: Test failed: Timed out waiting for the child process win.c:9991: Test failed: transparent window didn't get WM_NCHITTEST message win.c:9992: Test failed: button under static window didn't get WM_LBUTTONUP
=== debiant (32 bit report) ===
user32: monitor: Timeout
=== debiant (32 bit Chinese:China report) ===
user32: monitor: Timeout
=== debiant (32 bit WoW report) ===
user32: monitor: Timeout
=== debiant (64 bit WoW report) ===
user32: monitor: Timeout win.c:10753: Test failed: Expected foreground window 000E013E, got 00000000
When a window is on a virtual desktop, the WM iconifies it without marking it "hidden". We use that to mark it as "shell cloaked" instead of minimizing the window, to match what Windows does when switching virtual desktops. This fixes applications which do things when minimized, since they are *not* minimized on Windows when switching desktops, unlike on Wine currently.
When on a different desktop, GetWindowCompositionAttribute always reports DWM_CLOAKED_SHELL. The shell cloaking the window (when on a different desktop) is tracked independently of the manual cloaking done by an app. It always cloaks it when on a different desktop, even if the app asks the window to be uncloaked. However, when going back to the original desktop, the window will be uncloaked (because the app uncloaked it while off-desktop). So even if DWM_CLOAKED_SHELL is reported, it's possible that the app is still manually cloaked underneath, with no way to know until you switch back to the desktop containing it and calling the API again.
Windows on a different desktop (i.e. cloaked by the shell) can't be set to the foreground. It simply fails. If some item on the desktop is selected and has focus, then switching desktops keeps it selected and no window gets activated on a desktop switch, because the desktop items are common between desktops.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43691 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/winex11.drv/event.c | 47 +++++++++++++++++++++++++++++++--- dlls/winex11.drv/window.c | 13 ++++++++-- dlls/winex11.drv/x11drv.h | 4 +++ dlls/winex11.drv/x11drv_main.c | 1 + server/protocol.def | 1 + server/window.c | 16 +++++++++--- 6 files changed, 74 insertions(+), 8 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index d21d2a7..e27412c 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -549,15 +549,20 @@ DWORD EVENT_x11_time_to_win32_time(Time time) static inline BOOL can_activate_window( HWND hwnd ) { LONG style = GetWindowLongW( hwnd, GWL_STYLE ); + struct x11drv_win_data *data; + BOOL off_desktop; RECT rect;
if (!(style & WS_VISIBLE)) return FALSE; if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE; - if (style & WS_MINIMIZE) return FALSE; + if (style & (WS_MINIMIZE | WS_DISABLED)) return FALSE; if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE; if (hwnd == GetDesktopWindow()) return FALSE; if (GetWindowRect( hwnd, &rect ) && IsRectEmpty( &rect )) return FALSE; - return !(style & WS_DISABLED); + if (!(data = get_win_data( hwnd ))) return FALSE; + off_desktop = data->off_desktop; + release_win_data( data ); + return !off_desktop; }
@@ -1096,7 +1101,7 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
if (!hwnd) return FALSE; if (!(data = get_win_data( hwnd ))) return FALSE; - if (!data->mapped || data->iconic) goto done; + if (!data->mapped || data->iconic || data->off_desktop) goto done; if (data->whole_window && !data->managed) goto done; /* ignore synthetic events on foreign windows */ if (event->send_event && !data->whole_window) goto done; @@ -1325,6 +1330,26 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat } else if (!data->iconic && data->wm_state == IconicState) { + /* Check if the window is on another desktop rather than actually minimized */ + read_net_wm_states( event->display, data ); + if (!(data->net_wm_state & (1 << NET_WM_STATE_HIDDEN))) + { + if (!data->off_desktop) + { + struct WINCOMPATTRDATA attr; + BOOL cloak = TRUE; + + attr.attribute = WCA_CLOAK; + attr.pData = &cloak; + attr.dataSize = sizeof(cloak); + data->shell_cloak = TRUE; + SetWindowCompositionAttribute( hwnd, &attr ); + data->shell_cloak = FALSE; + data->off_desktop = TRUE; + } + goto done; + } + data->iconic = TRUE; if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED)) { @@ -1335,6 +1360,22 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL updat } TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style ); } + else if (!data->iconic && data->wm_state == NormalState) + { + if (data->off_desktop) + { + struct WINCOMPATTRDATA attr; + BOOL cloak = FALSE; + + attr.attribute = WCA_CLOAK; + attr.pData = &cloak; + attr.dataSize = sizeof(cloak); + data->shell_cloak = TRUE; + SetWindowCompositionAttribute( hwnd, &attr ); + data->shell_cloak = FALSE; + data->off_desktop = FALSE; + } + } done: release_win_data( data ); } diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 3e4f9eb..0d829e1 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -71,6 +71,7 @@ static const unsigned int net_wm_state_atoms[NB_NET_WM_STATES] = { XATOM__NET_WM_STATE_FULLSCREEN, XATOM__NET_WM_STATE_ABOVE, + XATOM__NET_WM_STATE_HIDDEN, XATOM__NET_WM_STATE_MAXIMIZED_VERT, XATOM__NET_WM_STATE_SKIP_PAGER, XATOM__NET_WM_STATE_SKIP_TASKBAR @@ -1263,7 +1264,7 @@ static void sync_window_position( struct x11drv_win_data *data, XWindowChanges changes; unsigned int mask = 0;
- if (data->managed && data->iconic) return; + if (data->managed && (data->iconic || data->off_desktop)) return;
/* resizing a managed maximized window is not allowed */ if (!(style & WS_MAXIMIZE) || !data->managed) @@ -2552,7 +2553,7 @@ UINT CDECL X11DRV_ShowWindow( HWND hwnd, INT cmd, RECT *rect, UINT swp ) } goto done; } - if (!data->managed || !data->mapped || data->iconic) goto done; + if (!data->managed || !data->mapped || data->iconic || data->off_desktop) goto done;
/* only fetch the new rectangle if the ShowWindow was a result of a window manager event */
@@ -2598,6 +2599,14 @@ DWORD CDECL X11DRV_SetWindowCompositionAttribute( HWND hwnd, DWORD attribute, vo } ret = *(BOOL*)attr_data ? SET_WINDOW_CLOAKED_ON : 0;
+ if (data->shell_cloak) + { + ret |= SET_WINDOW_CLOAKED_SHELL; + data->shell_cloak = FALSE; + release_win_data( data ); + break; + } + /* If the owner is cloaked, manual uncloaking is not allowed */ if (!ret && (owner = GetWindow( hwnd, GW_OWNER ))) { diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index d62fcfd..bf9000f 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -452,6 +452,7 @@ enum x11drv_atoms XATOM__NET_WM_STATE_ABOVE, XATOM__NET_WM_STATE_DEMANDS_ATTENTION, XATOM__NET_WM_STATE_FULLSCREEN, + XATOM__NET_WM_STATE_HIDDEN, XATOM__NET_WM_STATE_MAXIMIZED_HORZ, XATOM__NET_WM_STATE_MAXIMIZED_VERT, XATOM__NET_WM_STATE_SKIP_PAGER, @@ -547,6 +548,7 @@ enum x11drv_net_wm_state { NET_WM_STATE_FULLSCREEN, NET_WM_STATE_ABOVE, + NET_WM_STATE_HIDDEN, NET_WM_STATE_MAXIMIZED, NET_WM_STATE_SKIP_PAGER, NET_WM_STATE_SKIP_TASKBAR, @@ -575,6 +577,8 @@ struct x11drv_win_data BOOL shaped : 1; /* is window using a custom region shape? */ BOOL layered : 1; /* is window layered and with valid attributes? */ BOOL use_alpha : 1; /* does window use an alpha channel? */ + BOOL off_desktop : 1;/* is window on another WM desktop? */ + BOOL shell_cloak : 1;/* the shell is (un)cloaking the window */ int wm_state; /* current value of the WM_STATE property */ DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */ Window embedder; /* window id of embedder */ diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 9ec4c7a..639a291 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -165,6 +165,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_DEMANDS_ATTENTION", "_NET_WM_STATE_FULLSCREEN", + "_NET_WM_STATE_HIDDEN", "_NET_WM_STATE_MAXIMIZED_HORZ", "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_SKIP_PAGER", diff --git a/server/protocol.def b/server/protocol.def index 1bb4dcb..daed9e9 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2318,6 +2318,7 @@ enum message_type VARARG(windows,user_handles); /* window handles that inherit it */ @END #define SET_WINDOW_CLOAKED_ON 0x01 +#define SET_WINDOW_CLOAKED_SHELL 0x02
/* Set the parent of a window */ diff --git a/server/window.c b/server/window.c index 356995a..4a73c0a 100644 --- a/server/window.c +++ b/server/window.c @@ -83,6 +83,7 @@ struct window 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 is_cloaked_by_shell : 1; 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 */ @@ -506,6 +507,7 @@ static struct window *create_window( struct window *parent, struct window *owner win->is_linked = 0; win->is_layered = 0; win->is_cloaked = 0; + win->is_cloaked_by_shell = 0; win->dpi_awareness = DPI_AWARENESS_PER_MONITOR_AWARE; win->dpi = 0; win->user_data = 0; @@ -609,7 +611,7 @@ int is_child_window( user_handle_t parent, user_handle_t child ) int is_valid_foreground_window( user_handle_t window ) { struct window *win = get_user_object( window, USER_WINDOW ); - return win && (win->style & (WS_POPUP|WS_CHILD)) != WS_CHILD; + return win && (win->style & (WS_POPUP|WS_CHILD)) != WS_CHILD && !win->is_cloaked_by_shell; }
/* make a window active if possible */ @@ -787,7 +789,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_desktop_window( parent ) && (ptr->is_cloaked || ptr->is_cloaked_by_shell)) 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 */ @@ -2191,7 +2193,8 @@ DECL_HANDLER(get_window_cloaked) cloaked |= DWM_CLOAKED_INHERITED; }
- if (win->is_cloaked) cloaked |= DWM_CLOAKED_APP; + if (win->is_cloaked_by_shell) cloaked |= DWM_CLOAKED_SHELL; + else if (win->is_cloaked) cloaked |= DWM_CLOAKED_APP; else cloaked = 0;
reply->cloaked = cloaked; @@ -2206,6 +2209,13 @@ DECL_HANDLER(set_window_cloaked) user_handle_t *data;
if (!win) return; + if (req->cloaked & SET_WINDOW_CLOAKED_SHELL) + { + /* the shell has control over cloaking windows individually */ + win->is_cloaked_by_shell = req->cloaked & SET_WINDOW_CLOAKED_ON; + reply->count = 0; + return; + } if (is_desktop_window( win )) { set_error( STATUS_ACCESS_DENIED );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=81540
Your paranoid android.
=== debiant (32 bit report) ===
user32: monitor: Timeout
=== debiant (32 bit Chinese:China report) ===
user32: monitor: Timeout
=== debiant (32 bit WoW report) ===
user32: monitor: Timeout
=== debiant (64 bit WoW report) ===
user32: monitor: Timeout
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=81535
Your paranoid android.
=== debiant (32 bit report) ===
user32: monitor: Timeout
=== debiant (32 bit Chinese:China report) ===
user32: monitor: Timeout win.c:10149: Test failed: Expected foreground window 000E013E, got 00E10102
=== debiant (32 bit WoW report) ===
user32: monitor: Timeout
=== debiant (64 bit WoW report) ===
user32: monitor: Timeout