Original patch description https://www.winehq.org/pipermail/wine-patches/2017-February/157574.html
winehq.org no longer provides the wine-patches archives, so it's necessary to dig into [web.archive.org](https://web.archive.org/web/20211011014926/https://www.winehq.org/pipermail/...):
https://web.archive.org/web/20211011014926/https://www.winehq.org/pipermail/...
https://web.archive.org/web/20211011014930/https://www.winehq.org/pipermail/...
===================================================================
Based on a patch by Dmitry Timoshkov.
## Signed-off-by: Sebastian Lackner sebastian@fds-team.de
Fixes https://bugs.winehq.org/show_bug.cgi?id=33943.
Battle.Net initially creates completely transparent popup windows and afterwards uses UpdateLayeredWindow without hdcSrc to let them fade in. Testing confirms that this works fine on Windows - on Wine however the original content is lost and no longer available. To avoid that, this patch stores the original content in the surface and uses _NET_WM_WINDOW_OPACITY to create a transparency effect. The same method is already used by SetLayeredWindowAttributes. If compositing is disabled, the window content will be visible immediately.
Dmitrys original patch used a different approach, and manually implemented the alpha blending in x11drv_surface_flush using additional buffers, however this shouldn't really be necessary (yet). We can still use it later if there is any benefit compared to using _NET_WM_WINDOW_OPACITY.
-- v3: winex11: Fix alpha blending in X11DRV_UpdateLayeredWindow.
From: Sebastian Lackner sebastian@fds-team.de
Based on a patch by Dmitry Timoshkov.
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/win32u/driver.c | 6 +++--- dlls/win32u/window.c | 9 ++++++--- dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/window.c | 2 +- dlls/winex11.drv/window.c | 6 +++++- dlls/winex11.drv/x11drv.h | 2 +- include/wine/gdi_driver.h | 2 +- 7 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 22e48fa6782..5edac6a0c8a 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -862,7 +862,7 @@ static LRESULT nulldrv_SysCommand( HWND hwnd, WPARAM wparam, LPARAM lparam, cons return -1; }
-static void nulldrv_UpdateLayeredWindow( HWND hwnd, UINT flags ) +static void nulldrv_UpdateLayeredWindow( HWND hwnd, BYTE alpha, UINT flags ) { }
@@ -1221,9 +1221,9 @@ static void loaderdrv_SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw ) load_driver()->pSetWindowRgn( hwnd, hrgn, redraw ); }
-static void loaderdrv_UpdateLayeredWindow( HWND hwnd, UINT flags ) +static void loaderdrv_UpdateLayeredWindow( HWND hwnd, BYTE alpha, UINT flags ) { - load_driver()->pUpdateLayeredWindow( hwnd, flags ); + load_driver()->pUpdateLayeredWindow( hwnd, alpha, flags ); }
static UINT loaderdrv_VulkanInit( UINT version, void *vulkan_handle, const struct vulkan_driver_funcs **driver_funcs ) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 98e2e46e67d..38780fc0cba 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -2481,7 +2481,11 @@ BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_ apply_window_pos( hwnd, 0, swp_flags, surface, &new_rects, NULL ); if (!surface) return FALSE;
- if (!hdc_src || surface == &dummy_surface) ret = TRUE; + if (!hdc_src || surface == &dummy_surface) + { + user_driver->pUpdateLayeredWindow( hwnd, blend->SourceConstantAlpha, flags ); + ret = TRUE; + } else { BLENDFUNCTION src_blend = { AC_SRC_OVER, 0, 255, 0 }; @@ -2502,7 +2506,6 @@ BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_ if (pts_src) OffsetRect( &src_rect, pts_src->x, pts_src->y ); NtGdiTransformPoints( hdc_src, (POINT *)&src_rect, (POINT *)&src_rect, 2, NtGdiDPtoLP );
- if (flags & ULW_ALPHA) src_blend = *blend; ret = NtGdiAlphaBlend( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdc_src, src_rect.left, src_rect.top, src_rect.right - src_rect.left, src_rect.bottom - src_rect.top, *(DWORD *)&src_blend, 0 ); @@ -2514,7 +2517,7 @@ BOOL WINAPI NtUserUpdateLayeredWindow( HWND hwnd, HDC hdc_dst, const POINT *pts_ if (!(flags & ULW_COLORKEY)) key = CLR_INVALID; window_surface_set_layered( surface, key, -1, 0xff000000 );
- user_driver->pUpdateLayeredWindow( hwnd, flags ); + user_driver->pUpdateLayeredWindow( hwnd, blend->SourceConstantAlpha, flags ); window_surface_flush( surface ); }
diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 4457a7e186c..4673d69654a 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -145,7 +145,7 @@ extern void macdrv_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alph extern void macdrv_SetWindowText(HWND hwnd, LPCWSTR text); extern UINT macdrv_ShowWindow(HWND hwnd, INT cmd, RECT *rect, UINT swp); extern LRESULT macdrv_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam, const POINT *pos); -extern void macdrv_UpdateLayeredWindow(HWND hwnd, UINT flags); +extern void macdrv_UpdateLayeredWindow(HWND hwnd, BYTE alpha, UINT flags); extern LRESULT macdrv_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); extern BOOL macdrv_WindowPosChanging(HWND hwnd, UINT swp_flags, BOOL shaped, const struct window_rects *rects); extern BOOL macdrv_GetWindowStyleMasks(HWND hwnd, UINT style, UINT ex_style, UINT *style_mask, UINT *ex_style_mask); diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 05126678264..8d052aa750b 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -1627,7 +1627,7 @@ done: /*********************************************************************** * UpdateLayeredWindow (MACDRV.@) */ -void macdrv_UpdateLayeredWindow(HWND hwnd, UINT flags) +void macdrv_UpdateLayeredWindow(HWND hwnd, BYTE alpha, UINT flags) { struct macdrv_win_data *data;
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 615fb9da086..28026bbbfc3 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3207,12 +3207,16 @@ void X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWO /*********************************************************************** * UpdateLayeredWindow (X11DRV.@) */ -void X11DRV_UpdateLayeredWindow( HWND hwnd, UINT flags ) +void X11DRV_UpdateLayeredWindow( HWND hwnd, BYTE alpha, UINT flags ) { struct x11drv_win_data *data; BOOL mapped;
if (!(data = get_win_data( hwnd ))) return; + + if (data->whole_window) + sync_window_opacity( data->display, data->whole_window, alpha, flags ); + mapped = data->desired_state.wm_state != WithdrawnState; release_win_data( data );
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 91811cfd766..a5bab864ad9 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -239,7 +239,7 @@ extern UINT X11DRV_ShowWindow( HWND hwnd, INT cmd, RECT *rect, UINT swp ); extern LRESULT X11DRV_SysCommand( HWND hwnd, WPARAM wparam, LPARAM lparam, const POINT *pos ); extern LRESULT X11DRV_ClipboardWindowProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ); extern void X11DRV_UpdateClipboard(void); -extern void X11DRV_UpdateLayeredWindow( HWND hwnd, UINT flags ); +extern void X11DRV_UpdateLayeredWindow( HWND hwnd, BYTE alpha, UINT flags ); extern LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ); extern BOOL X11DRV_WindowPosChanging( HWND hwnd, UINT swp_flags, BOOL shaped, const struct window_rects *rects ); extern BOOL X11DRV_GetWindowStyleMasks( HWND hwnd, UINT style, UINT ex_style, UINT *style_mask, UINT *ex_style_mask ); diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 51a095fd2b0..2774ab6e53a 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -381,7 +381,7 @@ struct user_driver_funcs void (*pSetWindowText)(HWND,LPCWSTR); UINT (*pShowWindow)(HWND,INT,RECT*,UINT); LRESULT (*pSysCommand)(HWND,WPARAM,LPARAM,const POINT*); - void (*pUpdateLayeredWindow)(HWND,UINT); + void (*pUpdateLayeredWindow)(HWND,BYTE,UINT); LRESULT (*pWindowMessage)(HWND,UINT,WPARAM,LPARAM); BOOL (*pWindowPosChanging)(HWND,UINT,BOOL,const struct window_rects *); BOOL (*pGetWindowStyleMasks)(HWND,UINT,UINT,UINT*,UINT*);
v3: Removed undesirable change.
Rémi Bernon (@rbernon) commented about dlls/win32u/window.c:
if (pts_src) OffsetRect( &src_rect, pts_src->x, pts_src->y ); NtGdiTransformPoints( hdc_src, (POINT *)&src_rect, (POINT *)&src_rect, 2, NtGdiDPtoLP );
if (flags & ULW_ALPHA) src_blend = *blend;
I'm not really sure we want to rely on backend specific window opacity mechanisms for this kind of thing, as the idea was instead to move toward having Wine-managed surfaces that would just be composited together, although the changes here are probably simple enough to be acceptable until we have something better.
However, this removes the existing window-wide transparency that is currently stored in the window surface pixel alpha, to replace it with backend-specific window opacity but it only does it for the X11 backend. This needs to be implemented with other backends too if we go that way.
On Wed Mar 12 13:20:37 2025 +0000, Rémi Bernon wrote:
I'm not really sure we want to rely on backend specific window opacity mechanisms for this kind of thing, as the idea was instead to move toward having Wine-managed surfaces that would just be composited together, although the changes here are probably simple enough to be acceptable until we have something better. However, this removes the existing window-wide transparency that is currently stored in the window surface pixel alpha, to replace it with backend-specific window opacity but it only does it for the X11 backend. This needs to be implemented with other backends too if we go that way.
As explained in the patch description same method is already used by SetLayeredWindowAttributes(), so if there's a problem with this approach it already exists, besides this fixes a real bug.
On Wed Mar 12 14:37:40 2025 +0000, Dmitry Timoshkov wrote:
As explained in the patch description same method is already used by SetLayeredWindowAttributes(), so if there's a problem with this approach it already exists, besides this fixes a real bug.
Sure, well the second part of my comment still needs to be addressed.
On Wed Mar 12 16:01:59 2025 +0000, Rémi Bernon wrote:
Sure, well the second part of my comment still needs to be addressed.
Do you mean adding support for alpha parameter in other UpdateLayeredWindow() driver backends?
For winemac.drv it seems that replacing
`sync_window_opacity(data, 255, TRUE, flags);`
`by`
`sync_window_opacity(data, alpha, FALSE, flags);`
at https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/winemac.drv/window.c?...
would work, however I have no Mac to test this.
winewayland.drv and wineandroid.drv don't seem to have the UpdateLayeredWindow() backend implementation.
What would be the best way forward here? Should I resend the patch with proposed winemac.drv changes, or something else?
On Tue Mar 18 12:12:42 2025 +0000, Dmitry Timoshkov wrote:
What would be the best way forward here? Should I resend the patch with proposed winemac.drv changes, or something else?
Something like that, although it might need the per pixel alpha parameter set to TRUE. I'm not completely sure though, but I can test on a mac when I find some time.
On Tue Mar 18 12:12:42 2025 +0000, Rémi Bernon wrote:
Something like that, although it might need the per pixel alpha parameter set to TRUE. I'm not completely sure though, but I can test on a mac when I find some time.
Other places in dlls/winemac.drv/window.c including macdrv_SetLayeredWindowAttributes() call sync_window_opacity() with per_pixel_alpha set to FALSE.