From: Namo Nath <nn.git@tuta.io> Implement DWM Region masking utilizing a dual-path strategy (OpenGL and GDI) to support hardware-accelerated overlays, DWM glass, and late-bound transparency. Hardware (OpenGL) Path: - Always prefer ARGB-capable EGL configs during OpenGL surface creation to support runtime DWM and glass toggling. - Restrict EGL_PRESENT_OPAQUE_EXT to EGLConfigs with valid alpha channels to prevent EGL_BAD_MATCH compositor crashes. - Utilize targeted glScissor clears in wayland_drawable_swap to physically punch out transparent margins in hardware-accelerated buffers. Architectural Note on glScissor: Due to architectural limitations in combining GDI and OpenGL subsurfaces within copy_pixel_region, software-based alpha masking for hardware clients is unviable. Alternative implementations utilizing the Wayland wp-viewporter protocol introduced sizing and synchronization complexities. The glScissor implementation provides a performant, state-safe, and native OpenGL mechanism to cut out margins without desyncing the Wayland compositor state. Software (GDI) Path: - Implement an Alpha Matrix inside copy_pixel_region to clear GDI base layers. - When an OpenGL client is active alongside DWM margins, GDI yields the center of the surface while preserving GDI-rendered margins (e.g., titlebars). - Recreate the Wayland SHM buffer queue dynamically as ARGB upon transparency promotion. This purges initial XRGB buffers, preventing "black box" backgrounds during an application's late-binding initialization phase. Wayland State: - Dynamically recalculate and submit wl_surface_set_opaque_region based on DWM modes, WS_EX_LAYERED, and WS_EX_TRANSPARENT flags to ensure the compositor accurately blends or skips solid window segments. --- dlls/winewayland.drv/opengl.c | 98 ++++++++++++++++- dlls/winewayland.drv/waylanddrv.h | 18 +++ dlls/winewayland.drv/waylanddrv_main.c | 1 + dlls/winewayland.drv/window.c | 98 +++++++++++++++++ dlls/winewayland.drv/window_surface.c | 147 +++++++++++++++++++++---- 5 files changed, 338 insertions(+), 24 deletions(-) diff --git a/dlls/winewayland.drv/opengl.c b/dlls/winewayland.drv/opengl.c index 9a4b14ec7d9..3ed5fc9c641 100644 --- a/dlls/winewayland.drv/opengl.c +++ b/dlls/winewayland.drv/opengl.c @@ -83,17 +83,42 @@ static void wayland_gl_drawable_sync_size(struct wayland_gl_drawable *gl) static BOOL wayland_opengl_surface_create(HWND hwnd, int format, struct opengl_drawable **drawable) { - EGLConfig config = egl_config_for_format(format); + /* Always prefer ARGB to support runtime DWM/Glass toggling */ + EGLint argb_attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; + EGLConfig config; struct wayland_client_surface *client; EGLint attribs[4], *attrib = attribs; struct opengl_drawable *previous; struct wayland_gl_drawable *gl; RECT rect; + int count = 0; + EGLint alpha_size = 0; + struct wayland_win_data *data; + BOOL is_dwm_active = FALSE; TRACE("hwnd=%p format=%d\n", hwnd, format); if ((previous = *drawable) && previous->format == format) return TRUE; + if ((data = wayland_win_data_get(hwnd))) + { + is_dwm_active = (data->dwm_mode != WAYLAND_DWM_EXTEND_NONE); + wayland_win_data_release(data); + } + + if (funcs->p_eglChooseConfig(egl->display, argb_attribs, &config, 1, &count) && count) + { + TRACE("Selected ARGB capable EGL config for window %p\n", hwnd); + } + else + { + WARN("ARGB config not found, falling back to requested format\n"); + config = egl_config_for_format(format); + } + + /* Query the selected config for an alpha channel */ + funcs->p_eglGetConfigAttrib(egl->display, config, EGL_ALPHA_SIZE, &alpha_size); + NtUserGetClientRect(hwnd, &rect, NtUserGetDpiForWindow(hwnd)); if (rect.right == rect.left) rect.right = rect.left + 1; if (rect.bottom == rect.top) rect.bottom = rect.top + 1; @@ -102,8 +127,19 @@ static BOOL wayland_opengl_surface_create(HWND hwnd, int format, struct opengl_d WARN("Missing EGL_EXT_present_opaque extension\n"); else { - *attrib++ = EGL_PRESENT_OPAQUE_EXT; - *attrib++ = EGL_TRUE; + /* Automatically enable EGL transparency if the config supports it (ARGB). + * Standard games are protected from accidental transparency by the full-window + * wl_surface_set_opaque_region protocol applied in window.c */ + if (alpha_size > 0 || is_dwm_active) + { + *attrib++ = EGL_PRESENT_OPAQUE_EXT; + *attrib++ = EGL_FALSE; + } + else + { + *attrib++ = EGL_PRESENT_OPAQUE_EXT; + *attrib++ = EGL_TRUE; + } } *attrib++ = EGL_NONE; @@ -139,6 +175,51 @@ static void wayland_init_egl_platform(struct egl_platform *platform) egl = platform; } +static void wayland_gl_clear_margin_regions(struct wayland_gl_drawable *gl) +{ + struct wayland_win_data *data; + RECT rect; + int w, h; + + if (!(data = wayland_win_data_get(gl->base.client->hwnd))) return; + + if (data->dwm_mode == WAYLAND_DWM_EXTEND_MARGINS) + { + NtUserGetClientRect(data->hwnd, &rect, NtUserGetDpiForWindow(data->hwnd)); + + w = rect.right; + h = rect.bottom; + + if (funcs->p_glEnable && funcs->p_glScissor && funcs->p_glClear && funcs->p_glClearColor) + { + funcs->p_glEnable(GL_SCISSOR_TEST); + funcs->p_glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + /* Top margin */ + funcs->p_glScissor(0, h - data->margins.cyTopHeight, w, data->margins.cyTopHeight); + funcs->p_glClear(GL_COLOR_BUFFER_BIT); + + /* Bottom margin */ + funcs->p_glScissor(0, 0, w, data->margins.cyBottomHeight); + funcs->p_glClear(GL_COLOR_BUFFER_BIT); + + /* Left margin */ + funcs->p_glScissor(0, data->margins.cyBottomHeight, data->margins.cxLeftWidth, + h - data->margins.cyTopHeight - data->margins.cyBottomHeight); + funcs->p_glClear(GL_COLOR_BUFFER_BIT); + + /* Right margin */ + funcs->p_glScissor(w - data->margins.cxRightWidth, data->margins.cyBottomHeight, data->margins.cxRightWidth, + h - data->margins.cyTopHeight - data->margins.cyBottomHeight); + funcs->p_glClear(GL_COLOR_BUFFER_BIT); + + funcs->p_glDisable(GL_SCISSOR_TEST); + } + } + + wayland_win_data_release(data); +} + static void wayland_drawable_flush(struct opengl_drawable *base, UINT flags) { struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base); @@ -146,16 +227,23 @@ static void wayland_drawable_flush(struct opengl_drawable *base, UINT flags) TRACE("drawable %s, flags %#x\n", debugstr_opengl_drawable(base), flags); if (flags & GL_FLUSH_INTERVAL) funcs->p_eglSwapInterval(egl->display, abs(base->interval)); - /* Since context_flush is called from operations that may latch the native size, * perform any pending resizes before calling them. */ - if (flags & GL_FLUSH_UPDATED) wayland_gl_drawable_sync_size(gl); + if (flags & GL_FLUSH_UPDATED) + { + wayland_gl_drawable_sync_size(gl); + /* Ensure margins are cleared during the flush/resize cycle */ + wayland_gl_clear_margin_regions(gl); + } } static BOOL wayland_drawable_swap(struct opengl_drawable *base) { struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base); + /* Apply hardware scissor to mask the dwm margins before swapping. */ + wayland_gl_clear_margin_regions(gl); + client_surface_present(base->client); funcs->p_eglSwapBuffers(egl->display, gl->base.surface); diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index ad41a1b474e..e064d8cba6a 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -84,6 +84,21 @@ enum wayland_surface_role WAYLAND_SURFACE_ROLE_SUBSURFACE, }; +enum wayland_dwm_extend_mode +{ + WAYLAND_DWM_EXTEND_NONE = 0, + WAYLAND_DWM_EXTEND_MARGINS = 1, + WAYLAND_DWM_EXTEND_GLASS = 2 +}; + +struct wayland_dwm_margins +{ + int cxLeftWidth; + int cxRightWidth; + int cyTopHeight; + int cyBottomHeight; +}; + struct wayland_keyboard { struct wl_keyboard *wl_keyboard; @@ -372,6 +387,8 @@ struct wayland_win_data BOOL is_fullscreen; BOOL managed; BOOL layered_attribs_set; + struct wayland_dwm_margins margins; + int dwm_mode; }; struct wayland_win_data *wayland_win_data_get(HWND hwnd); @@ -446,6 +463,7 @@ BOOL WAYLAND_SetIMECompositionRect(HWND hwnd, RECT rect); void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor); BOOL WAYLAND_SetCursorPos(INT x, INT y); void WAYLAND_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alpha, DWORD flags); +BOOL WAYLAND_SetWindowDwmConfig(HWND hwnd, INT command, const void *data); void WAYLAND_SetWindowIcons(HWND hwnd, HICON icon, const ICONINFO *ii, HICON icon_small, const ICONINFO *ii_small); void WAYLAND_SetWindowStyle(HWND hwnd, INT offset, STYLESTRUCT *style); void WAYLAND_SetWindowText(HWND hwnd, LPCWSTR text); diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index cdb5dd8a956..d81009cb871 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -45,6 +45,7 @@ static const struct user_driver_funcs waylanddrv_funcs = .pSetCursor = WAYLAND_SetCursor, .pSetCursorPos = WAYLAND_SetCursorPos, .pSetLayeredWindowAttributes = WAYLAND_SetLayeredWindowAttributes, + .pSetWindowDwmConfig = WAYLAND_SetWindowDwmConfig, .pSetWindowIcons = WAYLAND_SetWindowIcons, .pSetWindowStyle = WAYLAND_SetWindowStyle, .pSetWindowText = WAYLAND_SetWindowText, diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 07e0858fb39..cb88ad4bddc 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -298,6 +298,11 @@ static void wayland_surface_update_state_toplevel(struct wayland_surface *surfac static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) { struct wayland_surface *surface = data->wayland_surface; + struct wayland_client_surface *client = data->client_surface; + DWORD ex_style = NtUserGetWindowLongW(data->hwnd, GWL_EXSTYLE); + struct wl_region *opaque_region = NULL; + RECT rect; + int center_w, center_h; switch (surface->role) { @@ -316,6 +321,39 @@ static void wayland_win_data_update_wayland_state(struct wayland_win_data *data) break; } + /* GLOBAL SCISSOR: Opaque Region Calculation */ + + /* Full Surface Alpha: DWM Glass or explicit click-through overlays */ + if (data->dwm_mode == WAYLAND_DWM_EXTEND_GLASS || ((ex_style & WS_EX_LAYERED) && (ex_style & WS_EX_TRANSPARENT))) + { + opaque_region = NULL; + } + /* Partial Surface Alpha: DWM Margins */ + else if (data->dwm_mode == WAYLAND_DWM_EXTEND_MARGINS) + { + NtUserGetClientRect(data->hwnd, &rect, NtUserGetDpiForWindow(data->hwnd)); + center_w = rect.right - data->margins.cxLeftWidth - data->margins.cxRightWidth; + center_h = rect.bottom - data->margins.cyTopHeight - data->margins.cyBottomHeight; + + opaque_region = wl_compositor_create_region(process_wayland.wl_compositor); + if (center_w > 0 && center_h > 0) + { + wl_region_add(opaque_region, data->margins.cxLeftWidth, data->margins.cyTopHeight, center_w, center_h); + } + } + /* Standard Opaque */ + else + { + opaque_region = wl_compositor_create_region(process_wayland.wl_compositor); + NtUserGetWindowRect(data->hwnd, &rect, NtUserGetDpiForWindow(data->hwnd)); + wl_region_add(opaque_region, 0, 0, rect.right, rect.bottom); + } + + wl_surface_set_opaque_region(surface->wl_surface, opaque_region); + if (client && client->wl_surface) wl_surface_set_opaque_region(client->wl_surface, opaque_region); + + if (opaque_region) wl_region_destroy(opaque_region); + wl_display_flush(process_wayland.wl_display); } @@ -648,6 +686,66 @@ void WAYLAND_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alpha, DWO wayland_win_data_release(data); } +/***************************************************************** + * WAYLAND_SetWindowDwmConfig + */ +BOOL WAYLAND_SetWindowDwmConfig(HWND hwnd, INT command, const void *data) +{ + struct wayland_win_data *data_ptr; + const struct wayland_dwm_margins *margins = data; + int mode = WAYLAND_DWM_EXTEND_NONE; + int width, height; + RECT rect; + + /* DWM_CONFIG_OPAQUE_REGION (1) is passed by NtUserSetWindowDwmConfig */ + if (command != 1 || !margins) return FALSE; + if (!(data_ptr = wayland_win_data_get(hwnd))) return FALSE; + + NtUserGetClientRect(hwnd, &rect, NtUserGetDpiForWindow(hwnd)); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + /* GLASS MODE: Full-surface composition. + * 1. 'Sheet of Glass': Triggered by the -1 magic value; DWM manages full coverage. + * 2. 'Saturated Margins': Manual insets that meet or exceed client dimensions, + * effectively covering the entire surface. */ + if (margins->cxLeftWidth == -1 || + (width > 0 && height > 0 && + margins->cxLeftWidth + margins->cxRightWidth >= width && + margins->cyTopHeight + margins->cyBottomHeight >= height)) + { + mode = WAYLAND_DWM_EXTEND_GLASS; + } + /* MARGINS MODE: Partial-surface composition. + * Triggered when at least one margin is non-zero, creating a partial + * glass frame, sidebar, or 'slice' while leaving the center opaque. */ + else if (margins->cxLeftWidth > 0 || margins->cxRightWidth > 0 || + margins->cyTopHeight > 0 || margins->cyBottomHeight > 0) + { + mode = WAYLAND_DWM_EXTEND_MARGINS; + } + + /* State Sync & Invalidation + * Only trigger visual flushes if the DWM state has actually changed. */ + if (data_ptr->dwm_mode != mode || memcmp(&data_ptr->margins, margins, sizeof(struct wayland_dwm_margins))) + { + TRACE("hwnd %p setting dwm_mode %d\n", hwnd, mode); + data_ptr->dwm_mode = mode; + data_ptr->margins = *margins; + + /* Invalidate to force a surface flush/re-creation with the new format + * and hardware blanking path (if transitioning to/from ARGB). */ + NtUserRedrawWindow(hwnd, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN); + + /* Immediately recalculate Wayland protocol regions (opaque_region) */ + if (data_ptr->wayland_surface) + wayland_win_data_update_wayland_state(data_ptr); + } + + wayland_win_data_release(data_ptr); + return TRUE; +} + static enum xdg_toplevel_resize_edge hittest_to_resize_edge(WPARAM hittest) { switch (hittest) diff --git a/dlls/winewayland.drv/window_surface.c b/dlls/winewayland.drv/window_surface.c index e7595b7ccb0..720d59b60fa 100644 --- a/dlls/winewayland.drv/window_surface.c +++ b/dlls/winewayland.drv/window_surface.c @@ -46,6 +46,7 @@ struct wayland_window_surface struct window_surface header; struct wayland_buffer_queue *wayland_buffer_queue; BOOL layered; + int dwm_mode; }; static struct wayland_window_surface *wayland_window_surface_cast( @@ -245,18 +246,22 @@ RGNDATA *get_region_data(HRGN region) */ static void copy_pixel_region(const char *src_pixels, RECT *src_rect, char *dst_pixels, RECT *dst_rect, - HRGN region, BOOL force_opaque) + HRGN region, BOOL force_opaque, + int dwm_mode, const struct wayland_dwm_margins *margins, + BOOL has_color_key, COLORREF color_key, BOOL layered_transparent, + BOOL has_client) { static const int bpp = WINEWAYLAND_BYTES_PER_PIXEL; RGNDATA *rgndata = get_region_data(region); - RECT *rgn_rect; - RECT *rgn_rect_end; - int src_stride, dst_stride; + RECT *rgn_rect, *rgn_rect_end; + int src_stride, dst_stride, win_w, win_h; if (!rgndata) return; src_stride = (src_rect->right - src_rect->left) * bpp; dst_stride = (dst_rect->right - dst_rect->left) * bpp; + win_w = src_rect->right - src_rect->left; + win_h = src_rect->bottom - src_rect->top; rgn_rect = (RECT *)rgndata->Buffer; rgn_rect_end = rgn_rect + rgndata->rdh.nCount; @@ -278,7 +283,57 @@ static void copy_pixel_region(const char *src_pixels, RECT *src_rect, width = rc.right - rc.left; height = rc.bottom - rc.top; - /* Fast path for full width rectangles. */ + /* Only enter the Alpha Matrix if transparency manipulation is explicitly required. */ + if (dwm_mode != WAYLAND_DWM_EXTEND_NONE || has_color_key || layered_transparent) + { + int rel_x, rel_y; + UINT32 pixel, rgb; + const UINT32 *src_ptr; + UINT32 *dst_ptr; + + for (y = 0; y < height; y++) + { + src_ptr = (const UINT32 *)src; + dst_ptr = (UINT32 *)dst; + rel_y = rc.top + y - src_rect->top; + + for (x = 0; x < width; x++) + { + pixel = src_ptr[x]; + rgb = pixel & 0x00FFFFFF; + rel_x = rc.left + x - src_rect->left; + + if (dwm_mode == WAYLAND_DWM_EXTEND_MARGINS && margins) + { + BOOL in_margin = (rel_y < margins->cyTopHeight || rel_y >= win_h - margins->cyBottomHeight || + rel_x < margins->cxLeftWidth || rel_x >= win_w - margins->cxRightWidth); + + if (in_margin) dst_ptr[x] = 0; /* Punch hole: yield center to GL subsurface */ + else if (has_client) dst_ptr[x] = pixel | 0xFF000000; /* Opaque GDI borders/titlebar */ + else dst_ptr[x] = pixel | 0xFF000000; /* Standard opaque GDI backing plate */ + } + else if (dwm_mode == WAYLAND_DWM_EXTEND_GLASS) + { + if (has_client) dst_ptr[x] = 0; /* Punch hole: yield entire surface to GL */ + else if (rgb == 0) dst_ptr[x] = 0; /* Transparent GDI Glass pixel */ + else dst_ptr[x] = pixel | 0xFF000000; /* Opaque GDI Glass pixel */ + } + else if (layered_transparent || (has_color_key && rgb == (color_key & 0x00FFFFFF))) + { + dst_ptr[x] = 0; /* Fully transparent layered or color-keyed pixel */ + } + else + { + dst_ptr[x] = pixel | 0xFF000000; /* Fallback: standard opaque GDI pixel */ + } + } + src += src_stride; + dst += dst_stride; + } + return; /* Exit after matrix processing */ + } + + /* Standard Opaque Fast Paths */ if (width * bpp == src_stride && src_stride == dst_stride) { if (force_opaque) @@ -315,26 +370,34 @@ static void copy_pixel_region(const char *src_pixels, RECT *src_rect, } /********************************************************************** - * wayland_shm_buffer_copy_data + * wayland_shm_buffer_copy_data */ static void wayland_shm_buffer_copy_data(struct wayland_shm_buffer *buffer, const char *bits, RECT *rect, - HRGN region, BOOL force_opaque) + HRGN region, BOOL force_opaque, + int dwm_mode, const struct wayland_dwm_margins *margins, + BOOL has_color_key, COLORREF color_key, BOOL layered_transparent, + BOOL has_client) { RECT buffer_rect = {0, 0, buffer->width, buffer->height}; TRACE("buffer=%p bits=%p rect=%s\n", buffer, bits, wine_dbgstr_rect(rect)); - copy_pixel_region(bits, rect, buffer->map_data, &buffer_rect, region, force_opaque); + copy_pixel_region(bits, rect, buffer->map_data, &buffer_rect, region, force_opaque, + dwm_mode, margins, has_color_key, color_key, layered_transparent, has_client); } static void wayland_shm_buffer_copy(struct wayland_shm_buffer *src, struct wayland_shm_buffer *dst, - HRGN region) + HRGN region, + int dwm_mode, const struct wayland_dwm_margins *margins, + BOOL has_color_key, COLORREF color_key, BOOL layered_transparent, + BOOL has_client) { RECT src_rect = {0, 0, src->width, src->height}; RECT dst_rect = {0, 0, dst->width, dst->height}; + BOOL force_opaque = (src->format == WL_SHM_FORMAT_XRGB8888 && dst->format == WL_SHM_FORMAT_ARGB8888); TRACE("src=%p dst=%p\n", src, dst); - copy_pixel_region(src->map_data, &src_rect, dst->map_data, &dst_rect, region, - src->format == WL_SHM_FORMAT_XRGB8888 && dst->format == WL_SHM_FORMAT_ARGB8888); + copy_pixel_region(src->map_data, &src_rect, dst->map_data, &dst_rect, region, force_opaque, + dwm_mode, margins, has_color_key, color_key, layered_transparent, has_client); } /********************************************************************** @@ -372,13 +435,50 @@ static BOOL wayland_window_surface_flush(struct window_surface *window_surface, const BITMAPINFO *color_info, const void *color_bits, BOOL shape_changed, const BITMAPINFO *shape_info, const void *shape_bits) { - RECT surface_rect = {.right = color_info->bmiHeader.biWidth, .bottom = abs(color_info->bmiHeader.biHeight)}; struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface); struct wayland_shm_buffer *shm_buffer = NULL, *latest_buffer; + struct wayland_win_data *data; BOOL flushed = FALSE; HRGN surface_damage_region = NULL; - HRGN copy_from_window_region; + HRGN copy_from_window_region = NULL; uint32_t buffer_format; + RECT surface_rect = {.right = color_info->bmiHeader.biWidth, .bottom = abs(color_info->bmiHeader.biHeight)}; + + DWORD ex_style = NtUserGetWindowLongW(window_surface->hwnd, GWL_EXSTYLE); + DWORD layered_flags = 0; + COLORREF color_key = 0; + BYTE alpha = 0; + + int dwm_mode = WAYLAND_DWM_EXTEND_NONE; + struct wayland_dwm_margins margins = {0}; + + BOOL layered = (ex_style & WS_EX_LAYERED) != 0; + BOOL transparent = (ex_style & WS_EX_TRANSPARENT) != 0; + BOOL layered_transparent = (ex_style & WS_EX_LAYERED) && (ex_style & WS_EX_TRANSPARENT); + BOOL layered_alpha = FALSE; + BOOL dwm_active = FALSE; + BOOL has_color_key = FALSE; + BOOL has_client = FALSE; + BOOL needs_alpha = FALSE; + BOOL force_opaque = FALSE; + + /* Sync DWM and Client state */ + if ((data = wayland_win_data_get(window_surface->hwnd))) + { + dwm_mode = data->dwm_mode; + margins = data->margins; + dwm_active = (dwm_mode != WAYLAND_DWM_EXTEND_NONE); + has_client = data->client_surface != NULL; + TRACE("surface_flush dwm_mode: %d\n", dwm_mode); + wayland_win_data_release(data); + } + + /* Extract Layered Attributes to detect LWA_ALPHA / LWA_COLORKEY */ + if (layered && NtUserGetLayeredWindowAttributes(window_surface->hwnd, &color_key, &alpha, &layered_flags)) + { + has_color_key = (layered_flags & LWA_COLORKEY) != 0; + layered_alpha = (layered_flags & LWA_ALPHA) != 0; + } surface_damage_region = NtGdiCreateRectRgn(rect->left + dirty->left, rect->top + dirty->top, rect->left + dirty->right, rect->top + dirty->bottom); @@ -388,7 +488,12 @@ static BOOL wayland_window_surface_flush(struct window_surface *window_surface, goto done; } - buffer_format = (shape_bits || wws->layered) ? WL_SHM_FORMAT_ARGB8888 : WL_SHM_FORMAT_XRGB8888; + /* DWM explicitly requires alpha. Layered requires alpha only if specific + * transparency attributes or click-through (Ghost) flags are set. */ + needs_alpha = dwm_active || (layered && (has_color_key || layered_alpha || transparent)) || shape_bits != NULL; + force_opaque = !needs_alpha; + buffer_format = needs_alpha ? WL_SHM_FORMAT_ARGB8888 : WL_SHM_FORMAT_XRGB8888; + if (wws->wayland_buffer_queue->format != buffer_format) { int width = wws->wayland_buffer_queue->width; @@ -422,8 +527,8 @@ static BOOL wayland_window_surface_flush(struct window_surface *window_surface, } NtGdiCombineRgn(copy_from_latest_region, shm_buffer->damage_region, surface_damage_region, RGN_DIFF); - wayland_shm_buffer_copy(latest_buffer, - shm_buffer, copy_from_latest_region); + wayland_shm_buffer_copy(latest_buffer, shm_buffer, copy_from_latest_region, + dwm_mode, &margins, has_color_key, color_key, layered_transparent, has_client); NtGdiDeleteObjectApp(copy_from_latest_region); } /* ... and use the window_surface as the source of pixel data contained @@ -439,8 +544,9 @@ static BOOL wayland_window_surface_flush(struct window_surface *window_surface, copy_from_window_region = shm_buffer->damage_region; } - wayland_shm_buffer_copy_data(shm_buffer, color_bits, &surface_rect, copy_from_window_region, - shape_bits && !wws->layered); + wayland_shm_buffer_copy_data(shm_buffer, color_bits, &surface_rect, copy_from_window_region, force_opaque, + dwm_mode, &margins, has_color_key, color_key, layered_transparent, has_client); + if (shape_bits) wayland_shm_buffer_copy_shape(shm_buffer, rect, shape_info, shape_bits); NtGdiSetRectRgn(shm_buffer->damage_region, 0, 0, 0, 0); @@ -498,10 +604,13 @@ static struct window_surface *wayland_window_surface_create(HWND hwnd, const REC if ((window_surface = window_surface_create(sizeof(*wws), &wayland_window_surface_funcs, hwnd, rect, info, 0))) { + DWORD ex_style = NtUserGetWindowLongW(hwnd, GWL_EXSTYLE); + BOOL layered_transparent = (ex_style & WS_EX_LAYERED) && (ex_style & WS_EX_TRANSPARENT); + struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface); wws->wayland_buffer_queue = wayland_buffer_queue_create(width, height, - layered ? WL_SHM_FORMAT_ARGB8888 : + (layered || layered_transparent) ? WL_SHM_FORMAT_ARGB8888 : WL_SHM_FORMAT_XRGB8888); wws->layered = layered; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10180