diff --git a/dlls/uxtheme/tests/system.c b/dlls/uxtheme/tests/system.c index 7f2e6d04aa4..b3b1afde14e 100644 --- a/dlls/uxtheme/tests/system.c +++ b/dlls/uxtheme/tests/system.c @@ -20,9 +20,15 @@ #include +#include "ntstatus.h" +#define WIN32_NO_STATUS + #include "windows.h" +#include "winternl.h" +#include "ddk/d3dkmthk.h" #include "vfwmsgs.h" #include "uxtheme.h" +#include "vsstyle.h" #include "msg.h" #include "wine/test.h" @@ -36,6 +42,14 @@ static HDC (WINAPI *pGetBufferedPaintDC)(HPAINTBUFFER); static HDC (WINAPI *pGetBufferedPaintTargetDC)(HPAINTBUFFER); static HRESULT (WINAPI *pGetBufferedPaintTargetRect)(HPAINTBUFFER, RECT *); +static LONG (WINAPI *pDisplayConfigGetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER *); +static LONG (WINAPI *pDisplayConfigSetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER *); +static BOOL (WINAPI *pGetDpiForMonitorInternal)(HMONITOR, UINT, UINT *, UINT *); +static DPI_AWARENESS_CONTEXT (WINAPI *pSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); + +static NTSTATUS (WINAPI *pD3DKMTCloseAdapter)(const D3DKMT_CLOSEADAPTER *); +static NTSTATUS (WINAPI *pD3DKMTOpenAdapterFromGdiDisplayName)(D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME *); + /* For message tests */ enum seq_index { @@ -48,20 +62,33 @@ static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; static void init_funcs(void) { - HMODULE hUxtheme = GetModuleHandleA("uxtheme.dll"); + HMODULE user32 = GetModuleHandleA("user32.dll"); + HMODULE gdi32 = GetModuleHandleA("gdi32.dll"); + HMODULE uxtheme = GetModuleHandleA("uxtheme.dll"); -#define UXTHEME_GET_PROC(func) p ## func = (void*)GetProcAddress(hUxtheme, #func) - UXTHEME_GET_PROC(BeginBufferedPaint); - UXTHEME_GET_PROC(BufferedPaintClear); - UXTHEME_GET_PROC(EndBufferedPaint); - UXTHEME_GET_PROC(GetBufferedPaintBits); - UXTHEME_GET_PROC(GetBufferedPaintDC); - UXTHEME_GET_PROC(GetBufferedPaintTargetDC); - UXTHEME_GET_PROC(GetBufferedPaintTargetRect); - UXTHEME_GET_PROC(BufferedPaintClear); +#define GET_PROC(module, func) \ + p##func = (void *)GetProcAddress(module, #func); \ + if (!p##func) \ + trace("GetProcAddress(%s, %s) failed.\n", #module, #func); - UXTHEME_GET_PROC(OpenThemeDataEx); -#undef UXTHEME_GET_PROC + GET_PROC(uxtheme, BeginBufferedPaint) + GET_PROC(uxtheme, BufferedPaintClear) + GET_PROC(uxtheme, EndBufferedPaint) + GET_PROC(uxtheme, GetBufferedPaintBits) + GET_PROC(uxtheme, GetBufferedPaintDC) + GET_PROC(uxtheme, GetBufferedPaintTargetDC) + GET_PROC(uxtheme, GetBufferedPaintTargetRect) + GET_PROC(uxtheme, OpenThemeDataEx) + + GET_PROC(user32, DisplayConfigGetDeviceInfo) + GET_PROC(user32, DisplayConfigSetDeviceInfo) + GET_PROC(user32, GetDpiForMonitorInternal) + GET_PROC(user32, SetThreadDpiAwarenessContext) + + GET_PROC(gdi32, D3DKMTCloseAdapter) + GET_PROC(gdi32, D3DKMTOpenAdapterFromGdiDisplayName) + +#undef GET_PROC } /* Try to make sure pending X events have been processed before continuing */ @@ -82,6 +109,113 @@ static void flush_events(void) } } +static unsigned int get_primary_monitor_effective_dpi(void) +{ + DPI_AWARENESS_CONTEXT old_context; + UINT dpi_x = 0, dpi_y = 0; + POINT point = {0, 0}; + HMONITOR monitor; + + if (pSetThreadDpiAwarenessContext && pGetDpiForMonitorInternal) + { + old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); + monitor = MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY); + pGetDpiForMonitorInternal(monitor, 0, &dpi_x, &dpi_y); + pSetThreadDpiAwarenessContext(old_context); + return dpi_y; + } + + return USER_DEFAULT_SCREEN_DPI; +} + +static int get_dpi_scale_index(int dpi) +{ + static const int scales[] = {100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500}; + int scale, scale_idx; + + scale = dpi * 100 / 96; + for (scale_idx = 0; scale_idx < ARRAY_SIZE(scales); ++scale_idx) + { + if (scales[scale_idx] == scale) + return scale_idx; + } + + return -1; +} + +static BOOL set_primary_monitor_effective_dpi(unsigned int primary_dpi) +{ + D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter_gdi_desc; + DISPLAYCONFIG_GET_SOURCE_DPI_SCALE get_scale_req; + DISPLAYCONFIG_SET_SOURCE_DPI_SCALE set_scale_req; + int current_scale_idx, target_scale_idx; + D3DKMT_CLOSEADAPTER close_adapter_desc; + BOOL ret = FALSE; + LONG error; + +#define CHECK_FUNC(func) \ + if (!p##func) \ + { \ + skip("%s() is unavailable.\n", #func); \ + return FALSE; \ + } + + CHECK_FUNC(D3DKMTCloseAdapter) + CHECK_FUNC(D3DKMTOpenAdapterFromGdiDisplayName) + CHECK_FUNC(DisplayConfigGetDeviceInfo) + CHECK_FUNC(DisplayConfigSetDeviceInfo) + +#undef CHECK_FUNC + + lstrcpyW(open_adapter_gdi_desc.DeviceName, L"\\\\.\\DISPLAY1"); + if (pD3DKMTOpenAdapterFromGdiDisplayName(&open_adapter_gdi_desc) == STATUS_PROCEDURE_NOT_FOUND) + { + win_skip("D3DKMTOpenAdapterFromGdiDisplayName() is unavailable.\n"); + return FALSE; + } + + get_scale_req.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_DPI_SCALE; + get_scale_req.header.size = sizeof(get_scale_req); + get_scale_req.header.adapterId = open_adapter_gdi_desc.AdapterLuid; + get_scale_req.header.id = open_adapter_gdi_desc.VidPnSourceId; + error = pDisplayConfigGetDeviceInfo(&get_scale_req.header); + if (error != NO_ERROR) + { + skip("DisplayConfigGetDeviceInfo failed, returned %d.\n", error); + goto failed; + } + + current_scale_idx = get_dpi_scale_index(get_primary_monitor_effective_dpi()); + if (current_scale_idx == -1) + { + skip("Failed to find current scale index.\n"); + goto failed; + } + + target_scale_idx = get_dpi_scale_index(primary_dpi); + if (target_scale_idx < get_scale_req.minRelativeScaleStep + || target_scale_idx > get_scale_req.maxRelativeScaleStep) + { + skip("DPI %d is not available.\n", primary_dpi); + goto failed; + } + + set_scale_req.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_SOURCE_DPI_SCALE; + set_scale_req.header.size = sizeof(set_scale_req); + set_scale_req.header.adapterId = open_adapter_gdi_desc.AdapterLuid; + set_scale_req.header.id = open_adapter_gdi_desc.VidPnSourceId; + set_scale_req.relativeScaleStep = get_scale_req.curRelativeScaleStep + (target_scale_idx - current_scale_idx); + error = pDisplayConfigSetDeviceInfo(&set_scale_req.header); + if (error == NO_ERROR) + ret = get_primary_monitor_effective_dpi() == primary_dpi; + flush_events(); + +failed: + close_adapter_desc.hAdapter = open_adapter_gdi_desc.hAdapter; + pD3DKMTCloseAdapter(&close_adapter_desc); + return ret; +} + static LRESULT WINAPI TestSetWindowThemeParentProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter; @@ -827,6 +961,115 @@ todo_wine DeleteDC(target); } +static void test_GetThemePartSize(void) +{ + DPI_AWARENESS_CONTEXT old_context; + unsigned int old_dpi, current_dpi; + HTHEME htheme = NULL; + HWND hwnd = NULL; + SIZE size, size2; + HRESULT hr; + HDC hdc; + + if (!pSetThreadDpiAwarenessContext) + { + win_skip("SetThreadDpiAwarenessContext is unavailable.\n"); + return; + } + + old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + current_dpi = get_primary_monitor_effective_dpi(); + old_dpi = current_dpi; + /* DPI needs to be 50% larger than 96 to avoid the effect of TrueSizeStretchMark */ + if (current_dpi < 192 && !set_primary_monitor_effective_dpi(192)) + { + skip("Failed to set primary monitor dpi to 192.\n"); + goto done; + } + + hwnd = CreateWindowA("Button", "Test", WS_POPUP, 100, 100, 100, 100, NULL, NULL, NULL, NULL); + htheme = OpenThemeData(hwnd, WC_BUTTONW); + if (!htheme) + { + skip("Theming is inactive.\n"); + goto done; + } + + hdc = GetDC(hwnd); + hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + todo_wine + ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", + size.cx, size.cy, size2.cx, size2.cy); + ReleaseDC(hwnd, hdc); + + /* Test that theme part size doesn't change even if DPI is changed */ + if (!set_primary_monitor_effective_dpi(96)) + { + skip("Failed to set primary monitor dpi to 96.\n"); + goto done; + } + + hdc = GetDC(hwnd); + hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx, + size.cy, size2.cx, size2.cy); + hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx, + size.cy, size2.cx, size2.cy); + + /* Test that theme part size changes after DPI is changed and theme handle is reopened. + * If DPI awareness context is not DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, theme part size + * still doesn't change, even after the theme handle is reopened. */ + CloseThemeData(htheme); + htheme = OpenThemeData(hwnd, WC_BUTTONW); + + hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + ok(size2.cx != size.cx || size2.cy != size.cy, "Expected size not equal to %dx%d.\n", size.cx, + size.cy); + hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + ok(size2.cx != size.cx || size2.cy != size.cy, "Expected size not equal to %dx%d.\n", size.cx, + size.cy); + ReleaseDC(hwnd, hdc); + + /* Test that OpenThemeData() without a window will assume the DPI is 96 */ + if (!set_primary_monitor_effective_dpi(192)) + { + skip("Failed to set primary monitor dpi to 192.\n"); + goto done; + } + + CloseThemeData(htheme); + htheme = OpenThemeData(NULL, WC_BUTTONW); + size = size2; + + hdc = GetDC(hwnd); + hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx, + size.cy, size2.cx, size2.cy); + hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2); + ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr); + ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx, + size.cy, size2.cx, size2.cy); + ReleaseDC(hwnd, hdc); + +done: + if (hwnd) + DestroyWindow(hwnd); + if (htheme) + CloseThemeData(htheme); + if (get_primary_monitor_effective_dpi() != old_dpi) + set_primary_monitor_effective_dpi(old_dpi); + pSetThreadDpiAwarenessContext(old_context); +} + START_TEST(system) { init_funcs(); @@ -842,6 +1085,7 @@ START_TEST(system) test_OpenThemeData(); test_OpenThemeDataEx(); test_GetCurrentThemeName(); + test_GetThemePartSize(); test_CloseThemeData(); test_buffered_paint(); }