[PATCH v2 0/1] MR11030: Add support for a layered window region. (v3)
When WindowFromPoint is called with a point inside the transparent region of the top-level window, the top-level window is returned instead of the expected window beneath it. I found that this problem was previously addressed in the patch [https://gitlab.winehq.org/wine/wine-staging/-/blob/master/patches/user32-Mou...]. I adapted that patch for the current version and added a test. -- v2: Add support for a layered window region. (v3) https://gitlab.winehq.org/wine/wine/-/merge_requests/11030
From: Ivan Lyugaev <valy@etersoft.ru> --- dlls/user32/tests/win.c | 94 ++++++++++++++++++++++++++++++ dlls/win32u/dce.c | 101 +++++++++++++++++++++++++++++++++ include/wine/server_protocol.h | 16 +++++- server/protocol.def | 7 +++ server/request_handlers.h | 4 ++ server/request_trace.h | 9 +++ server/window.c | 32 +++++++++++ 7 files changed, 262 insertions(+), 1 deletion(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 6ef13eaeaa3..d40da234982 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -10696,6 +10696,99 @@ static void window_from_point_proc(HWND parent) CloseHandle(end_event); } +static void test_window_from_point_layered(void) +{ + HWND win, top, bottom; + unsigned int *bits, width = 100, height = 100, x, y; + char bmibuf[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)]; + BITMAPINFO *bmi = (BITMAPINFO *)bmibuf; + HBITMAP bitmap, holdbmp; + SIZE size = { width, height }; + POINT dst = { 0, 0 }, src = { 0, 0 }, pt = { 0, 0 }; + HDC hdc; + BOOL ret; + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = 128; + bf.AlphaFormat = AC_SRC_ALPHA; + + if (!pUpdateLayeredWindow) + { + win_skip( "UpdateLayeredWindow is not available\n" ); + return; + } + + bottom = CreateWindowExA(0, "MainWindowClass", "bottom", WS_POPUP | WS_VISIBLE, + 0, 0, width, height, NULL, NULL, GetModuleHandleA(0), NULL); + ok(bottom != 0, "CreateWindowEx error %lu\n", GetLastError()); + + pt.x = 25; + pt.y = 50; + ClientToScreen( bottom, &pt ); + win = WindowFromPoint(pt); + pt.x = 75; + pt.y = 50; + ClientToScreen( bottom, &pt ); + if (win == bottom) + win = WindowFromPoint(pt); + if (win != bottom) + { + skip("there's another window covering test window\n"); + DestroyWindow( bottom ); + return; + } + + top = CreateWindowExA(WS_EX_LAYERED | WS_EX_TOPMOST, "MainWindowClass", "top", WS_POPUP | WS_VISIBLE, + 0, 0, width, height, NULL, NULL, GetModuleHandleA(0), NULL); + ok(top != 0, "CreateWindowEx error %lu\n", GetLastError()); + + hdc = CreateCompatibleDC( 0 ); + + memset( bmi, 0, sizeof(bmibuf) ); + bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader); + bmi->bmiHeader.biWidth = width; + bmi->bmiHeader.biHeight = height * -1; + bmi->bmiHeader.biBitCount = 32; + bmi->bmiHeader.biPlanes = 1; + bmi->bmiHeader.biCompression = BI_RGB; + + bitmap = CreateDIBSection( 0, bmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0 ); + ok( bitmap != NULL, "CreateDIBSection error %lu\n", GetLastError() ); + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + bits[y * width + x] = (x < width / 2) ? 0 : 0xFF00FFC8; + } + } + + holdbmp = SelectObject( hdc, bitmap ); + + ret = pUpdateLayeredWindow( top, 0, &dst, &size, hdc, &src, 0, &bf, ULW_ALPHA ); + ok( ret, "UpdateLayeredWindow should succeed on layered window\n" ); + + SelectObject( hdc, holdbmp ); + DeleteObject( bitmap ); + DeleteDC( hdc ); + + pt.x = 25; + pt.y = 50; + ClientToScreen( bottom, &pt ); + win = WindowFromPoint( pt ); + ok(win == bottom, "expected %p window, got %p\n", bottom, win); + + pt.x = 75; + pt.y = 50; + ClientToScreen( top, &pt ); + win = WindowFromPoint( pt ); + ok(win == top, "expected %p window, got %p\n", top, win); + + DestroyWindow( top ); + DestroyWindow( bottom ); +} + static void test_window_from_point(HWND main_window, const char *argv0) { HWND hwnd, child, win; @@ -14510,6 +14603,7 @@ START_TEST(win) /* Add the tests below this line */ test_child_window_from_point(); test_window_from_point(hwndMain, argv[0]); + test_window_from_point_layered(); test_thick_child_size(hwndMain); test_fullscreen(); test_hwnd_message(); diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c index cf641c4ec2b..7964ab9b3d5 100644 --- a/dlls/win32u/dce.c +++ b/dlls/win32u/dce.c @@ -409,6 +409,95 @@ static BYTE shape_from_color_key_32( UINT32 *bits, UINT32 color_mask, UINT32 col return ~mask; } +static void set_layer_region( struct window_surface *surface, HRGN hrgn ) +{ + static const RECT empty_rect; + RGNDATA *data; + DWORD size; + HWND hwnd = surface->hwnd; + + if (hrgn) + { + if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return; + if (!(data = malloc( size ))) return; + if (!NtGdiGetRegionData( hrgn, size, data )) + { + free( data ); + return; + } + SERVER_START_REQ( set_layer_region ) + { + req->window = wine_server_user_handle( hwnd ); + if (data->rdh.nCount) + wine_server_add_data( req, data->Buffer, data->rdh.nCount * sizeof(RECT) ); + else + wine_server_add_data( req, &empty_rect, sizeof(empty_rect) ); + wine_server_call( req ); + } + SERVER_END_REQ; + free( data ); + } + else /* clear existing region */ + { + SERVER_START_REQ( set_layer_region ) + { + req->window = wine_server_user_handle( hwnd ); + wine_server_call( req ); + } + SERVER_END_REQ; + } +} + +static inline void flush_rgn_data( HRGN rgn, RGNDATA *data ) +{ + HRGN tmp = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data ); + NtGdiCombineRgn( rgn, rgn, tmp, RGN_OR ); + NtGdiDeleteObjectApp( tmp ); + data->rdh.nCount = 0; +} + +static inline void add_row( HRGN rgn, RGNDATA *data, int x, int y, int len ) +{ + RECT *rect = (RECT *)data->Buffer + data->rdh.nCount; + + if (len <= 0) return; + rect->left = x; + rect->top = y; + rect->right = x + len; + rect->bottom = y + 1; + data->rdh.nCount++; + if (data->rdh.nCount * sizeof(RECT) > data->rdh.nRgnSize - sizeof(RECT)) + flush_rgn_data( rgn, data ); +} + +static HRGN create_region_from_shape_bits( BYTE *shape, UINT stride, UINT width, UINT height ) +{ + char buffer[4096]; + RGNDATA *rgn_data = (RGNDATA *)buffer; + HRGN rgn; + UINT start, x, y; + + rgn_data->rdh.dwSize = sizeof(rgn_data->rdh); + rgn_data->rdh.iType = RDH_RECTANGLES; + rgn_data->rdh.nCount = 0; + rgn_data->rdh.nRgnSize = sizeof(buffer) - sizeof(rgn_data->rdh); + rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 ); + + for (y = 0; y < height; y++, shape += stride) + { + x = 0; + while (x < width) + { + while (x < width && !(shape[x / 8] & (0x80 >> (x & 7)))) x++; + start = x; + while (x < width && (shape[x / 8] & (0x80 >> (x & 7)))) x++; + add_row( rgn, rgn_data, start, y, x - start ); + } + } + if (rgn_data->rdh.nCount) flush_rgn_data( rgn, rgn_data ); + return rgn; +} + static BOOL set_surface_shape( struct window_surface *surface, const RECT *rect, const RECT *dirty, const BITMAPINFO *color_info, void *color_bits ) { @@ -425,6 +514,12 @@ static BOOL set_surface_shape( struct window_surface *surface, const RECT *rect, height = abs( color_info->bmiHeader.biHeight ); assert( !(width & 7) ); /* expect 1bpp bitmap to be aligned on bytes */ + if (surface->color_key == CLR_INVALID && alpha_mask == 0) + { + set_layer_region( surface, NULL ); + return FALSE; + } + if (!surface->shape_bitmap) surface->shape_bitmap = NtGdiCreateBitmap( width, height, 1, 1, NULL ); if (!(shape_bits = window_surface_get_shape( surface, shape_info ))) return FALSE; @@ -500,6 +595,12 @@ static BOOL set_surface_shape( struct window_surface *surface, const RECT *rect, } ret = memcmp( old_shape, shape_bits, shape_info->bmiHeader.biSizeImage ); + if (ret) + { + HRGN rgn = alpha_mask == 0 ? NULL : create_region_from_shape_bits( shape_bits, shape_stride, width, height ); + set_layer_region( surface, rgn ); + NtGdiDeleteObjectApp( rgn ); + } free( old_shape ); return ret; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 517d458ad72..a2fb661d3b0 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3867,6 +3867,17 @@ struct set_window_region_reply }; +struct set_layer_region_request +{ + struct request_header __header; + user_handle_t window; + /* VARARG(region,rectangles); */ +}; +struct set_layer_region_reply +{ + struct reply_header __header; +}; + struct get_update_region_request { @@ -6360,6 +6371,7 @@ enum request REQ_get_visible_region, REQ_get_window_region, REQ_set_window_region, + REQ_set_layer_region, REQ_get_update_region, REQ_update_window_zorder, REQ_redraw_window, @@ -6674,6 +6686,7 @@ union generic_request struct get_visible_region_request get_visible_region_request; struct get_window_region_request get_window_region_request; struct set_window_region_request set_window_region_request; + struct set_layer_region_request set_layer_region_request; struct get_update_region_request get_update_region_request; struct update_window_zorder_request update_window_zorder_request; struct redraw_window_request redraw_window_request; @@ -6986,6 +6999,7 @@ union generic_reply struct get_visible_region_reply get_visible_region_reply; struct get_window_region_reply get_window_region_reply; struct set_window_region_reply set_window_region_reply; + struct set_layer_region_reply set_layer_region_reply; struct get_update_region_reply get_update_region_reply; struct update_window_zorder_reply update_window_zorder_reply; struct redraw_window_reply redraw_window_reply; @@ -7131,6 +7145,6 @@ union generic_reply struct d3dkmt_mutex_release_reply d3dkmt_mutex_release_reply; }; -#define SERVER_PROTOCOL_VERSION 944 +#define SERVER_PROTOCOL_VERSION 945 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index e7d966613ac..c7443a21cfc 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2869,6 +2869,13 @@ enum coords_relative @END +/* Set the layer region */ +@REQ(set_layer_region) + user_handle_t window; /* handle to the window */ + VARARG(region,rectangles); /* list of rectangles for the region (in window coords) */ +@END + + /* Get the window update region */ @REQ(get_update_region) user_handle_t window; /* handle to the window */ diff --git a/server/request_handlers.h b/server/request_handlers.h index 1e5689e6dcd..8571eaae44d 100644 --- a/server/request_handlers.h +++ b/server/request_handlers.h @@ -171,6 +171,7 @@ DECL_HANDLER(get_windows_offset); DECL_HANDLER(get_visible_region); DECL_HANDLER(get_window_region); DECL_HANDLER(set_window_region); +DECL_HANDLER(set_layer_region); DECL_HANDLER(get_update_region); DECL_HANDLER(update_window_zorder); DECL_HANDLER(redraw_window); @@ -482,6 +483,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_get_visible_region, (req_handler)req_get_window_region, (req_handler)req_set_window_region, + (req_handler)req_set_layer_region, (req_handler)req_get_update_region, (req_handler)req_update_window_zorder, (req_handler)req_redraw_window, @@ -1624,6 +1626,8 @@ C_ASSERT( sizeof(struct get_window_region_reply) == 32 ); C_ASSERT( offsetof(struct set_window_region_request, window) == 12 ); C_ASSERT( offsetof(struct set_window_region_request, redraw) == 16 ); C_ASSERT( sizeof(struct set_window_region_request) == 24 ); +C_ASSERT( FIELD_OFFSET(struct set_layer_region_request, window) == 12 ); +C_ASSERT( sizeof(struct set_layer_region_request) == 16 ); C_ASSERT( offsetof(struct get_update_region_request, window) == 12 ); C_ASSERT( offsetof(struct get_update_region_request, from_child) == 16 ); C_ASSERT( offsetof(struct get_update_region_request, flags) == 20 ); diff --git a/server/request_trace.h b/server/request_trace.h index 5c8325039f3..745e797cbbb 100644 --- a/server/request_trace.h +++ b/server/request_trace.h @@ -1955,6 +1955,12 @@ static void dump_set_window_region_request( const struct set_window_region_reque dump_varargs_rectangles( ", region=", cur_size ); } +static void dump_set_layer_region_request( const struct set_layer_region_request *req ) +{ + fprintf( stderr, " window=%08x", req->window ); + dump_varargs_rectangles( ", region=", cur_size ); +} + static void dump_get_update_region_request( const struct get_update_region_request *req ) { fprintf( stderr, " window=%08x", req->window ); @@ -3689,6 +3695,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = (dump_func)dump_get_visible_region_request, (dump_func)dump_get_window_region_request, (dump_func)dump_set_window_region_request, + (dump_func)dump_set_layer_region_request, (dump_func)dump_get_update_region_request, (dump_func)dump_update_window_zorder_request, (dump_func)dump_redraw_window_request, @@ -4000,6 +4007,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = (dump_func)dump_get_visible_region_reply, (dump_func)dump_get_window_region_reply, NULL, + NULL, (dump_func)dump_get_update_region_reply, NULL, NULL, @@ -4311,6 +4319,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = "get_visible_region", "get_window_region", "set_window_region", + "set_layer_region", "get_update_region", "update_window_zorder", "redraw_window", diff --git a/server/window.c b/server/window.c index 897034159a9..bff431a2791 100644 --- a/server/window.c +++ b/server/window.c @@ -71,6 +71,7 @@ struct window struct rectangle surface_rect; /* window surface rectangle (relative to parent client area) */ struct rectangle client_rect; /* client rectangle (relative to parent client area) */ struct region *win_region; /* region for shaped windows (relative to window rect) */ + struct region *layer_region; /* region for layered windows (relative to window rect) */ struct region *update_region; /* update region (relative to window rect) */ unsigned int style; /* window style */ unsigned int ex_style; /* window extended style */ @@ -661,6 +662,7 @@ static struct window *create_window( struct window *parent, struct window *owner win->class = class; win->atom = atom; win->win_region = NULL; + win->layer_region = NULL; win->update_region = NULL; win->style = 0; win->ex_style = 0; @@ -939,6 +941,9 @@ static int is_point_in_window( struct window *win, int *x, int *y, unsigned int if (win->win_region && !point_in_region( win->win_region, *x - win->window_rect.left, *y - win->window_rect.top )) return 0; /* not in window region */ + if (win->layer_region && + !point_in_region( win->layer_region, *x - win->window_rect.left, *y - win->window_rect.top )) + return 0; /* not in layer mask region */ return 1; } @@ -2130,6 +2135,14 @@ static void set_window_region( struct window *win, struct region *region, int re } +/* set the layer region */ +static void set_layer_region( struct window *win, struct region *region ) +{ + if (win->layer_region) free_region( win->layer_region ); + win->layer_region = region; +} + + /* destroy a window */ void free_window_handle( struct window *win ) { @@ -2193,6 +2206,7 @@ void free_window_handle( struct window *win ) detach_window_thread( win ); if (win->parent) set_parent_window( win, NULL ); + if (win->layer_region) free_region( win->layer_region ); free_user_handle( win->handle ); win->handle = 0; release_object( win ); @@ -2974,6 +2988,24 @@ DECL_HANDLER(set_window_region) } +/* set the layer region */ +DECL_HANDLER(set_layer_region) +{ + struct region *region = NULL; + struct window *win = get_window( req->window ); + + if (!win) return; + + if (get_req_data_size()) /* no data means remove the region completely */ + { + if (!(region = create_region_from_req_data( get_req_data(), get_req_data_size() ))) + return; + if (win->ex_style & WS_EX_LAYOUTRTL) mirror_region( &win->window_rect, region ); + } + set_layer_region( win, region ); +} + + /* get a window update region */ DECL_HANDLER(get_update_region) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11030
@group_10_bot_75d9c3a6f9018cf0de9c3e87113e0464 -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11030#note_142441
participants (2)
-
Ivan Lyugaev -
Ivan Lyugaev (@valy)