[PATCH 2/5] uxtheme: Hook DefDlgProc() for dialog theming.

Zhiyi Zhang zzhang at codeweavers.com
Mon Jan 24 01:47:07 CST 2022


Fix controls on OpenMPT's channel setting dialog having incorrect background.

Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 dlls/user32/defdlg.c        |  34 +++++--
 dlls/user32/hook.c          |   1 +
 dlls/user32/user_private.h  |   1 +
 dlls/uxtheme/Makefile.in    |   1 +
 dlls/uxtheme/dialog.c       | 176 ++++++++++++++++++++++++++++++++++++
 dlls/uxtheme/system.c       |   1 +
 dlls/uxtheme/tests/system.c |  20 +---
 dlls/uxtheme/uxthemedll.h   |   1 +
 include/winuser.h           |   1 +
 9 files changed, 210 insertions(+), 26 deletions(-)
 create mode 100644 dlls/uxtheme/dialog.c

diff --git a/dlls/user32/defdlg.c b/dlls/user32/defdlg.c
index 81c7cb0f207..4f44a93cd49 100644
--- a/dlls/user32/defdlg.c
+++ b/dlls/user32/defdlg.c
@@ -354,10 +354,7 @@ out:
     return dlgInfo;
 }
 
-/***********************************************************************
- *              DefDlgProcA (USER32.@)
- */
-LRESULT WINAPI DefDlgProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
+static LRESULT USER_DefDlgProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
 {
     DIALOGINFO *dlgInfo;
     DLGPROC dlgproc;
@@ -411,10 +408,7 @@ LRESULT WINAPI DefDlgProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
     return GetWindowLongPtrW( hwnd, DWLP_MSGRESULT );
 }
 
-/***********************************************************************
- *              DefDlgProcW (USER32.@)
- */
-LRESULT WINAPI DefDlgProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
+static LRESULT USER_DefDlgProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
 {
     DIALOGINFO *dlgInfo;
     DLGPROC dlgproc;
@@ -467,3 +461,27 @@ LRESULT WINAPI DefDlgProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
 
     return GetWindowLongPtrW( hwnd, DWLP_MSGRESULT );
 }
+
+LRESULT WINAPI USER_DefDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
+{
+    if (unicode)
+        return USER_DefDlgProcW( hwnd, msg, wParam, lParam );
+    else
+        return USER_DefDlgProcA( hwnd, msg, wParam, lParam );
+}
+
+/***********************************************************************
+ *              DefDlgProcA (USER32.@)
+ */
+LRESULT WINAPI DefDlgProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+    return user_api->pDefDlgProc( hwnd, msg, wParam, lParam, FALSE );
+}
+
+/***********************************************************************
+ *              DefDlgProcW (USER32.@)
+ */
+LRESULT WINAPI DefDlgProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+    return user_api->pDefDlgProc( hwnd, msg, wParam, lParam, TRUE );
+}
diff --git a/dlls/user32/hook.c b/dlls/user32/hook.c
index 159797020f2..f5b4c6ab8a1 100644
--- a/dlls/user32/hook.c
+++ b/dlls/user32/hook.c
@@ -83,6 +83,7 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 static struct user_api_hook original_user_api =
 {
+    USER_DefDlgProc,
     USER_ScrollBarDraw,
     USER_ScrollBarProc,
 };
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index 6923ddb2906..7a9845adeb9 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -303,6 +303,7 @@ extern BOOL get_icon_size( HICON handle, SIZE *size ) DECLSPEC_HIDDEN;
 #endif
 
 extern struct user_api_hook *user_api DECLSPEC_HIDDEN;
