Sweden-Number/dlls/uxtheme/tests/system.c

2285 lines
91 KiB
C

/* Unit test suite for uxtheme API functions
*
* Copyright 2006 Paul Vriens
* Copyright 2021-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 "ntstatus.h"
#define WIN32_NO_STATUS
#include "windows.h"
#include "winternl.h"
#include "ddk/d3dkmthk.h"
#include "vfwmsgs.h"
#include "uxtheme.h"
#include "vssym32.h"
#include "msg.h"
#include "wine/test.h"
#include "v6util.h"
static HTHEME (WINAPI * pOpenThemeDataEx)(HWND, LPCWSTR, DWORD);
static HTHEME (WINAPI *pOpenThemeDataForDpi)(HWND, LPCWSTR, UINT);
static HPAINTBUFFER (WINAPI *pBeginBufferedPaint)(HDC, const RECT *, BP_BUFFERFORMAT, BP_PAINTPARAMS *, HDC *);
static HRESULT (WINAPI *pBufferedPaintClear)(HPAINTBUFFER, const RECT *);
static HRESULT (WINAPI *pEndBufferedPaint)(HPAINTBUFFER, BOOL);
static HRESULT (WINAPI *pGetBufferedPaintBits)(HPAINTBUFFER, RGBQUAD **, int *);
static HDC (WINAPI *pGetBufferedPaintDC)(HPAINTBUFFER);
static HDC (WINAPI *pGetBufferedPaintTargetDC)(HPAINTBUFFER);
static HRESULT (WINAPI *pGetBufferedPaintTargetRect)(HPAINTBUFFER, RECT *);
static HRESULT (WINAPI *pGetThemeIntList)(HTHEME, int, int, int, INTLIST *);
static HRESULT (WINAPI *pGetThemeTransitionDuration)(HTHEME, int, int, int, int, DWORD *);
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
{
PARENT_SEQ_INDEX,
CHILD_SEQ_INDEX,
NUM_MSG_SEQUENCES
};
static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
static void init_funcs(void)
{
HMODULE user32 = GetModuleHandleA("user32.dll");
HMODULE gdi32 = GetModuleHandleA("gdi32.dll");
HMODULE uxtheme = GetModuleHandleA("uxtheme.dll");
#define GET_PROC(module, func) \
p##func = (void *)GetProcAddress(module, #func); \
if (!p##func) \
trace("GetProcAddress(%s, %s) failed.\n", #module, #func);
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, GetThemeIntList)
GET_PROC(uxtheme, GetThemeTransitionDuration)
GET_PROC(uxtheme, OpenThemeDataEx)
GET_PROC(uxtheme, OpenThemeDataForDpi)
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 */
static void flush_events(void)
{
MSG msg;
int diff = 200;
int min_timeout = 100;
DWORD time = GetTickCount() + diff;
while (diff > 0)
{
if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT)
break;
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
DispatchMessageA(&msg);
diff = time - GetTickCount();
}
}
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 %ld.\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;
struct message msg = {0};
LRESULT ret;
/* Only care about WM_THEMECHANGED */
if (message != WM_THEMECHANGED)
return DefWindowProcA(hwnd, message, wParam, lParam);
msg.message = message;
msg.flags = sent | wparam | lparam;
if (defwndproc_counter)
msg.flags |= defwinproc;
msg.wParam = wParam;
msg.lParam = lParam;
add_message(sequences, PARENT_SEQ_INDEX, &msg);
InterlockedIncrement(&defwndproc_counter);
ret = DefWindowProcA(hwnd, message, wParam, lParam);
InterlockedDecrement(&defwndproc_counter);
return ret;
}
static LRESULT WINAPI TestSetWindowThemeChildProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static LONG defwndproc_counter;
struct message msg = {0};
LRESULT ret;
/* Only care about WM_THEMECHANGED */
if (message != WM_THEMECHANGED)
return DefWindowProcA(hwnd, message, wParam, lParam);
msg.message = message;
msg.flags = sent | wparam | lparam;
if (defwndproc_counter)
msg.flags |= defwinproc;
msg.wParam = wParam;
msg.lParam = lParam;
add_message(sequences, CHILD_SEQ_INDEX, &msg);
InterlockedIncrement(&defwndproc_counter);
ret = DefWindowProcA(hwnd, message, wParam, lParam);
InterlockedDecrement(&defwndproc_counter);
return ret;
}
static void test_IsThemed(void)
{
BOOL bThemeActive;
BOOL bAppThemed;
bThemeActive = IsThemeActive();
trace("Theming is %s\n", (bThemeActive) ? "active" : "inactive");
bAppThemed = IsAppThemed();
trace("Test executable is %s\n", (bAppThemed) ? "themed" : "not themed");
}
static void test_IsThemePartDefined(void)
{
BOOL is_theme_active, ret;
HTHEME theme = NULL;
DWORD error;
HWND hwnd;
int i;
static const struct
{
const WCHAR *class_name;
int part;
int state;
BOOL defined;
DWORD error;
}
tests[] =
{
{NULL, 0, 0, FALSE, E_HANDLE},
{NULL, 0xdeadbeef, 0, FALSE, E_HANDLE},
{L"Button", 0xdeadbeef, 0, FALSE, NO_ERROR},
{L"Button", BP_PUSHBUTTON, 0, TRUE, NO_ERROR},
{L"Button", BP_PUSHBUTTON, PBS_NORMAL, FALSE, NO_ERROR},
{L"Button", BP_PUSHBUTTON, PBS_DEFAULTED_ANIMATING, FALSE, NO_ERROR},
{L"Button", BP_PUSHBUTTON, PBS_DEFAULTED_ANIMATING + 1, FALSE, NO_ERROR},
{L"Edit", EP_EDITTEXT, 0, TRUE, NO_ERROR},
{L"Edit", EP_EDITTEXT, ETS_NORMAL, FALSE, NO_ERROR},
{L"Edit", EP_EDITTEXT, ETS_FOCUSED, FALSE, NO_ERROR},
};
is_theme_active = IsThemeActive();
hwnd = CreateWindowA(WC_STATICA, "", WS_POPUP, 0, 0, 1, 1, 0, 0, 0, NULL);
ok(hwnd != NULL, "CreateWindowA failed, error %#lx.\n", GetLastError());
for (i = 0; i < ARRAY_SIZE(tests); ++i)
{
if (!is_theme_active && tests[i].class_name)
continue;
winetest_push_context("class %s part %d state %d", wine_dbgstr_w(tests[i].class_name),
tests[i].part, tests[i].state);
if (tests[i].class_name)
{
theme = OpenThemeData(hwnd, tests[i].class_name);
ok(theme != NULL, "OpenThemeData failed, error %#lx.\n", GetLastError());
}
SetLastError(0xdeadbeef);
ret = IsThemePartDefined(theme, tests[i].part, tests[i].state);
error = GetLastError();
ok(ret == tests[i].defined, "Expected %d.\n", tests[i].defined);
ok(error == tests[i].error, "Expected %#lx, got %#lx.\n", tests[i].error, error);
if (theme)
{
CloseThemeData(theme);
theme = NULL;
}
winetest_pop_context();
}
DestroyWindow(hwnd);
}
static void test_GetWindowTheme(void)
{
HTHEME hTheme;
HWND hWnd;
SetLastError(0xdeadbeef);
hTheme = GetWindowTheme(NULL);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", GetLastError() );
/* Only do the bare minimum to get a valid hwnd */
hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
ok(hWnd != NULL, "Failed to create a test window.\n");
SetLastError(0xdeadbeef);
hTheme = GetWindowTheme(hWnd);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == 0xdeadbeef,
"Expected 0xdeadbeef, got 0x%08lx\n",
GetLastError());
DestroyWindow(hWnd);
}
static const struct message SetWindowThemeSeq[] =
{
{WM_THEMECHANGED, sent},
{0}
};
static const struct message EmptySeq[] =
{
{0}
};
static void test_SetWindowTheme(void)
{
WNDCLASSA cls = {0};
HWND hWnd, child;
HTHEME hTheme;
HRESULT hRes;
MSG msg;
hRes = SetWindowTheme(NULL, NULL, NULL);
ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", hRes);
/* Test that WM_THEMECHANGED is sent and not posted to windows */
cls.hInstance = GetModuleHandleA(0);
cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
cls.hbrBackground = GetStockObject(WHITE_BRUSH);
cls.lpfnWndProc = TestSetWindowThemeParentProcA;
cls.lpszClassName = "TestSetWindowThemeParentClass";
RegisterClassA(&cls);
cls.lpfnWndProc = TestSetWindowThemeChildProcA;
cls.lpszClassName = "TestSetWindowThemeChildClass";
RegisterClassA(&cls);
hWnd = CreateWindowA("TestSetWindowThemeParentClass", "parent",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, 0, 0, 0, NULL);
ok(!!hWnd, "Failed to create a parent window.\n");
child = CreateWindowA("TestSetWindowThemeChildClass", "child", WS_CHILD | WS_VISIBLE, 0, 0, 50,
50, hWnd, 0, 0, NULL);
ok(!!child, "Failed to create a child window.\n");
flush_events();
flush_sequences(sequences, NUM_MSG_SEQUENCES);
hRes = SetWindowTheme(hWnd, NULL, NULL);
ok(hRes == S_OK, "Expected %#lx, got %#lx.\n", S_OK, hRes);
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
{
struct message recv_msg = {0};
if (msg.message == WM_THEMECHANGED)
{
recv_msg.message = msg.message;
recv_msg.flags = posted | wparam | lparam;
recv_msg.wParam = msg.wParam;
recv_msg.lParam = msg.lParam;
add_message(sequences, msg.hwnd == hWnd ? PARENT_SEQ_INDEX : CHILD_SEQ_INDEX, &recv_msg);
}
DispatchMessageA(&msg);
}
ok_sequence(sequences, PARENT_SEQ_INDEX, SetWindowThemeSeq, "SetWindowTheme parent", FALSE);
ok_sequence(sequences, CHILD_SEQ_INDEX, EmptySeq, "SetWindowTheme child", FALSE);
DestroyWindow(hWnd);
UnregisterClassA("TestSetWindowThemeParentClass", GetModuleHandleA(0));
UnregisterClassA("TestSetWindowThemeChildClass", GetModuleHandleA(0));
/* Only do the bare minimum to get a valid hwnd */
hWnd = CreateWindowExA(0, "button", "test", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
ok(hWnd != NULL, "Failed to create a test window.\n");
hRes = SetWindowTheme(hWnd, NULL, NULL);
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
if (IsThemeActive())
{
hTheme = OpenThemeData(hWnd, L"Button");
ok(!!hTheme, "OpenThemeData failed.\n");
CloseThemeData(hTheme);
hRes = SetWindowTheme(hWnd, L"deadbeef", NULL);
ok(hRes == S_OK, "Expected S_OK, got 0x%08lx.\n", hRes);
hTheme = OpenThemeData(hWnd, L"Button");
ok(!!hTheme, "OpenThemeData failed.\n");
CloseThemeData(hTheme);
}
else
{
skip("No active theme, skipping rest of SetWindowTheme tests.\n");
}
DestroyWindow(hWnd);
}
static void test_OpenThemeData(void)
{
HTHEME hTheme, hTheme2;
HWND hWnd;
BOOL bThemeActive;
HRESULT hRes;
BOOL bTPDefined;
const WCHAR szInvalidClassList[] = L"DEADBEEF";
const WCHAR szButtonClassList[] = L"Button";
const WCHAR szButtonClassList2[] = L"bUtToN";
const WCHAR szClassList[] = L"Button;ListBox";
bThemeActive = IsThemeActive();
/* All NULL */
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(NULL, NULL);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_POINTER,
"Expected GLE() to be E_POINTER, got 0x%08lx\n",
GetLastError());
/* A NULL hWnd and an invalid classlist */
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(NULL, szInvalidClassList);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(NULL, szClassList);
if (bThemeActive)
{
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
}
else
{
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
}
/* Only do the bare minimum to get a valid hdc */
hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
if (!hWnd) return;
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(hWnd, NULL);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_POINTER,
"Expected GLE() to be E_POINTER, got 0x%08lx\n",
GetLastError());
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(hWnd, szInvalidClassList);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
if (!bThemeActive)
{
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(hWnd, szButtonClassList);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
skip("No active theme, skipping rest of OpenThemeData tests\n");
return;
}
/* Only do the next checks if we have an active theme */
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(hWnd, szButtonClassList);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
/* Test with bUtToN instead of Button */
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(hWnd, szButtonClassList2);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
SetLastError(0xdeadbeef);
hTheme = OpenThemeData(hWnd, szClassList);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
/* GetWindowTheme should return the last handle opened by OpenThemeData */
SetLastError(0xdeadbeef);
hTheme2 = GetWindowTheme(hWnd);
ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n",
hTheme, hTheme2);
ok( GetLastError() == 0xdeadbeef,
"Expected 0xdeadbeef, got 0x%08lx\n",
GetLastError());
hRes = CloseThemeData(hTheme);
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
/* Close a second time */
hRes = CloseThemeData(hTheme);
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
/* See if closing makes a difference for GetWindowTheme */
SetLastError(0xdeadbeef);
hTheme2 = NULL;
hTheme2 = GetWindowTheme(hWnd);
ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n",
hTheme, hTheme2);
ok( GetLastError() == 0xdeadbeef,
"Expected 0xdeadbeef, got 0x%08lx\n",
GetLastError());
SetLastError(0xdeadbeef);
bTPDefined = IsThemePartDefined(hTheme, 0 , 0);
todo_wine
ok( bTPDefined == FALSE, "Expected FALSE\n" );
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
DestroyWindow(hWnd);
}
static void test_OpenThemeDataEx(void)
{
HTHEME hTheme;
HWND hWnd;
BOOL bThemeActive;
const WCHAR szInvalidClassList[] = L"DEADBEEF";
const WCHAR szButtonClassList[] = L"Button";
const WCHAR szButtonClassList2[] = L"bUtToN";
const WCHAR szClassList[] = L"Button;ListBox";
if (!pOpenThemeDataEx)
{
win_skip("OpenThemeDataEx not available\n");
return;
}
bThemeActive = IsThemeActive();
/* All NULL */
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(NULL, NULL, 0);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_POINTER,
"Expected GLE() to be E_POINTER, got 0x%08lx\n",
GetLastError());
/* A NULL hWnd and an invalid classlist without flags */
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(NULL, szInvalidClassList, 0);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(NULL, szClassList, 0);
if (bThemeActive)
{
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
}
else
{
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
}
/* Only do the bare minimum to get a valid hdc */
hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
if (!hWnd) return;
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, NULL, 0);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_POINTER,
"Expected GLE() to be E_POINTER, got 0x%08lx\n",
GetLastError());
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szInvalidClassList, 0);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
if (!bThemeActive)
{
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0);
ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
E_PROP_ID_UNSUPPORTED, GetLastError() );
skip("No active theme, skipping rest of OpenThemeDataEx tests\n");
return;
}
/* Only do the next checks if we have an active theme */
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, OTD_FORCE_RECT_SIZING);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, OTD_NONCLIENT);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0x3);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
/* Test with bUtToN instead of Button */
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szButtonClassList2, 0);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
SetLastError(0xdeadbeef);
hTheme = pOpenThemeDataEx(hWnd, szClassList, 0);
ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
DestroyWindow(hWnd);
}
static void test_OpenThemeDataForDpi(void)
{
BOOL is_theme_active;
HTHEME htheme;
if (!pOpenThemeDataForDpi)
{
win_skip("OpenThemeDataForDpi is unavailable.\n");
return;
}
is_theme_active = IsThemeActive();
SetLastError(0xdeadbeef);
htheme = pOpenThemeDataForDpi(NULL, WC_BUTTONW, 96);
if (is_theme_active)
{
ok(!!htheme, "Got a NULL handle.\n");
ok(GetLastError() == NO_ERROR, "Expected error %u, got %lu.\n", NO_ERROR, GetLastError());
CloseThemeData(htheme);
}
else
{
ok(!htheme, "Got a non-NULL handle.\n");
ok(GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected error %lu, got %lu.\n",
E_PROP_ID_UNSUPPORTED, GetLastError());
}
}
static void test_GetCurrentThemeName(void)
{
BOOL bThemeActive;
HRESULT hRes;
WCHAR currentTheme[MAX_PATH];
WCHAR currentColor[MAX_PATH];
WCHAR currentSize[MAX_PATH];
bThemeActive = IsThemeActive();
/* All NULLs */
hRes = GetCurrentThemeName(NULL, 0, NULL, 0, NULL, 0);
if (bThemeActive)
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
/* Number of characters given is 0 */
hRes = GetCurrentThemeName(currentTheme, 0, NULL, 0, NULL, 0);
if (bThemeActive)
ok( hRes == S_OK || broken(hRes == E_FAIL /* WinXP SP1 */), "Expected S_OK, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
hRes = GetCurrentThemeName(currentTheme, 2, NULL, 0, NULL, 0);
if (bThemeActive)
todo_wine
ok(hRes == E_NOT_SUFFICIENT_BUFFER ||
broken(hRes == E_FAIL /* WinXP SP1 */),
"Expected E_NOT_SUFFICIENT_BUFFER, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
/* The same is true if the number of characters is too small for Color and/or Size */
hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor, 2,
currentSize, ARRAY_SIZE(currentSize));
if (bThemeActive)
todo_wine
ok(hRes == E_NOT_SUFFICIENT_BUFFER ||
broken(hRes == E_FAIL /* WinXP SP1 */),
"Expected E_NOT_SUFFICIENT_BUFFER, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
/* Given number of characters is correct */
hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), NULL, 0, NULL, 0);
if (bThemeActive)
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
/* Given number of characters for the theme name is too large */
hRes = GetCurrentThemeName(currentTheme, sizeof(currentTheme), NULL, 0, NULL, 0);
if (bThemeActive)
ok( hRes == E_POINTER || hRes == S_OK, "Expected E_POINTER or S_OK, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED ||
hRes == E_POINTER, /* win2k3 */
"Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
/* The too large case is only for the theme name, not for color name or size name */
hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
sizeof(currentTheme), currentSize, ARRAY_SIZE(currentSize));
if (bThemeActive)
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
ARRAY_SIZE(currentTheme), currentSize, sizeof(currentSize));
if (bThemeActive)
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
/* Correct call */
hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
ARRAY_SIZE(currentColor), currentSize, ARRAY_SIZE(currentSize));
if (bThemeActive)
ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
else
ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
}
static void test_CloseThemeData(void)
{
HRESULT hRes;
hRes = CloseThemeData(NULL);
ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", hRes);
hRes = CloseThemeData(INVALID_HANDLE_VALUE);
ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", hRes);
}
static void test_buffer_dc_props(HDC hdc, const RECT *rect)
{
static const XFORM ident = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
XFORM xform;
POINT org;
RECT box;
BOOL ret;
ret = GetWorldTransform(hdc, &xform);
ok(ret, "Failed to get world transform\n");
ok(!memcmp(&xform, &ident, sizeof(xform)), "Unexpected world transform\n");
ret = GetViewportOrgEx(hdc, &org);
ok(ret, "Failed to get vport origin\n");
ok(org.x == 0 && org.y == 0, "Unexpected vport origin\n");
ret = GetWindowOrgEx(hdc, &org);
ok(ret, "Failed to get vport origin\n");
ok(org.x == rect->left && org.y == rect->top, "Unexpected window origin\n");
ret = GetClipBox(hdc, &box);
ok(ret, "Failed to get clip box\n");
ok(box.left == rect->left && box.top == rect->top, "Unexpected clip box\n");
ok(GetGraphicsMode(hdc) == GM_COMPATIBLE, "wrong graphics mode\n");
}
static void test_buffered_paint(void)
{
HDC target, src, hdc, screen_dc;
BP_PAINTPARAMS params = { 0 };
BP_BUFFERFORMAT format;
HPAINTBUFFER buffer;
RECT rect, rect2;
RGBQUAD *bits;
HBITMAP hbm;
HRESULT hr;
int row;
if (!pBeginBufferedPaint)
{
win_skip("Buffered painting API is not supported.\n");
return;
}
buffer = pBeginBufferedPaint(NULL, NULL, BPBF_COMPATIBLEBITMAP,
NULL, NULL);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
target = CreateCompatibleDC(0);
buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
NULL, NULL);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
params.cbSize = sizeof(params);
buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
&params, NULL);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
src = (void *)0xdeadbeef;
buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
&params, &src);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
ok(src == NULL, "Unexpected buffered dc %p\n", src);
/* target rect is mandatory */
SetRectEmpty(&rect);
src = (void *)0xdeadbeef;
buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
&params, &src);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
ok(src == NULL, "Unexpected buffered dc %p\n", src);
/* inverted rectangle */
SetRect(&rect, 10, 0, 5, 5);
src = (void *)0xdeadbeef;
buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
&params, &src);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
ok(src == NULL, "Unexpected buffered dc %p\n", src);
SetRect(&rect, 0, 10, 5, 0);
src = (void *)0xdeadbeef;
buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
&params, &src);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
ok(src == NULL, "Unexpected buffered dc %p\n", src);
/* valid rectangle, no target dc */
SetRect(&rect, 0, 0, 5, 5);
src = (void *)0xdeadbeef;
buffer = pBeginBufferedPaint(NULL, &rect, BPBF_COMPATIBLEBITMAP,
&params, &src);
ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
ok(src == NULL, "Unexpected buffered dc %p\n", src);
SetRect(&rect, 0, 0, 5, 5);
src = NULL;
buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
&params, &src);
ok(buffer != NULL, "Unexpected buffer %p\n", buffer);
ok(src != NULL, "Expected buffered dc\n");
hr = pEndBufferedPaint(buffer, FALSE);
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
SetRect(&rect, 0, 0, 5, 5);
buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
&params, &src);
ok(buffer != NULL, "Unexpected buffer %p\n", buffer);
/* clearing */
hr = pBufferedPaintClear(NULL, NULL);
todo_wine
ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
hr = pBufferedPaintClear(buffer, NULL);
todo_wine
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
/* access buffer attributes */
hdc = pGetBufferedPaintDC(buffer);
ok(hdc == src, "Unexpected hdc, %p, buffered dc %p\n", hdc, src);
hdc = pGetBufferedPaintTargetDC(buffer);
ok(hdc == target, "Unexpected target hdc %p, original %p\n", hdc, target);
hr = pGetBufferedPaintTargetRect(NULL, NULL);
ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
hr = pGetBufferedPaintTargetRect(buffer, NULL);
ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
hr = pGetBufferedPaintTargetRect(NULL, &rect2);
ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
SetRectEmpty(&rect2);
hr = pGetBufferedPaintTargetRect(buffer, &rect2);
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
ok(EqualRect(&rect, &rect2), "Wrong target rect\n");
hr = pEndBufferedPaint(buffer, FALSE);
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
/* invalid buffer handle */
hr = pEndBufferedPaint(NULL, FALSE);
ok(hr == E_INVALIDARG, "Unexpected return code %#lx\n", hr);
hdc = pGetBufferedPaintDC(NULL);
ok(hdc == NULL, "Unexpected hdc %p\n", hdc);
hdc = pGetBufferedPaintTargetDC(NULL);
ok(hdc == NULL, "Unexpected target hdc %p\n", hdc);
hr = pGetBufferedPaintTargetRect(NULL, &rect2);
ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
hr = pGetBufferedPaintTargetRect(NULL, NULL);
ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
bits = (void *)0xdeadbeef;
row = 10;
hr = pGetBufferedPaintBits(NULL, &bits, &row);
ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
ok(row == 10, "Unexpected row count %d\n", row);
ok(bits == (void *)0xdeadbeef, "Unexpected data pointer %p\n", bits);
hr = pGetBufferedPaintBits(NULL, NULL, NULL);
ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
hr = pGetBufferedPaintBits(NULL, &bits, NULL);
ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
hr = pGetBufferedPaintBits(NULL, NULL, &row);
ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
screen_dc = GetDC(0);
hdc = CreateCompatibleDC(screen_dc);
ok(hdc != NULL, "Failed to create a DC\n");
hbm = CreateCompatibleBitmap(screen_dc, 64, 64);
ok(hbm != NULL, "Failed to create a bitmap\n");
SelectObject(hdc, hbm);
ReleaseDC(0, screen_dc);
SetRect(&rect, 1, 2, 34, 56);
buffer = pBeginBufferedPaint(hdc, &rect, BPBF_COMPATIBLEBITMAP, NULL, &src);
test_buffer_dc_props(src, &rect);
hr = pEndBufferedPaint(buffer, FALSE);
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
DeleteObject(hbm);
DeleteDC(hdc);
buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP, NULL, &src);
test_buffer_dc_props(src, &rect);
hr = pEndBufferedPaint(buffer, FALSE);
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
/* access buffer bits */
for (format = BPBF_COMPATIBLEBITMAP; format <= BPBF_TOPDOWNMONODIB; format++)
{
buffer = pBeginBufferedPaint(target, &rect, format, &params, &src);
/* only works for DIB buffers */
bits = NULL;
row = 0;
hr = pGetBufferedPaintBits(buffer, &bits, &row);
if (format == BPBF_COMPATIBLEBITMAP)
ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
else
{
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
ok(bits != NULL, "Bitmap bits %p\n", bits);
ok(row >= (rect.right - rect.left), "format %d: bitmap width %d\n", format, row);
}
hr = pEndBufferedPaint(buffer, FALSE);
ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
}
DeleteDC(target);
}
static void test_GetThemePartSize(void)
{
static const DWORD enabled = 1;
DPI_AWARENESS_CONTEXT old_context;
unsigned int old_dpi, current_dpi;
HTHEME htheme = NULL;
HWND hwnd = NULL;
SIZE size, size2;
HKEY key = NULL;
HRESULT hr;
HDC hdc;
if (!pSetThreadDpiAwarenessContext)
{
win_skip("SetThreadDpiAwarenessContext is unavailable.\n");
return;
}
/* Set IgnorePerProcessSystemDPIToast to 1 to disable "fix blurry apps popup" on Windows 10,
* which may interfere other tests because it steals focus */
RegOpenKeyA(HKEY_CURRENT_USER, "Control Panel\\Desktop", &key);
RegSetValueExA(key, "IgnorePerProcessSystemDPIToast", 0, REG_DWORD, (const BYTE *)&enabled,
sizeof(enabled));
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 %#lx.\n", hr);
hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %ldx%ld, got %ldx%ld.\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 %#lx.\n", hr);
ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %ldx%ld, got %ldx%ld.\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 %#lx.\n", hr);
ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %ldx%ld, got %ldx%ld.\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 %#lx.\n", hr);
ok(size2.cx != size.cx || size2.cy != size.cy, "Expected size not equal to %ldx%ld.\n", size.cx,
size.cy);
hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
ok(size2.cx != size.cx || size2.cy != size.cy, "Expected size not equal to %ldx%ld.\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 %#lx.\n", hr);
ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %ldx%ld, got %ldx%ld.\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 %#lx.\n", hr);
ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %ldx%ld, got %ldx%ld.\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);
if (key)
{
RegDeleteValueA(key, "IgnorePerProcessSystemDPIToast");
RegCloseKey(key);
}
pSetThreadDpiAwarenessContext(old_context);
}
static void test_EnableTheming(void)
{
WCHAR old_color_string[13], new_color_string[13], color_string[13];
BOOL old_gradient_caption, new_gradient_caption, gradient_caption;
BOOL old_flat_menu, new_flat_menu, flat_menu;
LOGFONTW old_logfont, new_logfont, logfont;
NONCLIENTMETRICSW old_ncm, new_ncm, ncm;
DPI_AWARENESS_CONTEXT old_context = 0;
COLORREF old_color, new_color;
BOOL is_theme_active, ret;
DWORD size, length;
HRESULT hr;
LSTATUS ls;
HKEY hkey;
if (IsThemeActive())
{
hr = EnableTheming(TRUE);
ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
ok(IsThemeActive(), "Expected theming active.\n");
/* Only run in interactive mode because once theming is disabled, it can't be turned back on
* via EnableTheming() */
if (winetest_interactive)
{
if (pSetThreadDpiAwarenessContext)
{
old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
else if (get_primary_monitor_effective_dpi() != 96)
{
skip("DPI isn't 96, skipping.\n");
return;
}
/* Read current system metrics */
old_color = GetSysColor(COLOR_SCROLLBAR);
swprintf(old_color_string, ARRAY_SIZE(old_color_string), L"%d %d %d",
GetRValue(old_color), GetGValue(old_color), GetBValue(old_color));
memset(&old_ncm, 0, sizeof(old_ncm));
old_ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth);
ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(old_ncm), &old_ncm, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
memset(&old_logfont, 0, sizeof(old_logfont));
ret = SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(old_logfont), &old_logfont, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_GETFLATMENU, 0, &old_flat_menu, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &old_gradient_caption, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
/* Write new system metrics to the registry */
new_color = ~old_color;
new_flat_menu = !old_flat_menu;
new_gradient_caption = !old_gradient_caption;
memcpy(&new_ncm, &old_ncm, sizeof(new_ncm));
new_ncm.iScrollWidth += 5;
memcpy(&new_logfont, &old_logfont, sizeof(new_logfont));
new_logfont.lfWidth += 5;
ls = RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Colors", 0, KEY_ALL_ACCESS,
&hkey);
ok(!ls, "RegOpenKeyExW failed, ls %#lx.\n", ls);
length = swprintf(new_color_string, ARRAY_SIZE(new_color_string), L"%d %d %d",
GetRValue(new_color), GetGValue(new_color), GetBValue(new_color));
ls = RegSetValueExW(hkey, L"Scrollbar", 0, REG_SZ, (BYTE *)new_color_string,
(length + 1) * sizeof(WCHAR));
ok(!ls, "RegSetValueExW failed, ls %#lx.\n", ls);
ret = SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(new_ncm), &new_ncm,
SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_SETICONTITLELOGFONT, sizeof(new_logfont), &new_logfont,
SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_SETFLATMENU, 0, (void *)(INT_PTR)new_flat_menu,
SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_SETGRADIENTCAPTIONS, 0,
(void *)(INT_PTR)new_gradient_caption, SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
/* Change theming state */
hr = EnableTheming(FALSE);
ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
is_theme_active = IsThemeActive();
ok(!is_theme_active || broken(is_theme_active), /* Win8+ can no longer disable theming */
"Expected theming inactive.\n");
/* Test that system metrics are unchanged */
size = sizeof(color_string);
ls = RegQueryValueExW(hkey, L"Scrollbar", NULL, NULL, (BYTE *)color_string, &size);
ok(!ls, "RegQueryValueExW failed, ls %#lx.\n", ls);
ok(!lstrcmpW(color_string, new_color_string), "Expected %s, got %s.\n",
wine_dbgstr_w(new_color_string), wine_dbgstr_w(color_string));
ret = SystemParametersInfoW(SPI_GETFLATMENU, 0, &flat_menu, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ok(flat_menu == new_flat_menu, "Expected %d, got %d.\n", new_flat_menu, flat_menu);
ret = SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &gradient_caption, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ok(gradient_caption == new_gradient_caption, "Expected %d, got %d.\n",
new_gradient_caption, gradient_caption);
memset(&ncm, 0, sizeof(ncm));
ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth);
ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ok(!memcmp(&ncm, &new_ncm, sizeof(ncm)), "Expected non-client metrics unchanged.\n");
memset(&logfont, 0, sizeof(logfont));
ret = SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(logfont), &logfont, 0);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ok(!memcmp(&logfont, &new_logfont, sizeof(logfont)),
"Expected icon title font unchanged.\n");
/* Test that theming cannot be turned on via EnableTheming() */
hr = EnableTheming(TRUE);
ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
is_theme_active = IsThemeActive();
ok(!is_theme_active || broken(is_theme_active), /* Win8+ can no longer disable theming */
"Expected theming inactive.\n");
/* Restore system metrics */
ls = RegSetValueExW(hkey, L"Scrollbar", 0, REG_SZ, (BYTE *)old_color_string,
(lstrlenW(old_color_string) + 1) * sizeof(WCHAR));
ok(!ls, "RegSetValueExW failed, ls %#lx.\n", ls);
ret = SystemParametersInfoW(SPI_SETFLATMENU, 0, (void *)(INT_PTR)old_flat_menu,
SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_SETGRADIENTCAPTIONS, 0,
(void *)(INT_PTR)old_gradient_caption, SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(old_ncm), &old_ncm,
SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
ret = SystemParametersInfoW(SPI_SETICONTITLELOGFONT, sizeof(old_logfont), &old_logfont,
SPIF_UPDATEINIFILE);
ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
RegCloseKey(hkey);
if (pSetThreadDpiAwarenessContext)
pSetThreadDpiAwarenessContext(old_context);
}
}
else
{
hr = EnableTheming(FALSE);
ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
ok(!IsThemeActive(), "Expected theming inactive.\n");
hr = EnableTheming(TRUE);
ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
ok(!IsThemeActive(), "Expected theming inactive.\n");
hr = EnableTheming(FALSE);
ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
ok(!IsThemeActive(), "Expected theming inactive.\n");
}
}
static void test_GetThemeIntList(void)
{
INTLIST intlist;
HTHEME theme;
HRESULT hr;
HWND hwnd;
if (!pGetThemeIntList)
{
win_skip("GetThemeIntList is unavailable.\n");
return;
}
hwnd = CreateWindowA("static", "", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
theme = OpenThemeData(hwnd, L"Button");
if (!theme)
{
skip("Theming is not active.\n");
DestroyWindow(hwnd);
return;
}
/* Check properties */
/* TMT_TRANSITIONDURATIONS is a vista+ property */
hr = pGetThemeIntList(theme, BP_PUSHBUTTON, PBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
if (LOBYTE(LOWORD(GetVersion())) < 6)
ok(hr == E_PROP_ID_UNSUPPORTED, "Expected %#lx, got %#lx.\n", E_PROP_ID_UNSUPPORTED, hr);
else
ok(hr == S_OK, "GetThemeIntList failed, hr %#lx.\n", hr);
CloseThemeData(theme);
DestroyWindow(hwnd);
}
static void test_GetThemeTransitionDuration(void)
{
int from_state, to_state, expected;
INTLIST intlist;
DWORD duration;
HTHEME theme;
HRESULT hr;
HWND hwnd;
if (!pGetThemeTransitionDuration || !pGetThemeIntList)
{
win_skip("GetThemeTransitionDuration or GetThemeIntList is unavailable.\n");
return;
}
hwnd = CreateWindowA("static", "", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
theme = OpenThemeData(hwnd, L"Button");
if (!theme)
{
skip("Theming is not active.\n");
DestroyWindow(hwnd);
return;
}
/* Invalid parameter tests */
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(NULL, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
TMT_TRANSITIONDURATIONS, &duration);
ok(hr == E_HANDLE, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
/* Crash on Wine. HTHEME is not a pointer that can be directly referenced. */
if (strcmp(winetest_platform, "wine"))
{
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration((HTHEME)0xdeadbeef, BP_PUSHBUTTON, PBS_NORMAL,
PBS_DEFAULTED_ANIMATING, TMT_TRANSITIONDURATIONS, &duration);
todo_wine
ok(hr == E_HANDLE, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
}
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, 0xdeadbeef, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
TMT_TRANSITIONDURATIONS, &duration);
ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL - 1, PBS_DEFAULTED_ANIMATING,
TMT_TRANSITIONDURATIONS, &duration);
ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_DEFAULTED_ANIMATING + 1,
PBS_DEFAULTED_ANIMATING, TMT_TRANSITIONDURATIONS, &duration);
ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_NORMAL - 1,
TMT_TRANSITIONDURATIONS, &duration);
ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING + 1,
TMT_TRANSITIONDURATIONS, &duration);
ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
TMT_BACKGROUND, &duration);
ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
0xdeadbeef, &duration);
ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
TMT_TRANSITIONDURATIONS, NULL);
ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
/* Parts that don't have TMT_TRANSITIONDURATIONS */
hr = GetThemeIntList(theme, BP_GROUPBOX, GBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeIntList failed, hr %#lx.\n", hr);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_GROUPBOX, GBS_NORMAL, GBS_DISABLED,
TMT_TRANSITIONDURATIONS, &duration);
ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
/* Test parsing TMT_TRANSITIONDURATIONS property. TMT_TRANSITIONDURATIONS is a vista+ property */
if (LOBYTE(LOWORD(GetVersion())) < 6)
goto done;
hr = pGetThemeIntList(theme, BP_PUSHBUTTON, PBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
ok(hr == S_OK, "GetThemeIntList failed, hr %#lx.\n", hr);
/* The first value is the theme part state count. The following are the values from every state
* to every state. So the total value count should be 1 + state ^ 2 */
expected = PBS_DEFAULTED_ANIMATING - PBS_NORMAL + 1;
ok(intlist.iValues[0] == expected, "Expected the first value %d, got %d.\n", expected,
intlist.iValues[0]);
expected = 1 + intlist.iValues[0] * intlist.iValues[0];
ok(intlist.iValueCount == expected, "Expected value count %d, got %d.\n", expected,
intlist.iValueCount);
if (hr == S_OK)
{
for (from_state = PBS_NORMAL; from_state <= PBS_DEFAULTED_ANIMATING; ++from_state)
{
for (to_state = PBS_NORMAL; to_state <= PBS_DEFAULTED_ANIMATING; ++to_state)
{
winetest_push_context("from state %d to %d", from_state, to_state);
duration = 0xdeadbeef;
hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, from_state, to_state,
TMT_TRANSITIONDURATIONS, &duration);
ok(hr == S_OK, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
expected = intlist.iValues[1 + intlist.iValues[0] * (from_state - 1) + (to_state - 1)];
ok(duration == expected, "Expected duration %d, got %ld.\n", expected, duration);
winetest_pop_context();
}
}
}
done:
CloseThemeData(theme);
DestroyWindow(hwnd);
}
static const struct message DrawThemeParentBackground_seq[] =
{
{WM_ERASEBKGND, sent},
{WM_PRINTCLIENT, sent | lparam, 0, PRF_CLIENT},
{0}
};
static LRESULT WINAPI test_DrawThemeParentBackground_proc(HWND hwnd, UINT message, WPARAM wp,
LPARAM lp)
{
static LONG defwndproc_counter;
struct message msg = {0};
LRESULT lr;
POINT org;
BOOL ret;
switch (message)
{
/* Test that DrawThemeParentBackground() doesn't change brush origins */
case WM_ERASEBKGND:
case WM_PRINTCLIENT:
ret = GetBrushOrgEx((HDC)wp, &org);
ok(ret, "GetBrushOrgEx failed, error %ld.\n", GetLastError());
ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
break;
default:
break;
}
msg.message = message;
msg.flags = sent | wparam | lparam;
if (defwndproc_counter)
msg.flags |= defwinproc;
msg.wParam = wp;
msg.lParam = lp;
add_message(sequences, PARENT_SEQ_INDEX, &msg);
InterlockedIncrement(&defwndproc_counter);
lr = DefWindowProcA(hwnd, message, wp, lp);
InterlockedDecrement(&defwndproc_counter);
return lr;
}
static void test_DrawThemeParentBackground(void)
{
HWND child, parent;
WNDCLASSA cls;
HRESULT hr;
POINT org;
RECT rect;
BOOL ret;
HDC hdc;
memset(&cls, 0, sizeof(cls));
cls.hInstance = GetModuleHandleA(0);
cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
cls.hbrBackground = GetStockObject(WHITE_BRUSH);
cls.lpfnWndProc = test_DrawThemeParentBackground_proc;
cls.lpszClassName = "TestDrawThemeParentBackgroundClass";
RegisterClassA(&cls);
parent = CreateWindowA("TestDrawThemeParentBackgroundClass", "parent", WS_POPUP | WS_VISIBLE, 0,
0, 100, 100, 0, 0, 0, 0);
ok(parent != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, parent, 0, 0,
NULL);
ok(child != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
flush_events();
flush_sequences(sequences, NUM_MSG_SEQUENCES);
hdc = GetDC(child);
ret = GetBrushOrgEx(hdc, &org);
ok(ret, "GetBrushOrgEx failed, error %ld.\n", GetLastError());
ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
hr = DrawThemeParentBackground(child, hdc, NULL);
ok(SUCCEEDED(hr), "DrawThemeParentBackground failed, hr %#lx.\n", hr);
ok_sequence(sequences, PARENT_SEQ_INDEX, DrawThemeParentBackground_seq,
"DrawThemeParentBackground parent", FALSE);
flush_sequences(sequences, NUM_MSG_SEQUENCES);
GetClientRect(child, &rect);
hr = DrawThemeParentBackground(child, hdc, &rect);
ok(SUCCEEDED(hr), "DrawThemeParentBackground failed, hr %#lx.\n", hr);
ok_sequence(sequences, PARENT_SEQ_INDEX, DrawThemeParentBackground_seq,
"DrawThemeParentBackground parent", FALSE);
flush_sequences(sequences, NUM_MSG_SEQUENCES);
ReleaseDC(child, hdc);
DestroyWindow(parent);
UnregisterClassA("TestDrawThemeParentBackgroundClass", GetModuleHandleA(0));
}
struct test_EnableThemeDialogTexture_param
{
const CHAR *class_name;
DWORD style;
};
static const struct message empty_seq[] =
{
{0}
};
static HWND dialog_child;
static DWORD dialog_init_flag;
static BOOL handle_WM_CTLCOLORSTATIC;
static INT_PTR CALLBACK test_EnableThemeDialogTexture_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
struct test_EnableThemeDialogTexture_param *param;
struct message message = {0};
message.message = msg;
message.flags = sent | wparam | lparam;
message.wParam = wp;
message.lParam = lp;
add_message(sequences, PARENT_SEQ_INDEX, &message);
switch (msg)
{
case WM_INITDIALOG:
param = (struct test_EnableThemeDialogTexture_param *)lp;
dialog_child = CreateWindowA(param->class_name, "child",
param->style | WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, hwnd,
(HMENU)100, 0, NULL);
ok(dialog_child != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
if (dialog_init_flag)
EnableThemeDialogTexture(hwnd, dialog_init_flag);
return TRUE;
case WM_CTLCOLORSTATIC:
return (INT_PTR)(handle_WM_CTLCOLORSTATIC ? GetSysColorBrush(COLOR_MENU) : 0);
case WM_CLOSE:
DestroyWindow(dialog_child);
dialog_child = NULL;
return TRUE;
default:
return FALSE;
}
}
static void test_EnableThemeDialogTexture(void)
{
struct test_EnableThemeDialogTexture_param param;
HWND dialog, child, hwnd, hwnd2;
int mode, old_mode, count, i, j;
COLORREF color, old_color;
HBRUSH brush, brush2;
HDC child_hdc, hdc;
LOGBRUSH log_brush;
ULONG_PTR proc;
WNDCLASSA cls;
HTHEME theme;
DWORD error;
BITMAP bmp;
HRESULT hr;
LRESULT lr;
POINT org;
SIZE size;
UINT msg;
BOOL ret;
struct
{
DLGTEMPLATE template;
WORD menu;
WORD class;
WORD title;
} temp = {{0}};
static const DWORD flags[] =
{
ETDT_DISABLE,
ETDT_ENABLE,
ETDT_USETABTEXTURE,
ETDT_USEAEROWIZARDTABTEXTURE,
ETDT_ENABLETAB,
ETDT_ENABLEAEROWIZARDTAB,
/* Bad flags */
0,
ETDT_DISABLE | ETDT_ENABLE,
ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB
};
static const struct invalid_flag_test
{
DWORD flag;
BOOL enabled;
}
invalid_flag_tests[] =
{
{0, FALSE},
{ETDT_DISABLE | ETDT_ENABLE, FALSE},
{ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB, TRUE},
{ETDT_USETABTEXTURE | ETDT_USEAEROWIZARDTABTEXTURE, TRUE},
{ETDT_VALIDBITS, FALSE},
{~ETDT_VALIDBITS, FALSE},
{~ETDT_VALIDBITS | ETDT_DISABLE, FALSE}
};
static const struct class_test
{
struct test_EnableThemeDialogTexture_param param;
BOOL texture_enabled;
}
class_tests[] =
{
{{ANIMATE_CLASSA}},
{{WC_BUTTONA, BS_PUSHBUTTON}, TRUE},
{{WC_BUTTONA, BS_DEFPUSHBUTTON}, TRUE},
{{WC_BUTTONA, BS_CHECKBOX}, TRUE},
{{WC_BUTTONA, BS_AUTOCHECKBOX}, TRUE},
{{WC_BUTTONA, BS_RADIOBUTTON}, TRUE},
{{WC_BUTTONA, BS_3STATE}, TRUE},
{{WC_BUTTONA, BS_AUTO3STATE}, TRUE},
{{WC_BUTTONA, BS_GROUPBOX}, TRUE},
{{WC_BUTTONA, BS_USERBUTTON}, TRUE},
{{WC_BUTTONA, BS_AUTORADIOBUTTON}, TRUE},
{{WC_BUTTONA, BS_PUSHBOX}, TRUE},
{{WC_BUTTONA, BS_OWNERDRAW}, TRUE},
{{WC_BUTTONA, BS_SPLITBUTTON}, TRUE},
{{WC_BUTTONA, BS_DEFSPLITBUTTON}, TRUE},
{{WC_BUTTONA, BS_COMMANDLINK}, TRUE},
{{WC_BUTTONA, BS_DEFCOMMANDLINK}, TRUE},
{{WC_COMBOBOXA}},
{{WC_COMBOBOXEXA}},
{{DATETIMEPICK_CLASSA}},
{{WC_EDITA}},
{{WC_HEADERA}},
{{HOTKEY_CLASSA}},
{{WC_IPADDRESSA}},
{{WC_LISTBOXA}},
{{WC_LISTVIEWA}},
{{MONTHCAL_CLASSA}},
{{WC_NATIVEFONTCTLA}},
{{WC_PAGESCROLLERA}},
{{PROGRESS_CLASSA}},
{{REBARCLASSNAMEA}},
{{WC_STATICA, SS_LEFT}, TRUE},
{{WC_STATICA, SS_ICON}, TRUE},
{{WC_STATICA, SS_BLACKRECT}, TRUE},
{{WC_STATICA, SS_OWNERDRAW}, TRUE},
{{WC_STATICA, SS_BITMAP}, TRUE},
{{WC_STATICA, SS_ENHMETAFILE}, TRUE},
{{WC_STATICA, SS_ETCHEDHORZ}, TRUE},
{{STATUSCLASSNAMEA}},
{{"SysLink"}},
{{WC_TABCONTROLA}},
{{TOOLBARCLASSNAMEA}},
{{TOOLTIPS_CLASSA}},
{{TRACKBAR_CLASSA}},
{{WC_TREEVIEWA}},
{{UPDOWN_CLASSA}},
{{WC_SCROLLBARA}},
};
if (!IsThemeActive())
{
skip("Theming is inactive.\n");
return;
}
memset(&cls, 0, sizeof(cls));
cls.lpfnWndProc = DefWindowProcA;
cls.hInstance = GetModuleHandleA(NULL);
cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
cls.hbrBackground = GetStockObject(GRAY_BRUSH);
cls.lpszClassName = "TestEnableThemeDialogTextureClass";
RegisterClassA(&cls);
temp.template.style = WS_CHILD | WS_VISIBLE;
temp.template.cx = 100;
temp.template.cy = 100;
param.class_name = cls.lpszClassName;
param.style = 0;
dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
test_EnableThemeDialogTexture_proc, (LPARAM)&param);
ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
child = GetDlgItem(dialog, 100);
ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
child_hdc = GetDC(child);
/* Test that dialog procedure is unchanged */
proc = GetWindowLongPtrA(dialog, DWLP_DLGPROC);
ok(proc == (ULONG_PTR)test_EnableThemeDialogTexture_proc, "Unexpected proc %#Ix.\n", proc);
/* Test dialog texture is disabled by default. EnableThemeDialogTexture() needs to be called */
ret = IsThemeDialogTextureEnabled(dialog);
ok(!ret, "Expected theme dialog texture disabled.\n");
ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
/* Test ETDT_ENABLE | ETDT_USETABTEXTURE doesn't take effect immediately */
hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
ret = IsThemeDialogTextureEnabled(dialog);
ok(ret, "Expected theme dialog texture enabled.\n");
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
GetSysColorBrush(COLOR_BTNFACE), brush);
ret = GetBrushOrgEx(child_hdc, &org);
ok(ret, "GetBrushOrgEx failed, error %lu.\n", GetLastError());
ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
/* Test WM_THEMECHANGED doesn't make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
ok(lr == 0, "WM_THEMECHANGED failed.\n");
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
GetSysColorBrush(COLOR_BTNFACE), brush);
/* Test WM_ERASEBKGND make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
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);
ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected brush changed.\n");
/* Test disabling theme dialog texture should change the brush immediately */
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 != brush, "Expected a different brush.\n");
ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
GetSysColorBrush(COLOR_BTNFACE), brush2);
/* Test re-enabling theme dialog texture with ETDT_ENABLE doesn't change the brush */
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 == brush, "Expected the same brush.\n");
ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
GetSysColorBrush(COLOR_BTNFACE), brush2);
/* Test adding ETDT_USETABTEXTURE should change the brush immediately */
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
hr = EnableThemeDialogTexture(dialog, ETDT_USETABTEXTURE);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 != brush, "Expected a different brush.\n");
/* Test ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE should change the brush immediately */
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
/* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
if (LOBYTE(LOWORD(GetVersion())) < 6)
ok(brush2 == brush, "Expected the same brush.\n");
else
ok(brush2 != brush, "Expected a different brush.\n");
hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
/* Test that the dialog procedure should take precedence over DefDlgProc() for WM_CTLCOLORSTATIC */
handle_WM_CTLCOLORSTATIC = TRUE;
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush == GetSysColorBrush(COLOR_MENU), "Expected brush %p, got %p.\n",
GetSysColorBrush(COLOR_MENU), brush);
handle_WM_CTLCOLORSTATIC = FALSE;
/* Test that dialog doesn't have theme handle opened for itself */
ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
theme = OpenThemeData(NULL, L"Tab");
ok(theme != NULL, "OpenThemeData failed.\n");
size.cx = 0;
size.cy = 0;
hr = GetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
CloseThemeData(theme);
/* Test which WM_CTLCOLOR* message uses tab background as dialog texture */
for (msg = WM_CTLCOLORMSGBOX; msg <= WM_CTLCOLORSTATIC; ++msg)
{
winetest_push_context("msg %#x", msg);
/* Test that some WM_CTLCOLOR* messages change brush origin when dialog texture is on */
ret = SetBrushOrgEx(child_hdc, 0, 0, NULL);
ok(ret, "SetBrushOrgEx failed, error %lu.\n", GetLastError());
SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
ret = GetBrushOrgEx(child_hdc, &org);
ok(ret, "GetBrushOrgEx failed, error %lu.\n", GetLastError());
/* WM_CTLCOLOREDIT, WM_CTLCOLORLISTBOX and WM_CTLCOLORSCROLLBAR don't use tab background */
if (msg == WM_CTLCOLOREDIT || msg == WM_CTLCOLORLISTBOX || msg == WM_CTLCOLORSCROLLBAR)
{
ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
winetest_pop_context();
continue;
}
else
{
ok(org.x == -1 && org.y == -2, "Expected (-1,-2), got %s.\n", wine_dbgstr_point(&org));
}
/* Test that some WM_CTLCOLOR* messages change background mode when dialog texture is on */
old_mode = SetBkMode(child_hdc, OPAQUE);
ok(old_mode != 0, "SetBkMode failed.\n");
SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
mode = SetBkMode(child_hdc, old_mode);
ok(mode == TRANSPARENT, "Expected mode %#x, got %#x.\n", TRANSPARENT, mode);
/* Test that some WM_CTLCOLOR* messages change background color when dialog texture is on */
old_color = SetBkColor(child_hdc, 0xaa5511);
ok(old_color != CLR_INVALID, "SetBkColor failed.\n");
SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
color = SetBkColor(child_hdc, old_color);
ok(color == GetSysColor(COLOR_BTNFACE), "Expected background color %#lx, got %#lx.\n",
GetSysColor(COLOR_BTNFACE), color);
/* Test that the returned brush is a pattern brush created from the tab body */
brush = (HBRUSH)SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
memset(&log_brush, 0, sizeof(log_brush));
count = GetObjectA(brush, sizeof(log_brush), &log_brush);
ok(count == sizeof(log_brush), "GetObjectA failed, error %lu.\n", GetLastError());
ok(log_brush.lbColor == 0, "Expected brush color %#x, got %#lx.\n", 0, log_brush.lbColor);
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);
ok(count == sizeof(bmp), "GetObjectA failed, error %lu.\n", GetLastError());
ok(bmp.bmWidth == size.cx, "Expected width %ld, got %d.\n", size.cx, bmp.bmWidth);
ok(bmp.bmHeight == size.cy, "Expected height %ld, got %d.\n", size.cy, bmp.bmHeight);
/* Test that DefDlgProcA/W() are hooked for some WM_CTLCOLOR* messages */
brush = (HBRUSH)SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
brush2 = (HBRUSH)DefDlgProcW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 == brush, "Expected the same brush.\n");
brush2 = (HBRUSH)DefDlgProcA(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 == brush, "Expected the same brush.\n");
/* Test that DefWindowProcA/W() are also hooked for some WM_CTLCOLOR* messages */
brush = (HBRUSH)SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
if (msg != WM_CTLCOLORDLG)
{
brush2 = (HBRUSH)DefWindowProcW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
todo_wine
ok(brush2 == brush, "Expected the same brush.\n");
brush2 = (HBRUSH)DefWindowProcA(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
todo_wine
ok(brush2 == brush, "Expected the same brush.\n");
}
else
{
brush2 = (HBRUSH)DefWindowProcW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 != brush, "Expected a different brush.\n");
brush2 = (HBRUSH)DefWindowProcA(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 != brush, "Expected a different brush.\n");
}
winetest_pop_context();
}
/* Test that DefWindowProcA/W() are not hooked for WM_ERASEBKGND. So the background is still
* drawn with hbrBackground, which in this case, is GRAY_BRUSH.
*
* This test means it could be that both DefWindowProc() and DefDlgProc() are hooked for
* WM_CTLCOLORSTATIC and only DefDlgProc() is hooked for WM_ERASEBKGND. Or it could mean
* DefWindowProc() is hooked for WM_CTLCOLORSTATIC and DefDlgProc() is hooked for WM_ERASEBKGND.
* Considering the dialog theming needs a WM_ERASEBKGND to activate, it would be weird for let
* only DefWindowProc() to hook WM_CTLCOLORSTATIC. For example, what's the point of hooking
* WM_CTLCOLORSTATIC in DefWindowProc() for a feature that can only be activated in
* DefDlgProc()? So I tend to believe both DefWindowProc() and DefDlgProc() are hooked for
* WM_CTLCOLORSTATIC */
hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, 0, 0,
0, NULL);
ok(hwnd != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
hwnd2 = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 10, 10, 20, 20, hwnd, NULL, 0,
NULL);
hr = EnableThemeDialogTexture(hwnd, ETDT_ENABLETAB);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
ret = IsThemeDialogTextureEnabled(hwnd);
ok(ret, "Wrong dialog texture status.\n");
flush_events();
hdc = GetDC(hwnd);
color = GetPixel(hdc, 0, 0);
ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
"Expected color %#x, got %#lx.\n", 0x808080, color);
color = GetPixel(hdc, 50, 50);
ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
"Expected color %#x, got %#lx.\n", 0x808080, color);
color = GetPixel(hdc, 99, 99);
ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
"Expected color %#x, got %#lx.\n", 0x808080, color);
ReleaseDC(hwnd, hdc);
/* Test EnableThemeDialogTexture() doesn't work for non-dialog windows */
hdc = GetDC(hwnd2);
brush = (HBRUSH)SendMessageW(hwnd, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd2);
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
ReleaseDC(hwnd2, hdc);
DestroyWindow(hwnd);
/* Test that the brush is not a system object and has only one reference and shouldn't be freed */
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ret = DeleteObject(brush);
ok(ret, "DeleteObject failed, error %lu.\n", GetLastError());
SetLastError(0xdeadbeef);
ret = GetObjectA(brush, sizeof(log_brush), &log_brush);
error = GetLastError();
ok(!ret || broken(ret) /* XP */, "GetObjectA succeeded.\n");
todo_wine
ok(error == ERROR_INVALID_PARAMETER || broken(error == 0xdeadbeef) /* XP */,
"Expected error %u, got %lu.\n", ERROR_INVALID_PARAMETER, error);
ret = DeleteObject(brush);
ok(!ret || broken(ret) /* XP */, "DeleteObject succeeded.\n");
/* Should still report the same brush handle after the brush handle was freed */
brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush2 == brush, "Expected the same brush.\n");
/* Test WM_THEMECHANGED can update the brush now that ETDT_ENABLE | ETDT_USETABTEXTURE is in
* effect. This test needs to be ran last as it affect other tests for the same dialog for
* unknown reason, causing the brush not to update */
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
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);
ok(brush2 != brush, "Expected a different brush.\n");
ReleaseDC(child, child_hdc);
EndDialog(dialog, 0);
/* Test invalid flags */
for (i = 0; i < ARRAY_SIZE(invalid_flag_tests); ++i)
{
winetest_push_context("%d flag %#lx", i, invalid_flag_tests[i].flag);
dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
test_EnableThemeDialogTexture_proc, (LPARAM)&param);
ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
hr = EnableThemeDialogTexture(dialog, invalid_flag_tests[i].flag);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
ret = IsThemeDialogTextureEnabled(dialog);
ok(ret == invalid_flag_tests[i].enabled, "Wrong dialog texture status.\n");
EndDialog(dialog, 0);
winetest_pop_context();
}
/* Test different flag combinations */
for (i = 0; i < ARRAY_SIZE(flags); ++i)
{
for (j = 0; j < ARRAY_SIZE(flags); ++j)
{
/* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
if (LOBYTE(LOWORD(GetVersion())) < 6
&& ((flags[i] | flags[j]) & ETDT_USEAEROWIZARDTABTEXTURE))
continue;
winetest_push_context("%#lx to %#lx", flags[i], flags[j]);
dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
test_EnableThemeDialogTexture_proc, (LPARAM)&param);
ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
flush_events();
flush_sequences(sequences, NUM_MSG_SEQUENCES);
hr = EnableThemeDialogTexture(dialog, flags[i]);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
"EnableThemeDialogTexture first flag", FALSE);
ret = IsThemeDialogTextureEnabled(dialog);
/* Non-zero flags without ETDT_DISABLE enables dialog texture */
ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
child = GetDlgItem(dialog, 100);
ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
child_hdc = GetDC(child);
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);
if (flags[i] == ETDT_ENABLETAB || flags[i] == ETDT_ENABLEAEROWIZARDTAB)
ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
else
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
flush_sequences(sequences, NUM_MSG_SEQUENCES);
hr = EnableThemeDialogTexture(dialog, flags[j]);
ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
"EnableThemeDialogTexture second flag", FALSE);
ret = IsThemeDialogTextureEnabled(dialog);
/* If the flag is zero, it will have previous dialog texture status */
if (flags[j])
ok(ret == !(flags[j] & ETDT_DISABLE), "Wrong dialog texture status.\n");
else
ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
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);
/* Dialog texture is turned on when the flag contains ETDT_ENABLETAB or
* ETDT_ENABLEAEROWIZARDTAB. The flag can be turned on in multiple steps, but you can't
* do things like set ETDT_ENABLETAB and then ETDT_USEAEROWIZARDTABTEXTURE */
if (((flags[j] == ETDT_ENABLETAB || flags[j] == ETDT_ENABLEAEROWIZARDTAB)
|| ((((flags[i] | flags[j]) & ETDT_ENABLETAB) == ETDT_ENABLETAB
|| ((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)))
ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
else
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
ReleaseDC(child, child_hdc);
EndDialog(dialog, 0);
winetest_pop_context();
}
}
/* Test that the dialog procedure should set ETDT_USETABTEXTURE/ETDT_USEAEROWIZARDTABTEXTURE and
* find out which comctl32 class should set ETDT_ENABLE to turn on dialog texture */
for (i = 0; i < ARRAY_SIZE(class_tests); ++i)
{
winetest_push_context("%s %#lx", wine_dbgstr_a(class_tests[i].param.class_name),
class_tests[i].param.style);
dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
test_EnableThemeDialogTexture_proc,
(LPARAM)&class_tests[i].param);
ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
/* GetDlgItem() fails to get the child control if the child is a tooltip */
child = dialog_child;
ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
child_hdc = GetDC(child);
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
ReleaseDC(child, child_hdc);
EndDialog(dialog, 0);
dialog_init_flag = ETDT_ENABLE;
dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
test_EnableThemeDialogTexture_proc,
(LPARAM)&class_tests[i].param);
ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
child = dialog_child;
ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
child_hdc = GetDC(child);
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
ReleaseDC(child, child_hdc);
EndDialog(dialog, 0);
dialog_init_flag = ETDT_USETABTEXTURE;
dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
test_EnableThemeDialogTexture_proc,
(LPARAM)&class_tests[i].param);
ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
child = dialog_child;
ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
child_hdc = GetDC(child);
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
if (class_tests[i].texture_enabled)
ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
else
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
ReleaseDC(child, child_hdc);
EndDialog(dialog, 0);
if (LOBYTE(LOWORD(GetVersion())) < 6)
{
dialog_init_flag = 0;
winetest_pop_context();
continue;
}
dialog_init_flag = ETDT_USEAEROWIZARDTABTEXTURE;
dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
test_EnableThemeDialogTexture_proc,
(LPARAM)&class_tests[i].param);
ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
child = dialog_child;
ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
child_hdc = GetDC(child);
brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
if (class_tests[i].texture_enabled)
ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
else
ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
ReleaseDC(child, child_hdc);
EndDialog(dialog, 0);
dialog_init_flag = 0;
winetest_pop_context();
}
/* Test that EnableThemeDialogTexture() is called from child controls for its parent */
hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
0, 0, NULL);
ok(hwnd != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
ret = IsThemeDialogTextureEnabled(hwnd);
ok(!ret, "Wrong dialog texture status.\n");
child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0,
NULL);
ok(child != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
ret = IsThemeDialogTextureEnabled(hwnd);
ok(ret, "Wrong dialog texture status.\n");
/* Test that if you move the child control to another window, it doesn't enables tab texture for
* the new parent */
hwnd2 = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
0, 0, NULL);
ok(hwnd2 != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
ret = IsThemeDialogTextureEnabled(hwnd2);
ok(!ret, "Wrong dialog texture status.\n");
SetParent(child, hwnd2);
ok(GetParent(child) == hwnd2, "Wrong parent.\n");
ret = IsThemeDialogTextureEnabled(hwnd2);
ok(!ret, "Wrong dialog texture status.\n");
InvalidateRect(child, NULL, TRUE);
flush_events();
ret = IsThemeDialogTextureEnabled(hwnd2);
ok(!ret, "Wrong dialog texture status.\n");
DestroyWindow(hwnd2);
DestroyWindow(hwnd);
UnregisterClassA(cls.lpszClassName, GetModuleHandleA(NULL));
}
START_TEST(system)
{
ULONG_PTR ctx_cookie;
HANDLE ctx;
init_funcs();
init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
/* No real functional theme API tests will be done (yet). The current tests
* only show input/return behaviour
*/
test_IsThemed();
test_IsThemePartDefined();
test_GetWindowTheme();
test_SetWindowTheme();
test_OpenThemeData();
test_OpenThemeDataEx();
test_OpenThemeDataForDpi();
test_GetCurrentThemeName();
test_GetThemePartSize();
test_CloseThemeData();
test_buffered_paint();
test_GetThemeIntList();
test_GetThemeTransitionDuration();
test_DrawThemeParentBackground();
if (load_v6_module(&ctx_cookie, &ctx))
{
test_EnableThemeDialogTexture();
unload_v6_module(ctx_cookie, ctx);
}
/* Test EnableTheming() in the end because it may disable theming */
test_EnableTheming();
}