+LRESULT WINAPI USER_DefDlgProc(HWND, UINT, WPARAM, LPARAM, BOOL) DECLSPEC_HIDDEN;
 LRESULT WINAPI USER_ScrollBarProc(HWND, UINT, WPARAM, LPARAM, BOOL) DECLSPEC_HIDDEN;
 void WINAPI USER_ScrollBarDraw(HWND, HDC, INT, enum SCROLL_HITTEST,
                                const struct SCROLL_TRACKING_INFO *, BOOL, BOOL, RECT *, INT, INT,
diff --git a/dlls/uxtheme/Makefile.in b/dlls/uxtheme/Makefile.in
index 01751d3243a..933650f4261 100644
--- a/dlls/uxtheme/Makefile.in
+++ b/dlls/uxtheme/Makefile.in
@@ -5,6 +5,7 @@ DELAYIMPORTS = msimg32
 
 C_SRCS = \
 	buffer.c \
+	dialog.c \
 	draw.c \
 	main.c \
 	metric.c \
diff --git a/dlls/uxtheme/dialog.c b/dlls/uxtheme/dialog.c
new file mode 100644
index 00000000000..87c2b7d1c13
--- /dev/null
+++ b/dlls/uxtheme/dialog.c
@@ -0,0 +1,176 @@
+/*
+ * Dialog theming support
+ *
+ * Copyright 2022 Zhiyi Zhang for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "uxtheme.h"
+#include "uxthemedll.h"
+#include "vssym32.h"
+
+extern ATOM atDialogThemeEnabled;
+static const WCHAR wine_dialog_brush[] = L"wine_dialog_brush";
+
+static HBRUSH get_dialog_background_brush(HWND hwnd, BOOL create)
+{
+    HBITMAP bitmap, old_bitmap;
+    HDC hdc, hdc_screen;
+    HBRUSH brush;
+    HTHEME theme;
+    DWORD flag;
+    HRESULT hr;
+    RECT rect;
+    SIZE size;
+
+    if (!IsThemeActive())
+        return NULL;
+
+    flag = HandleToUlong(GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled)));
+    if (flag != ETDT_ENABLETAB && flag != ETDT_ENABLEAEROWIZARDTAB)
+        return NULL;
+
+    brush = GetPropW(hwnd, wine_dialog_brush);
+    if (brush)
+        return brush;
+
+    if (!create)
+        return NULL;
+
+    theme = OpenThemeData(NULL, L"Tab");
+    if (!theme)
+        return NULL;
+
+    hr = GetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
+    if (FAILED(hr))
+    {
+        size.cx = 10;
+        size.cy = 600;
+    }
+
+    hdc_screen = GetDC(NULL);
+    hdc = CreateCompatibleDC(hdc_screen);
+    bitmap = CreateCompatibleBitmap(hdc_screen, size.cx, size.cy);
+    old_bitmap = SelectObject(hdc, bitmap);
+
+    SetRect(&rect, 0, 0, size.cx, size.cy);
+    /* FIXME: XP draws the tab body bitmap directly without transparency even if there is */
+    FillRect(hdc, &rect, GetSysColorBrush(COLOR_3DFACE));
+    hr = DrawThemeBackground(theme, hdc, TABP_BODY, 0, &rect, NULL);
+    if (SUCCEEDED(hr))
+    {
+        brush = CreatePatternBrush(bitmap);
+        SetPropW(hwnd, wine_dialog_brush, brush);
+    }
+
+    SelectObject(hdc, old_bitmap);
+    DeleteDC(hdc);
+    ReleaseDC(NULL, hdc_screen);
+    CloseThemeData(theme);
+    return brush;
+}
+
+static void destroy_dialog_brush(HWND hwnd)
+{
+    LOGBRUSH logbrush;
+    HBRUSH brush;
+
+    brush = GetPropW(hwnd, wine_dialog_brush);
+    if (brush)
+    {
+        RemovePropW(hwnd, wine_dialog_brush);
+        if (GetObjectW(brush, sizeof(logbrush), &logbrush) == sizeof(logbrush))
+            DeleteObject((HBITMAP)logbrush.lbHatch);
+        DeleteObject(brush);
+    }
+}
+
+LRESULT WINAPI UXTHEME_DefDlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, BOOL unicode)
+{
+    POINT org, old_org;
+    WNDPROC dlgproc;
+    HBRUSH brush;
+    LRESULT lr;
+    RECT rect;
+    HDC hdc;
+
+    switch (msg)
+    {
+    case WM_NCDESTROY:
+    {
+        destroy_dialog_brush(hwnd);
+        break;
+    }
+    case WM_THEMECHANGED:
+    {
+        destroy_dialog_brush(hwnd);
+        InvalidateRect(hwnd, NULL, TRUE);
+        break;
+    }
+    case WM_ERASEBKGND:
+    {
+        dlgproc = (WNDPROC)GetWindowLongPtrW(hwnd, DWLP_DLGPROC);
+        lr = CallWindowProcW(dlgproc, hwnd, msg, wp, lp);
+        if (lr)
+            return lr;
+
+        brush = get_dialog_background_brush(hwnd, TRUE);
+        if (!brush)
+            break;
+
+        /* Using FillRect() to draw background could introduce a tiling effect if the destination
+         * rectangle is larger than the pattern brush size, which is usually 10x600. This bug is
+         * visible on property sheet pages if system DPI is set to 192. However, the same bug also
+         * exists on XP and explains why vista+ don't use gradient tab body background anymore */
+        hdc = (HDC)wp;
+        GetViewportOrgEx(hdc, &org);
+        SetBrushOrgEx(hdc, org.x, org.y, &old_org);
+        GetClientRect(hwnd, &rect);
+        FillRect(hdc, &rect, brush);
+        SetBrushOrgEx(hdc, old_org.x, old_org.y, NULL);
+        return TRUE;
+    }
+    case WM_CTLCOLORSTATIC:
+    {
+        dlgproc = (WNDPROC)GetWindowLongPtrW(hwnd, DWLP_DLGPROC);
+        lr = CallWindowProcW(dlgproc, hwnd, msg, wp, lp);
+        if (lr)
+            return lr;
+
+        brush = get_dialog_background_brush(hwnd, FALSE);
+        if (!brush)
+            break;
+
+        hdc = (HDC)wp;
+        SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
+        SetBkMode(hdc, TRANSPARENT);
+
+        org.x = 0;
+        org.y = 0;
+        MapWindowPoints((HWND)lp, hwnd, &org, 1);
+        SetBrushOrgEx(hdc, -org.x, -org.y, NULL);
+        return (LRESULT)brush;
+    }
+    }
+
+    return user_api.pDefDlgProc(hwnd, msg, wp, lp, unicode);
+}
diff --git a/dlls/uxtheme/system.c b/dlls/uxtheme/system.c
index 61a608e5962..37c484387ec 100644
--- a/dlls/uxtheme/system.c
+++ b/dlls/uxtheme/system.c
@@ -1245,6 +1245,7 @@ BOOL WINAPI ThemeHooksInstall(void)
 {
     struct user_api_hook hooks;
 
+    hooks.pDefDlgProc = UXTHEME_DefDlgProc;
     hooks.pScrollBarDraw = UXTHEME_ScrollBarDraw;
     hooks.pScrollBarWndProc = UXTHEME_ScrollbarWndProc;
     return RegisterUserApiHook(&hooks, &user_api);
diff --git a/dlls/uxtheme/tests/system.c b/dlls/uxtheme/tests/system.c
index 4a5a6d291fc..540cd7d2fcd 100644
--- a/dlls/uxtheme/tests/system.c
+++ b/dlls/uxtheme/tests/system.c
@@ -1772,7 +1772,6 @@ static void test_EnableThemeDialogTexture(void)
     lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
     ok(lr != 0, "WM_ERASEBKGND failed.\n");
     brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
-    todo_wine
     ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected brush changed.\n");
 
     /* Test disabling theme dialog texture should change the brush immediately */
@@ -1780,7 +1779,6 @@ static void test_EnableThemeDialogTexture(void)
     hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
     ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
     brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
-    todo_wine
     ok(brush2 != brush, "Expected a different brush.\n");
     ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
        GetSysColorBrush(COLOR_BTNFACE), brush2);
@@ -1799,7 +1797,6 @@ static void test_EnableThemeDialogTexture(void)
     hr = EnableThemeDialogTexture(dialog, ETDT_USETABTEXTURE);
     ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
     brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
-    todo_wine
     ok(brush2 != brush, "Expected a different brush.\n");
 
     /* Test ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE should change the brush immediately */
@@ -1811,7 +1808,6 @@ static void test_EnableThemeDialogTexture(void)
     if (LOBYTE(LOWORD(GetVersion())) < 6)
         ok(brush2 == brush, "Expected the same brush.\n");
     else
-        todo_wine
         ok(brush2 != brush, "Expected a different brush.\n");
 
     hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
@@ -1832,7 +1828,6 @@ static void test_EnableThemeDialogTexture(void)
     SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
     ret = GetBrushOrgEx(child_hdc, &org);
     ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
-    todo_wine
     ok(org.x == -1 && org.y == -2, "Expected (-1,-2), got %s.\n", wine_dbgstr_point(&org));
 
     /* Test that WM_CTLCOLORSTATIC changes background mode when dialog texture is on */
@@ -1840,7 +1835,6 @@ static void test_EnableThemeDialogTexture(void)
     ok(old_mode != 0, "SetBkMode failed.\n");
     SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
     mode = SetBkMode(child_hdc, old_mode);
-    todo_wine
     ok(mode == TRANSPARENT, "Expected mode %#x, got %#x.\n", TRANSPARENT, mode);
 
     /* Test that WM_CTLCOLORSTATIC changes background color when dialog texture is on */
@@ -1859,15 +1853,12 @@ static void test_EnableThemeDialogTexture(void)
     memset(&log_brush, 0, sizeof(log_brush));
     count = GetObjectA(brush, sizeof(log_brush), &log_brush);
     ok(count == sizeof(log_brush), "GetObjectA failed, error %u.\n", GetLastError());
-    todo_wine
     ok(log_brush.lbColor == 0, "Expected brush color %#x, got %#x.\n", 0, log_brush.lbColor);
-    todo_wine
     ok(log_brush.lbStyle == BS_PATTERN, "Expected brush style %#x, got %#x.\n", BS_PATTERN,
        log_brush.lbStyle);
 
     memset(&bmp, 0, sizeof(bmp));
     count = GetObjectA((HBITMAP)log_brush.lbHatch, sizeof(bmp), &bmp);
-    todo_wine
     ok(count == sizeof(bmp), "GetObjectA failed, error %u.\n", GetLastError());
 
     theme = OpenThemeData(NULL, L"Tab");
@@ -1877,16 +1868,13 @@ static void test_EnableThemeDialogTexture(void)
     size.cy = 0;
     hr = GetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
     ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
-    todo_wine
     ok(bmp.bmWidth == size.cx, "Expected width %d, got %d.\n", size.cx, bmp.bmWidth);
-    todo_wine
     ok(bmp.bmHeight == size.cy, "Expected height %d, got %d.\n", size.cy, bmp.bmHeight);
 
     CloseThemeData(theme);
 
     /* Test that DefDlgProcA/W() are hooked for WM_CTLCOLORSTATIC */
     brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
-    todo_wine
     ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
     brush2 = (HBRUSH)DefDlgProcW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
     ok(brush2 == brush, "Expected the same brush.\n");
@@ -1895,11 +1883,12 @@ static void test_EnableThemeDialogTexture(void)
 
     /* Test that DefWindowProcA/W() are also hooked for WM_CTLCOLORSTATIC */
     brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
-    todo_wine
     ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
     brush2 = (HBRUSH)DefWindowProcW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+    todo_wine
     ok(brush2 == brush, "Expected the same brush.\n");
     brush2 = (HBRUSH)DefWindowProcA(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
+    todo_wine
     ok(brush2 == brush, "Expected the same brush.\n");
 
     /* Test that DefWindowProcA/W() are not hooked for WM_ERASEBKGND. So the background is still
@@ -1951,13 +1940,11 @@ static void test_EnableThemeDialogTexture(void)
     SetLastError(0xdeadbeef);
     ret = GetObjectA(brush, sizeof(log_brush), &log_brush);
     error = GetLastError();
-    todo_wine
     ok(!ret || broken(ret) /* XP */, "GetObjectA succeeded.\n");
     todo_wine
     ok(error == ERROR_INVALID_PARAMETER || broken(error == 0xdeadbeef) /* XP */,
        "Expected error %u, got %u.\n", ERROR_INVALID_PARAMETER, error);
     ret = DeleteObject(brush);
-    todo_wine
     ok(!ret || broken(ret) /* XP */, "DeleteObject succeeded.\n");
 
     /* Should still report the same brush handle after the brush handle was freed */
@@ -1971,7 +1958,6 @@ static void test_EnableThemeDialogTexture(void)
     lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
     ok(lr == 0, "WM_THEMECHANGED failed.\n");
     brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
-    todo_wine
     ok(brush2 != brush, "Expected a different brush.\n");
 
     ReleaseDC(child, child_hdc);
@@ -2027,7 +2013,6 @@ static void test_EnableThemeDialogTexture(void)
             ok(lr != 0, "WM_ERASEBKGND failed.\n");
             brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
             if (flags[i] == ETDT_ENABLETAB || flags[i] == ETDT_ENABLEAEROWIZARDTAB)
-                todo_wine
                 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
             else
                 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
@@ -2054,7 +2039,6 @@ static void test_EnableThemeDialogTexture(void)
                       || ((flags[i] | flags[j]) & ETDT_ENABLEAEROWIZARDTAB) == ETDT_ENABLEAEROWIZARDTAB)
                       && !((flags[i] | flags[j]) & ETDT_DISABLE)))
                  && (((flags[i] | flags[j]) & (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)) != (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)))
-                todo_wine
                 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
             else
                 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
diff --git a/dlls/uxtheme/uxthemedll.h b/dlls/uxtheme/uxthemedll.h
index c43ad703884..67130774392 100644
--- a/dlls/uxtheme/uxthemedll.h
+++ b/dlls/uxtheme/uxthemedll.h
@@ -106,6 +106,7 @@ extern HRESULT UXTHEME_SetActiveTheme(PTHEME_FILE tf) DECLSPEC_HIDDEN;
 extern void UXTHEME_UninitSystem(void) DECLSPEC_HIDDEN;
 
 extern struct user_api_hook user_api DECLSPEC_HIDDEN;
+LRESULT WINAPI UXTHEME_DefDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL unicode) DECLSPEC_HIDDEN;
 void WINAPI UXTHEME_ScrollBarDraw(HWND hwnd, HDC dc, INT bar, enum SCROLL_HITTEST hit_test,
                                   const struct SCROLL_TRACKING_INFO *tracking_info,
                                   BOOL draw_arrows, BOOL draw_interior, RECT *rect, INT arrowsize,
diff --git a/include/winuser.h b/include/winuser.h
index 1d37bd44344..898c9034e33 100644
--- a/include/winuser.h
+++ b/include/winuser.h
@@ -4435,6 +4435,7 @@ struct SCROLL_TRACKING_INFO
 
 struct user_api_hook
 {
+    LRESULT (WINAPI *pDefDlgProc)(HWND, UINT, WPARAM, LPARAM, BOOL);
     void (WINAPI *pScrollBarDraw)(HWND, HDC, INT, enum SCROLL_HITTEST,
                                   const struct SCROLL_TRACKING_INFO *, BOOL, BOOL, RECT *, INT, INT,
                                   INT, BOOL);
-- 
2.32.0




More information about the wine-devel mailing list