uxtheme/tests: Add a test for OpenThemeData("explorer::treeview").
[wine.git] / dlls / uxtheme / tests / system.c
blobc9fdf30820d967549c30dd79878b6b1811b81214
1 /* Unit test suite for uxtheme API functions
3 * Copyright 2006 Paul Vriens
4 * Copyright 2021-2022 Zhiyi Zhang for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
24 #include "ntstatus.h"
25 #define WIN32_NO_STATUS
27 #include "windows.h"
28 #include "winternl.h"
29 #include "ddk/d3dkmthk.h"
30 #include "vfwmsgs.h"
31 #include "uxtheme.h"
32 #include "vssym32.h"
34 #include "msg.h"
35 #include "wine/test.h"
37 #include "v6util.h"
39 static HTHEME (WINAPI * pOpenThemeDataEx)(HWND, LPCWSTR, DWORD);
40 static HTHEME (WINAPI *pOpenThemeDataForDpi)(HWND, LPCWSTR, UINT);
41 static HPAINTBUFFER (WINAPI *pBeginBufferedPaint)(HDC, const RECT *, BP_BUFFERFORMAT, BP_PAINTPARAMS *, HDC *);
42 static HRESULT (WINAPI *pBufferedPaintClear)(HPAINTBUFFER, const RECT *);
43 static HRESULT (WINAPI *pDrawThemeBackgroundEx)(HTHEME, HDC, int, int, const RECT *, const DTBGOPTS *);
44 static HRESULT (WINAPI *pEndBufferedPaint)(HPAINTBUFFER, BOOL);
45 static HRESULT (WINAPI *pGetBufferedPaintBits)(HPAINTBUFFER, RGBQUAD **, int *);
46 static HDC (WINAPI *pGetBufferedPaintDC)(HPAINTBUFFER);
47 static HDC (WINAPI *pGetBufferedPaintTargetDC)(HPAINTBUFFER);
48 static HRESULT (WINAPI *pGetBufferedPaintTargetRect)(HPAINTBUFFER, RECT *);
49 static HRESULT (WINAPI *pGetThemeIntList)(HTHEME, int, int, int, INTLIST *);
50 static HRESULT (WINAPI *pGetThemeTransitionDuration)(HTHEME, int, int, int, int, DWORD *);
51 static BOOL (WINAPI *pShouldSystemUseDarkMode)(void);
52 static BOOL (WINAPI *pShouldAppsUseDarkMode)(void);
54 static LONG (WINAPI *pDisplayConfigGetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER *);
55 static LONG (WINAPI *pDisplayConfigSetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER *);
56 static BOOL (WINAPI *pGetDpiForMonitorInternal)(HMONITOR, UINT, UINT *, UINT *);
57 static UINT (WINAPI *pGetDpiForSystem)(void);
58 static DPI_AWARENESS_CONTEXT (WINAPI *pSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
60 static NTSTATUS (WINAPI *pD3DKMTCloseAdapter)(const D3DKMT_CLOSEADAPTER *);
61 static NTSTATUS (WINAPI *pD3DKMTOpenAdapterFromGdiDisplayName)(D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME *);
63 /* For message tests */
64 enum seq_index
66 PARENT_SEQ_INDEX,
67 CHILD_SEQ_INDEX,
68 NUM_MSG_SEQUENCES
71 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
73 static void init_funcs(void)
75 HMODULE user32 = GetModuleHandleA("user32.dll");
76 HMODULE gdi32 = GetModuleHandleA("gdi32.dll");
77 HMODULE uxtheme = GetModuleHandleA("uxtheme.dll");
79 pShouldSystemUseDarkMode = (void *)GetProcAddress(uxtheme, MAKEINTRESOURCEA(138));
80 pShouldAppsUseDarkMode = (void *)GetProcAddress(uxtheme, MAKEINTRESOURCEA(132));
82 #define GET_PROC(module, func) \
83 p##func = (void *)GetProcAddress(module, #func); \
84 if (!p##func) \
85 trace("GetProcAddress(%s, %s) failed.\n", #module, #func);
87 GET_PROC(uxtheme, BeginBufferedPaint)
88 GET_PROC(uxtheme, BufferedPaintClear)
89 GET_PROC(uxtheme, EndBufferedPaint)
90 GET_PROC(uxtheme, DrawThemeBackgroundEx)
91 GET_PROC(uxtheme, GetBufferedPaintBits)
92 GET_PROC(uxtheme, GetBufferedPaintDC)
93 GET_PROC(uxtheme, GetBufferedPaintTargetDC)
94 GET_PROC(uxtheme, GetBufferedPaintTargetRect)
95 GET_PROC(uxtheme, GetThemeIntList)
96 GET_PROC(uxtheme, GetThemeTransitionDuration)
97 GET_PROC(uxtheme, OpenThemeDataEx)
98 GET_PROC(uxtheme, OpenThemeDataForDpi)
100 GET_PROC(user32, DisplayConfigGetDeviceInfo)
101 GET_PROC(user32, DisplayConfigSetDeviceInfo)
102 GET_PROC(user32, GetDpiForMonitorInternal)
103 GET_PROC(user32, GetDpiForSystem)
104 GET_PROC(user32, SetThreadDpiAwarenessContext)
106 GET_PROC(gdi32, D3DKMTCloseAdapter)
107 GET_PROC(gdi32, D3DKMTOpenAdapterFromGdiDisplayName)
109 #undef GET_PROC
112 static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff)
114 unsigned int diff = x > y ? x - y : y - x;
116 return diff <= max_diff;
119 /* Try to make sure pending X events have been processed before continuing */
120 static void flush_events(void)
122 MSG msg;
123 int diff = 200;
124 int min_timeout = 100;
125 DWORD time = GetTickCount() + diff;
127 while (diff > 0)
129 if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT)
130 break;
131 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
132 DispatchMessageA(&msg);
133 diff = time - GetTickCount();
137 static unsigned int get_primary_monitor_effective_dpi(void)
139 DPI_AWARENESS_CONTEXT old_context;
140 UINT dpi_x = 0, dpi_y = 0;
141 POINT point = {0, 0};
142 HMONITOR monitor;
144 if (pSetThreadDpiAwarenessContext && pGetDpiForMonitorInternal)
146 old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
147 monitor = MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY);
148 pGetDpiForMonitorInternal(monitor, 0, &dpi_x, &dpi_y);
149 pSetThreadDpiAwarenessContext(old_context);
150 return dpi_y;
153 return USER_DEFAULT_SCREEN_DPI;
156 static int get_dpi_scale_index(int dpi)
158 static const int scales[] = {100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500};
159 int scale, scale_idx;
161 scale = dpi * 100 / 96;
162 for (scale_idx = 0; scale_idx < ARRAY_SIZE(scales); ++scale_idx)
164 if (scales[scale_idx] == scale)
165 return scale_idx;
168 return -1;
171 static BOOL set_primary_monitor_effective_dpi(unsigned int primary_dpi)
173 D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter_gdi_desc;
174 DISPLAYCONFIG_GET_SOURCE_DPI_SCALE get_scale_req;
175 DISPLAYCONFIG_SET_SOURCE_DPI_SCALE set_scale_req;
176 int current_scale_idx, target_scale_idx;
177 D3DKMT_CLOSEADAPTER close_adapter_desc;
178 BOOL ret = FALSE;
179 LONG error;
181 #define CHECK_FUNC(func) \
182 if (!p##func) \
184 win_skip("%s() is unavailable.\n", #func); \
185 ret = TRUE; \
188 CHECK_FUNC(D3DKMTCloseAdapter)
189 CHECK_FUNC(D3DKMTOpenAdapterFromGdiDisplayName)
190 CHECK_FUNC(DisplayConfigGetDeviceInfo)
191 todo_wine CHECK_FUNC(DisplayConfigSetDeviceInfo)
192 if (ret) return FALSE;
194 #undef CHECK_FUNC
196 lstrcpyW(open_adapter_gdi_desc.DeviceName, L"\\\\.\\DISPLAY1");
197 if (pD3DKMTOpenAdapterFromGdiDisplayName(&open_adapter_gdi_desc) == STATUS_PROCEDURE_NOT_FOUND)
199 win_skip("D3DKMTOpenAdapterFromGdiDisplayName() is unavailable.\n");
200 return FALSE;
203 get_scale_req.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_DPI_SCALE;
204 get_scale_req.header.size = sizeof(get_scale_req);
205 get_scale_req.header.adapterId = open_adapter_gdi_desc.AdapterLuid;
206 get_scale_req.header.id = open_adapter_gdi_desc.VidPnSourceId;
207 error = pDisplayConfigGetDeviceInfo(&get_scale_req.header);
208 if (error != NO_ERROR)
210 skip("DisplayConfigGetDeviceInfo failed, returned %ld.\n", error);
211 goto failed;
214 current_scale_idx = get_dpi_scale_index(get_primary_monitor_effective_dpi());
215 if (current_scale_idx == -1)
217 skip("Failed to find current scale index.\n");
218 goto failed;
221 target_scale_idx = get_dpi_scale_index(primary_dpi);
222 if (target_scale_idx < get_scale_req.minRelativeScaleStep
223 || target_scale_idx > get_scale_req.maxRelativeScaleStep)
225 skip("DPI %d is not available.\n", primary_dpi);
226 goto failed;
229 set_scale_req.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_SOURCE_DPI_SCALE;
230 set_scale_req.header.size = sizeof(set_scale_req);
231 set_scale_req.header.adapterId = open_adapter_gdi_desc.AdapterLuid;
232 set_scale_req.header.id = open_adapter_gdi_desc.VidPnSourceId;
233 set_scale_req.relativeScaleStep = get_scale_req.curRelativeScaleStep + (target_scale_idx - current_scale_idx);
234 error = pDisplayConfigSetDeviceInfo(&set_scale_req.header);
235 if (error == NO_ERROR)
236 ret = get_primary_monitor_effective_dpi() == primary_dpi;
237 flush_events();
239 failed:
240 close_adapter_desc.hAdapter = open_adapter_gdi_desc.hAdapter;
241 pD3DKMTCloseAdapter(&close_adapter_desc);
242 return ret;
245 static LRESULT WINAPI TestSetWindowThemeParentProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
247 static LONG defwndproc_counter;
248 struct message msg = {0};
249 LRESULT ret;
251 /* Only care about WM_THEMECHANGED */
252 if (message != WM_THEMECHANGED)
253 return DefWindowProcA(hwnd, message, wParam, lParam);
255 msg.message = message;
256 msg.flags = sent | wparam | lparam;
257 if (defwndproc_counter)
258 msg.flags |= defwinproc;
259 msg.wParam = wParam;
260 msg.lParam = lParam;
261 add_message(sequences, PARENT_SEQ_INDEX, &msg);
263 InterlockedIncrement(&defwndproc_counter);
264 ret = DefWindowProcA(hwnd, message, wParam, lParam);
265 InterlockedDecrement(&defwndproc_counter);
266 return ret;
269 static LRESULT WINAPI TestSetWindowThemeChildProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
271 static LONG defwndproc_counter;
272 struct message msg = {0};
273 LRESULT ret;
275 /* Only care about WM_THEMECHANGED */
276 if (message != WM_THEMECHANGED)
277 return DefWindowProcA(hwnd, message, wParam, lParam);
279 msg.message = message;
280 msg.flags = sent | wparam | lparam;
281 if (defwndproc_counter)
282 msg.flags |= defwinproc;
283 msg.wParam = wParam;
284 msg.lParam = lParam;
285 add_message(sequences, CHILD_SEQ_INDEX, &msg);
287 InterlockedIncrement(&defwndproc_counter);
288 ret = DefWindowProcA(hwnd, message, wParam, lParam);
289 InterlockedDecrement(&defwndproc_counter);
290 return ret;
293 static void test_IsThemed(void)
295 BOOL bThemeActive;
296 BOOL bAppThemed;
298 bThemeActive = IsThemeActive();
299 trace("Theming is %s\n", (bThemeActive) ? "active" : "inactive");
301 bAppThemed = IsAppThemed();
302 trace("Test executable is %s\n", (bAppThemed) ? "themed" : "not themed");
305 static void test_IsThemePartDefined(void)
307 BOOL is_theme_active, ret;
308 HTHEME theme = NULL;
309 DWORD error;
310 HWND hwnd;
311 int i;
313 static const struct
315 const WCHAR *class_name;
316 int part;
317 int state;
318 BOOL defined;
319 DWORD error;
321 tests[] =
323 {NULL, 0, 0, FALSE, E_HANDLE},
324 {NULL, 0xdeadbeef, 0, FALSE, E_HANDLE},
325 {L"Button", 0xdeadbeef, 0, FALSE, NO_ERROR},
326 {L"Button", BP_PUSHBUTTON, 0, TRUE, NO_ERROR},
327 {L"Button", BP_PUSHBUTTON, PBS_NORMAL, FALSE, NO_ERROR},
328 {L"Button", BP_PUSHBUTTON, PBS_DEFAULTED_ANIMATING, FALSE, NO_ERROR},
329 {L"Button", BP_PUSHBUTTON, PBS_DEFAULTED_ANIMATING + 1, FALSE, NO_ERROR},
330 {L"Edit", EP_EDITTEXT, 0, TRUE, NO_ERROR},
331 {L"Edit", EP_EDITTEXT, ETS_NORMAL, FALSE, NO_ERROR},
332 {L"Edit", EP_EDITTEXT, ETS_FOCUSED, FALSE, NO_ERROR},
335 is_theme_active = IsThemeActive();
336 hwnd = CreateWindowA(WC_STATICA, "", WS_POPUP, 0, 0, 1, 1, 0, 0, 0, NULL);
337 ok(hwnd != NULL, "CreateWindowA failed, error %#lx.\n", GetLastError());
339 for (i = 0; i < ARRAY_SIZE(tests); ++i)
341 if (!is_theme_active && tests[i].class_name)
342 continue;
344 winetest_push_context("class %s part %d state %d", wine_dbgstr_w(tests[i].class_name),
345 tests[i].part, tests[i].state);
347 if (tests[i].class_name)
349 theme = OpenThemeData(hwnd, tests[i].class_name);
350 ok(theme != NULL, "OpenThemeData failed, error %#lx.\n", GetLastError());
353 SetLastError(0xdeadbeef);
354 ret = IsThemePartDefined(theme, tests[i].part, tests[i].state);
355 error = GetLastError();
356 ok(ret == tests[i].defined, "Expected %d.\n", tests[i].defined);
357 ok(error == tests[i].error, "Expected %#lx, got %#lx.\n", tests[i].error, error);
359 if (theme)
361 CloseThemeData(theme);
362 theme = NULL;
364 winetest_pop_context();
367 DestroyWindow(hwnd);
370 static void test_GetWindowTheme(void)
372 HTHEME hTheme;
373 HWND hWnd;
375 SetLastError(0xdeadbeef);
376 hTheme = GetWindowTheme(NULL);
377 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
378 ok( GetLastError() == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", GetLastError() );
380 /* Only do the bare minimum to get a valid hwnd */
381 hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
382 ok(hWnd != NULL, "Failed to create a test window.\n");
384 SetLastError(0xdeadbeef);
385 hTheme = GetWindowTheme(hWnd);
386 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
387 ok( GetLastError() == 0xdeadbeef,
388 "Expected 0xdeadbeef, got 0x%08lx\n",
389 GetLastError());
391 DestroyWindow(hWnd);
394 static const struct message SetWindowThemeSeq[] =
396 {WM_THEMECHANGED, sent},
400 static const struct message EmptySeq[] =
405 static void test_SetWindowTheme(void)
407 WNDCLASSA cls = {0};
408 HWND hWnd, child;
409 HTHEME hTheme;
410 HRESULT hRes;
411 MSG msg;
413 hRes = SetWindowTheme(NULL, NULL, NULL);
414 ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", hRes);
416 /* Test that WM_THEMECHANGED is sent and not posted to windows */
417 cls.hInstance = GetModuleHandleA(0);
418 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
419 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
420 cls.lpfnWndProc = TestSetWindowThemeParentProcA;
421 cls.lpszClassName = "TestSetWindowThemeParentClass";
422 RegisterClassA(&cls);
424 cls.lpfnWndProc = TestSetWindowThemeChildProcA;
425 cls.lpszClassName = "TestSetWindowThemeChildClass";
426 RegisterClassA(&cls);
428 hWnd = CreateWindowA("TestSetWindowThemeParentClass", "parent",
429 WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, 0, 0, 0, NULL);
430 ok(!!hWnd, "Failed to create a parent window.\n");
431 child = CreateWindowA("TestSetWindowThemeChildClass", "child", WS_CHILD | WS_VISIBLE, 0, 0, 50,
432 50, hWnd, 0, 0, NULL);
433 ok(!!child, "Failed to create a child window.\n");
434 flush_events();
435 flush_sequences(sequences, NUM_MSG_SEQUENCES);
437 hRes = SetWindowTheme(hWnd, NULL, NULL);
438 ok(hRes == S_OK, "Expected %#lx, got %#lx.\n", S_OK, hRes);
439 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
441 struct message recv_msg = {0};
443 if (msg.message == WM_THEMECHANGED)
445 recv_msg.message = msg.message;
446 recv_msg.flags = posted | wparam | lparam;
447 recv_msg.wParam = msg.wParam;
448 recv_msg.lParam = msg.lParam;
449 add_message(sequences, msg.hwnd == hWnd ? PARENT_SEQ_INDEX : CHILD_SEQ_INDEX, &recv_msg);
451 DispatchMessageA(&msg);
453 ok_sequence(sequences, PARENT_SEQ_INDEX, SetWindowThemeSeq, "SetWindowTheme parent", FALSE);
454 ok_sequence(sequences, CHILD_SEQ_INDEX, EmptySeq, "SetWindowTheme child", FALSE);
455 DestroyWindow(hWnd);
456 UnregisterClassA("TestSetWindowThemeParentClass", GetModuleHandleA(0));
457 UnregisterClassA("TestSetWindowThemeChildClass", GetModuleHandleA(0));
459 /* Only do the bare minimum to get a valid hwnd */
460 hWnd = CreateWindowExA(0, "button", "test", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
461 ok(hWnd != NULL, "Failed to create a test window.\n");
463 hRes = SetWindowTheme(hWnd, NULL, NULL);
464 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
466 if (IsThemeActive())
468 hTheme = OpenThemeData(hWnd, L"Button");
469 ok(!!hTheme, "OpenThemeData failed.\n");
470 CloseThemeData(hTheme);
472 hRes = SetWindowTheme(hWnd, L"deadbeef", NULL);
473 ok(hRes == S_OK, "Expected S_OK, got 0x%08lx.\n", hRes);
475 hTheme = OpenThemeData(hWnd, L"Button");
476 ok(!!hTheme, "OpenThemeData failed.\n");
477 CloseThemeData(hTheme);
479 else
481 skip("No active theme, skipping rest of SetWindowTheme tests.\n");
484 DestroyWindow(hWnd);
487 static void test_OpenThemeData(void)
489 HTHEME hTheme, hTheme2;
490 HWND hWnd;
491 BOOL bThemeActive;
492 HRESULT hRes;
493 BOOL bTPDefined;
495 const WCHAR szInvalidClassList[] = L"DEADBEEF";
496 const WCHAR szButtonClassList[] = L"Button";
497 const WCHAR szButtonClassList2[] = L"bUtToN";
498 const WCHAR szClassList[] = L"Button;ListBox";
500 bThemeActive = IsThemeActive();
502 /* All NULL */
503 SetLastError(0xdeadbeef);
504 hTheme = OpenThemeData(NULL, NULL);
505 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
506 ok( GetLastError() == E_POINTER,
507 "Expected GLE() to be E_POINTER, got 0x%08lx\n",
508 GetLastError());
510 /* A NULL hWnd and an invalid classlist */
511 SetLastError(0xdeadbeef);
512 hTheme = OpenThemeData(NULL, szInvalidClassList);
513 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
514 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
515 E_PROP_ID_UNSUPPORTED, GetLastError() );
517 SetLastError(0xdeadbeef);
518 hTheme = OpenThemeData(NULL, szClassList);
519 if (bThemeActive)
521 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
522 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
524 else
526 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
527 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
528 E_PROP_ID_UNSUPPORTED, GetLastError() );
531 /* Only do the bare minimum to get a valid hdc */
532 hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
533 if (!hWnd) return;
535 SetLastError(0xdeadbeef);
536 hTheme = OpenThemeData(hWnd, NULL);
537 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
538 ok( GetLastError() == E_POINTER,
539 "Expected GLE() to be E_POINTER, got 0x%08lx\n",
540 GetLastError());
542 SetLastError(0xdeadbeef);
543 hTheme = OpenThemeData(hWnd, szInvalidClassList);
544 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
545 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
546 E_PROP_ID_UNSUPPORTED, GetLastError() );
548 if (!bThemeActive)
550 SetLastError(0xdeadbeef);
551 hTheme = OpenThemeData(hWnd, szButtonClassList);
552 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
553 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
554 E_PROP_ID_UNSUPPORTED, GetLastError() );
555 skip("No active theme, skipping rest of OpenThemeData tests\n");
556 return;
559 /* Only do the next checks if we have an active theme */
561 SetLastError(0xdeadbeef);
562 hTheme = OpenThemeData(hWnd, L"dead::beef;explorer::treeview");
563 ok(!hTheme, "OpenThemeData() should fail\n");
564 ok(GetLastError() == E_PROP_ID_UNSUPPORTED, "Got unexpected %#lx.\n", GetLastError());
566 SetLastError(0xdeadbeef);
567 hTheme = OpenThemeData(hWnd, L"explorer::treeview");
568 todo_wine
569 ok(hTheme != NULL, "OpenThemeData() failed\n");
570 todo_wine
571 ok(GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError());
572 CloseThemeData(hTheme);
574 SetLastError(0xdeadbeef);
575 hTheme = OpenThemeData(hWnd, L"deadbeef::treeview;dead::beef");
576 todo_wine
577 ok(hTheme != NULL, "OpenThemeData() failed\n");
578 todo_wine
579 ok(GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError());
580 CloseThemeData(hTheme);
582 SetLastError(0xdeadbeef);
583 hTheme = OpenThemeData(hWnd, szButtonClassList);
584 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
585 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
586 CloseThemeData(hTheme);
588 /* Test with bUtToN instead of Button */
589 SetLastError(0xdeadbeef);
590 hTheme = OpenThemeData(hWnd, szButtonClassList2);
591 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
592 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
593 CloseThemeData(hTheme);
595 SetLastError(0xdeadbeef);
596 hTheme = OpenThemeData(hWnd, szClassList);
597 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
598 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
600 /* GetWindowTheme should return the last handle opened by OpenThemeData */
601 SetLastError(0xdeadbeef);
602 hTheme2 = GetWindowTheme(hWnd);
603 ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n",
604 hTheme, hTheme2);
605 ok( GetLastError() == 0xdeadbeef,
606 "Expected 0xdeadbeef, got 0x%08lx\n",
607 GetLastError());
609 SetLastError(0xdeadbeef);
610 bTPDefined = IsThemePartDefined(hTheme, 0, 0);
611 todo_wine
612 ok( bTPDefined == FALSE, "Expected FALSE\n" );
613 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
615 hRes = CloseThemeData(hTheme);
616 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
618 /* Close a second time */
619 hRes = CloseThemeData(hTheme);
620 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
622 /* See if closing makes a difference for GetWindowTheme */
623 SetLastError(0xdeadbeef);
624 hTheme2 = NULL;
625 hTheme2 = GetWindowTheme(hWnd);
626 ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n",
627 hTheme, hTheme2);
628 ok( GetLastError() == 0xdeadbeef,
629 "Expected 0xdeadbeef, got 0x%08lx\n",
630 GetLastError());
632 DestroyWindow(hWnd);
635 static void test_OpenThemeDataEx(void)
637 HTHEME hTheme;
638 HWND hWnd;
639 BOOL bThemeActive;
641 const WCHAR szInvalidClassList[] = L"DEADBEEF";
642 const WCHAR szButtonClassList[] = L"Button";
643 const WCHAR szButtonClassList2[] = L"bUtToN";
644 const WCHAR szClassList[] = L"Button;ListBox";
646 if (!pOpenThemeDataEx)
648 win_skip("OpenThemeDataEx not available\n");
649 return;
652 bThemeActive = IsThemeActive();
654 /* All NULL */
655 SetLastError(0xdeadbeef);
656 hTheme = pOpenThemeDataEx(NULL, NULL, 0);
657 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
658 ok( GetLastError() == E_POINTER,
659 "Expected GLE() to be E_POINTER, got 0x%08lx\n",
660 GetLastError());
662 /* A NULL hWnd and an invalid classlist without flags */
663 SetLastError(0xdeadbeef);
664 hTheme = pOpenThemeDataEx(NULL, szInvalidClassList, 0);
665 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
666 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
667 E_PROP_ID_UNSUPPORTED, GetLastError() );
669 SetLastError(0xdeadbeef);
670 hTheme = pOpenThemeDataEx(NULL, szClassList, 0);
671 if (bThemeActive)
673 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
674 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
676 else
678 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
679 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
680 E_PROP_ID_UNSUPPORTED, GetLastError() );
683 /* Only do the bare minimum to get a valid hdc */
684 hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
685 if (!hWnd) return;
687 SetLastError(0xdeadbeef);
688 hTheme = pOpenThemeDataEx(hWnd, NULL, 0);
689 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
690 ok( GetLastError() == E_POINTER,
691 "Expected GLE() to be E_POINTER, got 0x%08lx\n",
692 GetLastError());
694 SetLastError(0xdeadbeef);
695 hTheme = pOpenThemeDataEx(hWnd, szInvalidClassList, 0);
696 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
697 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
698 E_PROP_ID_UNSUPPORTED, GetLastError() );
700 if (!bThemeActive)
702 SetLastError(0xdeadbeef);
703 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0);
704 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
705 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08lx, got 0x%08lx\n",
706 E_PROP_ID_UNSUPPORTED, GetLastError() );
707 skip("No active theme, skipping rest of OpenThemeDataEx tests\n");
708 return;
711 /* Only do the next checks if we have an active theme */
713 SetLastError(0xdeadbeef);
714 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0);
715 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
716 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
718 SetLastError(0xdeadbeef);
719 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, OTD_FORCE_RECT_SIZING);
720 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
721 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
723 SetLastError(0xdeadbeef);
724 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, OTD_NONCLIENT);
725 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
726 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
728 SetLastError(0xdeadbeef);
729 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0x3);
730 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
731 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
733 /* Test with bUtToN instead of Button */
734 SetLastError(0xdeadbeef);
735 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList2, 0);
736 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
737 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
739 SetLastError(0xdeadbeef);
740 hTheme = pOpenThemeDataEx(hWnd, szClassList, 0);
741 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
742 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08lx\n", GetLastError() );
744 DestroyWindow(hWnd);
747 static void test_OpenThemeDataForDpi(void)
749 BOOL is_theme_active;
750 HTHEME htheme;
752 if (!pOpenThemeDataForDpi)
754 win_skip("OpenThemeDataForDpi is unavailable.\n");
755 return;
758 is_theme_active = IsThemeActive();
759 SetLastError(0xdeadbeef);
760 htheme = pOpenThemeDataForDpi(NULL, WC_BUTTONW, 96);
761 if (is_theme_active)
763 ok(!!htheme, "Got a NULL handle.\n");
764 ok(GetLastError() == NO_ERROR, "Expected error %u, got %lu.\n", NO_ERROR, GetLastError());
765 CloseThemeData(htheme);
767 else
769 ok(!htheme, "Got a non-NULL handle.\n");
770 ok(GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected error %lu, got %lu.\n",
771 E_PROP_ID_UNSUPPORTED, GetLastError());
775 static void test_GetCurrentThemeName(void)
777 BOOL bThemeActive;
778 HRESULT hRes;
779 WCHAR currentTheme[MAX_PATH];
780 WCHAR currentColor[MAX_PATH];
781 WCHAR currentSize[MAX_PATH];
783 bThemeActive = IsThemeActive();
785 /* All NULLs */
786 hRes = GetCurrentThemeName(NULL, 0, NULL, 0, NULL, 0);
787 if (bThemeActive)
788 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
789 else
790 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
792 /* Number of characters given is 0 */
793 hRes = GetCurrentThemeName(currentTheme, 0, NULL, 0, NULL, 0);
794 if (bThemeActive)
795 ok( hRes == S_OK || broken(hRes == E_FAIL /* WinXP SP1 */), "Expected S_OK, got 0x%08lx\n", hRes);
796 else
797 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
799 hRes = GetCurrentThemeName(currentTheme, 2, NULL, 0, NULL, 0);
800 if (bThemeActive)
801 todo_wine
802 ok(hRes == E_NOT_SUFFICIENT_BUFFER ||
803 broken(hRes == E_FAIL /* WinXP SP1 */),
804 "Expected E_NOT_SUFFICIENT_BUFFER, got 0x%08lx\n", hRes);
805 else
806 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
808 /* The same is true if the number of characters is too small for Color and/or Size */
809 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor, 2,
810 currentSize, ARRAY_SIZE(currentSize));
811 if (bThemeActive)
812 todo_wine
813 ok(hRes == E_NOT_SUFFICIENT_BUFFER ||
814 broken(hRes == E_FAIL /* WinXP SP1 */),
815 "Expected E_NOT_SUFFICIENT_BUFFER, got 0x%08lx\n", hRes);
816 else
817 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
819 /* Given number of characters is correct */
820 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), NULL, 0, NULL, 0);
821 if (bThemeActive)
822 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
823 else
824 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
826 /* Given number of characters for the theme name is too large */
827 hRes = GetCurrentThemeName(currentTheme, sizeof(currentTheme), NULL, 0, NULL, 0);
828 if (bThemeActive)
829 ok( hRes == E_POINTER || hRes == S_OK, "Expected E_POINTER or S_OK, got 0x%08lx\n", hRes);
830 else
831 ok( hRes == E_PROP_ID_UNSUPPORTED ||
832 hRes == E_POINTER, /* win2k3 */
833 "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
835 /* The too large case is only for the theme name, not for color name or size name */
836 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
837 sizeof(currentTheme), currentSize, ARRAY_SIZE(currentSize));
838 if (bThemeActive)
839 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
840 else
841 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
843 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
844 ARRAY_SIZE(currentTheme), currentSize, sizeof(currentSize));
845 if (bThemeActive)
846 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
847 else
848 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
850 /* Correct call */
851 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
852 ARRAY_SIZE(currentColor), currentSize, ARRAY_SIZE(currentSize));
853 if (bThemeActive)
854 ok( hRes == S_OK, "Expected S_OK, got 0x%08lx\n", hRes);
855 else
856 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08lx\n", hRes);
859 static void test_CloseThemeData(void)
861 HRESULT hRes;
863 hRes = CloseThemeData(NULL);
864 ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", hRes);
865 hRes = CloseThemeData(INVALID_HANDLE_VALUE);
866 ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", hRes);
867 hRes = CloseThemeData((HTHEME)0xdeadbeef);
868 ok(hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08lx\n", hRes);
871 static void test_buffer_dc_props(HDC hdc, const RECT *rect)
873 static const XFORM ident = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
874 XFORM xform;
875 POINT org;
876 RECT box;
877 BOOL ret;
879 ret = GetWorldTransform(hdc, &xform);
880 ok(ret, "Failed to get world transform\n");
881 ok(!memcmp(&xform, &ident, sizeof(xform)), "Unexpected world transform\n");
883 ret = GetViewportOrgEx(hdc, &org);
884 ok(ret, "Failed to get vport origin\n");
885 ok(org.x == 0 && org.y == 0, "Unexpected vport origin\n");
887 ret = GetWindowOrgEx(hdc, &org);
888 ok(ret, "Failed to get vport origin\n");
889 ok(org.x == rect->left && org.y == rect->top, "Unexpected window origin\n");
891 ret = GetClipBox(hdc, &box);
892 ok(ret, "Failed to get clip box\n");
893 ok(box.left == rect->left && box.top == rect->top, "Unexpected clip box\n");
895 ok(GetGraphicsMode(hdc) == GM_COMPATIBLE, "wrong graphics mode\n");
898 static void test_buffered_paint(void)
900 HDC target, src, hdc, screen_dc;
901 BP_PAINTPARAMS params = { 0 };
902 BP_BUFFERFORMAT format;
903 HPAINTBUFFER buffer;
904 RECT rect, rect2;
905 RGBQUAD *bits;
906 HBITMAP hbm;
907 HRESULT hr;
908 int row;
910 if (!pBeginBufferedPaint)
912 win_skip("Buffered painting API is not supported.\n");
913 return;
916 buffer = pBeginBufferedPaint(NULL, NULL, BPBF_COMPATIBLEBITMAP,
917 NULL, NULL);
918 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
920 target = CreateCompatibleDC(0);
921 buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
922 NULL, NULL);
923 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
925 params.cbSize = sizeof(params);
926 buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
927 &params, NULL);
928 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
930 src = (void *)0xdeadbeef;
931 buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
932 &params, &src);
933 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
934 ok(src == NULL, "Unexpected buffered dc %p\n", src);
936 /* target rect is mandatory */
937 SetRectEmpty(&rect);
938 src = (void *)0xdeadbeef;
939 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
940 &params, &src);
941 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
942 ok(src == NULL, "Unexpected buffered dc %p\n", src);
944 /* inverted rectangle */
945 SetRect(&rect, 10, 0, 5, 5);
946 src = (void *)0xdeadbeef;
947 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
948 &params, &src);
949 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
950 ok(src == NULL, "Unexpected buffered dc %p\n", src);
952 SetRect(&rect, 0, 10, 5, 0);
953 src = (void *)0xdeadbeef;
954 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
955 &params, &src);
956 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
957 ok(src == NULL, "Unexpected buffered dc %p\n", src);
959 /* valid rectangle, no target dc */
960 SetRect(&rect, 0, 0, 5, 5);
961 src = (void *)0xdeadbeef;
962 buffer = pBeginBufferedPaint(NULL, &rect, BPBF_COMPATIBLEBITMAP,
963 &params, &src);
964 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
965 ok(src == NULL, "Unexpected buffered dc %p\n", src);
967 SetRect(&rect, 0, 0, 5, 5);
968 src = NULL;
969 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
970 &params, &src);
971 ok(buffer != NULL, "Unexpected buffer %p\n", buffer);
972 ok(src != NULL, "Expected buffered dc\n");
973 hr = pEndBufferedPaint(buffer, FALSE);
974 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
976 SetRect(&rect, 0, 0, 5, 5);
977 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
978 &params, &src);
979 ok(buffer != NULL, "Unexpected buffer %p\n", buffer);
981 /* clearing */
982 hr = pBufferedPaintClear(NULL, NULL);
983 todo_wine
984 ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
986 hr = pBufferedPaintClear(buffer, NULL);
987 todo_wine
988 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
990 /* access buffer attributes */
991 hdc = pGetBufferedPaintDC(buffer);
992 ok(hdc == src, "Unexpected hdc, %p, buffered dc %p\n", hdc, src);
994 hdc = pGetBufferedPaintTargetDC(buffer);
995 ok(hdc == target, "Unexpected target hdc %p, original %p\n", hdc, target);
997 hr = pGetBufferedPaintTargetRect(NULL, NULL);
998 ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
1000 hr = pGetBufferedPaintTargetRect(buffer, NULL);
1001 ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
1003 hr = pGetBufferedPaintTargetRect(NULL, &rect2);
1004 ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
1006 SetRectEmpty(&rect2);
1007 hr = pGetBufferedPaintTargetRect(buffer, &rect2);
1008 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
1009 ok(EqualRect(&rect, &rect2), "Wrong target rect\n");
1011 hr = pEndBufferedPaint(buffer, FALSE);
1012 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
1014 /* invalid buffer handle */
1015 hr = pEndBufferedPaint(NULL, FALSE);
1016 ok(hr == E_INVALIDARG, "Unexpected return code %#lx\n", hr);
1018 hdc = pGetBufferedPaintDC(NULL);
1019 ok(hdc == NULL, "Unexpected hdc %p\n", hdc);
1021 hdc = pGetBufferedPaintTargetDC(NULL);
1022 ok(hdc == NULL, "Unexpected target hdc %p\n", hdc);
1024 hr = pGetBufferedPaintTargetRect(NULL, &rect2);
1025 ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
1027 hr = pGetBufferedPaintTargetRect(NULL, NULL);
1028 ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
1030 bits = (void *)0xdeadbeef;
1031 row = 10;
1032 hr = pGetBufferedPaintBits(NULL, &bits, &row);
1033 ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
1034 ok(row == 10, "Unexpected row count %d\n", row);
1035 ok(bits == (void *)0xdeadbeef, "Unexpected data pointer %p\n", bits);
1037 hr = pGetBufferedPaintBits(NULL, NULL, NULL);
1038 ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
1040 hr = pGetBufferedPaintBits(NULL, &bits, NULL);
1041 ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
1043 hr = pGetBufferedPaintBits(NULL, NULL, &row);
1044 ok(hr == E_POINTER, "Unexpected return code %#lx\n", hr);
1046 screen_dc = GetDC(0);
1048 hdc = CreateCompatibleDC(screen_dc);
1049 ok(hdc != NULL, "Failed to create a DC\n");
1050 hbm = CreateCompatibleBitmap(screen_dc, 64, 64);
1051 ok(hbm != NULL, "Failed to create a bitmap\n");
1052 SelectObject(hdc, hbm);
1054 ReleaseDC(0, screen_dc);
1056 SetRect(&rect, 1, 2, 34, 56);
1058 buffer = pBeginBufferedPaint(hdc, &rect, BPBF_COMPATIBLEBITMAP, NULL, &src);
1059 test_buffer_dc_props(src, &rect);
1060 hr = pEndBufferedPaint(buffer, FALSE);
1061 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
1063 DeleteObject(hbm);
1064 DeleteDC(hdc);
1066 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP, NULL, &src);
1067 test_buffer_dc_props(src, &rect);
1068 hr = pEndBufferedPaint(buffer, FALSE);
1069 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
1071 /* access buffer bits */
1072 for (format = BPBF_COMPATIBLEBITMAP; format <= BPBF_TOPDOWNMONODIB; format++)
1074 buffer = pBeginBufferedPaint(target, &rect, format, &params, &src);
1076 /* only works for DIB buffers */
1077 bits = NULL;
1078 row = 0;
1079 hr = pGetBufferedPaintBits(buffer, &bits, &row);
1080 if (format == BPBF_COMPATIBLEBITMAP)
1081 ok(hr == E_FAIL, "Unexpected return code %#lx\n", hr);
1082 else
1084 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
1085 ok(bits != NULL, "Bitmap bits %p\n", bits);
1086 ok(row >= (rect.right - rect.left), "format %d: bitmap width %d\n", format, row);
1089 hr = pEndBufferedPaint(buffer, FALSE);
1090 ok(hr == S_OK, "Unexpected return code %#lx\n", hr);
1093 DeleteDC(target);
1096 static void test_GetThemePartSize(void)
1098 static const DWORD enabled = 1;
1099 int old_dpi, current_dpi, system_dpi, target_dpi, min_dpi;
1100 DPI_AWARENESS_CONTEXT old_context;
1101 int i, expected, stretch_mark = 0;
1102 HTHEME htheme = NULL;
1103 HWND hwnd = NULL;
1104 HKEY key = NULL;
1105 HDC hdc = NULL;
1106 HRESULT hr;
1107 SIZE size;
1109 if (!pSetThreadDpiAwarenessContext || !pGetDpiForSystem)
1111 win_skip("SetThreadDpiAwarenessContext() or GetDpiForSystem() is unavailable.\n");
1112 return;
1115 /* Set IgnorePerProcessSystemDPIToast to 1 to disable "fix blurry apps popup" on Windows 10,
1116 * which may interfere other tests because it steals focus */
1117 RegOpenKeyA(HKEY_CURRENT_USER, "Control Panel\\Desktop", &key);
1118 RegSetValueExA(key, "IgnorePerProcessSystemDPIToast", 0, REG_DWORD, (const BYTE *)&enabled,
1119 sizeof(enabled));
1121 old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
1122 current_dpi = get_primary_monitor_effective_dpi();
1123 old_dpi = current_dpi;
1124 system_dpi = pGetDpiForSystem();
1125 target_dpi = system_dpi;
1127 hwnd = CreateWindowA("Button", "Test", WS_POPUP, 100, 100, 100, 100, NULL, NULL, NULL, NULL);
1128 hdc = GetDC(hwnd);
1130 /* Test in the current DPI */
1131 /* Test that OpenThemeData() with NULL window handle uses system DPI even if DPI changes while running */
1132 htheme = OpenThemeData(NULL, WC_BUTTONW);
1133 if (!htheme)
1135 skip("Theming is inactive.\n");
1136 goto done;
1139 /* TMT_TRUESIZESTRETCHMARK is present, choose the next minimum DPI */
1140 hr = GetThemeInt(htheme, BP_CHECKBOX, CBS_CHECKEDNORMAL, TMT_TRUESIZESTRETCHMARK, &stretch_mark);
1141 if (SUCCEEDED(hr) && stretch_mark > 0)
1143 for (i = 7; i >= 1; --i)
1145 hr = GetThemeInt(htheme, BP_CHECKBOX, CBS_CHECKEDNORMAL,
1146 i <= 5 ? TMT_MINDPI1 + i - 1 : TMT_MINDPI6 + i - 6, &min_dpi);
1147 if (SUCCEEDED(hr) && min_dpi <= system_dpi)
1149 target_dpi = min_dpi;
1150 break;
1154 expected = MulDiv(13, target_dpi, 96);
1156 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1157 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1158 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1159 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1160 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1161 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1162 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1163 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1164 CloseThemeData(htheme);
1166 /* Test in 192 DPI */
1167 /* DPI needs to be 50% larger than 96 to avoid the effect of TrueSizeStretchMark */
1168 if (current_dpi != 192 && !set_primary_monitor_effective_dpi(192))
1170 skip("Failed to set primary monitor dpi to 192.\n");
1171 goto done;
1174 /* Test that OpenThemeData() with NULL window handle uses system DPI even if DPI changes while running */
1175 expected = MulDiv(13, target_dpi, 96);
1176 htheme = OpenThemeData(NULL, WC_BUTTONW);
1177 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1178 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1179 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1180 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1181 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1182 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1183 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1184 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1185 CloseThemeData(htheme);
1187 /* Test that OpenThemeData() with a window handle use window DPI */
1188 expected = 26;
1189 htheme = OpenThemeData(hwnd, WC_BUTTONW);
1190 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1191 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1192 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1193 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1194 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1195 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1196 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1197 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1199 /* Test in 96 DPI */
1200 if (!set_primary_monitor_effective_dpi(96))
1202 skip("Failed to set primary monitor dpi to 96.\n");
1203 CloseThemeData(htheme);
1204 goto done;
1207 /* Test that theme part size doesn't change even if DPI is changed */
1208 expected = 26;
1209 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1210 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1211 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1212 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1213 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1214 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1215 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1216 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1218 /* Test that theme part size changes after DPI is changed and theme handle is reopened.
1219 * If DPI awareness context is not DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, theme part size
1220 * still doesn't change, even after the theme handle is reopened. */
1221 CloseThemeData(htheme);
1222 htheme = OpenThemeData(hwnd, WC_BUTTONW);
1223 expected = 13;
1224 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1225 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1226 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1227 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1228 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1229 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1230 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1231 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1232 CloseThemeData(htheme);
1234 /* Test that OpenThemeData() with NULL window handle use system DPI even if DPI changes while running */
1235 expected = MulDiv(13, target_dpi, 96);
1236 htheme = OpenThemeData(NULL, WC_BUTTONW);
1237 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1238 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1239 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1240 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1241 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1242 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
1243 ok(compare_uint(size.cx, expected, 1) && compare_uint(size.cy, expected, 1),
1244 "Got unexpected size %ldx%ld.\n", size.cx, size.cy);
1245 CloseThemeData(htheme);
1247 done:
1248 if (hdc)
1249 ReleaseDC(hwnd, hdc);
1250 if (hwnd)
1251 DestroyWindow(hwnd);
1252 if (get_primary_monitor_effective_dpi() != old_dpi)
1253 set_primary_monitor_effective_dpi(old_dpi);
1254 if (key)
1256 RegDeleteValueA(key, "IgnorePerProcessSystemDPIToast");
1257 RegCloseKey(key);
1259 pSetThreadDpiAwarenessContext(old_context);
1262 static void test_EnableTheming(void)
1264 WCHAR old_color_string[13], new_color_string[13], color_string[13];
1265 BOOL old_gradient_caption, new_gradient_caption, gradient_caption;
1266 BOOL old_flat_menu, new_flat_menu, flat_menu;
1267 LOGFONTW old_logfont, new_logfont, logfont;
1268 NONCLIENTMETRICSW old_ncm, new_ncm, ncm;
1269 DPI_AWARENESS_CONTEXT old_context = 0;
1270 COLORREF old_color, new_color;
1271 BOOL is_theme_active, ret;
1272 DWORD size, length;
1273 HRESULT hr;
1274 LSTATUS ls;
1275 HKEY hkey;
1277 if (IsThemeActive())
1279 hr = EnableTheming(TRUE);
1280 ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
1281 ok(IsThemeActive(), "Expected theming active.\n");
1283 /* Only run in interactive mode because once theming is disabled, it can't be turned back on
1284 * via EnableTheming() */
1285 if (winetest_interactive)
1287 if (pSetThreadDpiAwarenessContext)
1289 old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
1291 else if (get_primary_monitor_effective_dpi() != 96)
1293 skip("DPI isn't 96, skipping.\n");
1294 return;
1297 /* Read current system metrics */
1298 old_color = GetSysColor(COLOR_SCROLLBAR);
1299 swprintf(old_color_string, ARRAY_SIZE(old_color_string), L"%d %d %d",
1300 GetRValue(old_color), GetGValue(old_color), GetBValue(old_color));
1302 memset(&old_ncm, 0, sizeof(old_ncm));
1303 old_ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth);
1304 ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(old_ncm), &old_ncm, 0);
1305 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1307 memset(&old_logfont, 0, sizeof(old_logfont));
1308 ret = SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(old_logfont), &old_logfont, 0);
1309 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1310 ret = SystemParametersInfoW(SPI_GETFLATMENU, 0, &old_flat_menu, 0);
1311 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1312 ret = SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &old_gradient_caption, 0);
1313 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1315 /* Write new system metrics to the registry */
1316 new_color = ~old_color;
1317 new_flat_menu = !old_flat_menu;
1318 new_gradient_caption = !old_gradient_caption;
1319 memcpy(&new_ncm, &old_ncm, sizeof(new_ncm));
1320 new_ncm.iScrollWidth += 5;
1321 memcpy(&new_logfont, &old_logfont, sizeof(new_logfont));
1322 new_logfont.lfWidth += 5;
1324 ls = RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Colors", 0, KEY_ALL_ACCESS,
1325 &hkey);
1326 ok(!ls, "RegOpenKeyExW failed, ls %#lx.\n", ls);
1328 length = swprintf(new_color_string, ARRAY_SIZE(new_color_string), L"%d %d %d",
1329 GetRValue(new_color), GetGValue(new_color), GetBValue(new_color));
1330 ls = RegSetValueExW(hkey, L"Scrollbar", 0, REG_SZ, (BYTE *)new_color_string,
1331 (length + 1) * sizeof(WCHAR));
1332 ok(!ls, "RegSetValueExW failed, ls %#lx.\n", ls);
1334 ret = SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(new_ncm), &new_ncm,
1335 SPIF_UPDATEINIFILE);
1336 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1337 ret = SystemParametersInfoW(SPI_SETICONTITLELOGFONT, sizeof(new_logfont), &new_logfont,
1338 SPIF_UPDATEINIFILE);
1339 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1340 ret = SystemParametersInfoW(SPI_SETFLATMENU, 0, (void *)(INT_PTR)new_flat_menu,
1341 SPIF_UPDATEINIFILE);
1342 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1343 ret = SystemParametersInfoW(SPI_SETGRADIENTCAPTIONS, 0,
1344 (void *)(INT_PTR)new_gradient_caption, SPIF_UPDATEINIFILE);
1345 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1347 /* Change theming state */
1348 hr = EnableTheming(FALSE);
1349 ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
1350 is_theme_active = IsThemeActive();
1351 ok(!is_theme_active || broken(is_theme_active), /* Win8+ can no longer disable theming */
1352 "Expected theming inactive.\n");
1354 /* Test that system metrics are unchanged */
1355 size = sizeof(color_string);
1356 ls = RegQueryValueExW(hkey, L"Scrollbar", NULL, NULL, (BYTE *)color_string, &size);
1357 ok(!ls, "RegQueryValueExW failed, ls %#lx.\n", ls);
1358 ok(!lstrcmpW(color_string, new_color_string), "Expected %s, got %s.\n",
1359 wine_dbgstr_w(new_color_string), wine_dbgstr_w(color_string));
1361 ret = SystemParametersInfoW(SPI_GETFLATMENU, 0, &flat_menu, 0);
1362 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1363 ok(flat_menu == new_flat_menu, "Expected %d, got %d.\n", new_flat_menu, flat_menu);
1364 ret = SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &gradient_caption, 0);
1365 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1366 ok(gradient_caption == new_gradient_caption, "Expected %d, got %d.\n",
1367 new_gradient_caption, gradient_caption);
1369 memset(&ncm, 0, sizeof(ncm));
1370 ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth);
1371 ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
1372 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1373 ok(!memcmp(&ncm, &new_ncm, sizeof(ncm)), "Expected non-client metrics unchanged.\n");
1375 memset(&logfont, 0, sizeof(logfont));
1376 ret = SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(logfont), &logfont, 0);
1377 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1378 ok(!memcmp(&logfont, &new_logfont, sizeof(logfont)),
1379 "Expected icon title font unchanged.\n");
1381 /* Test that theming cannot be turned on via EnableTheming() */
1382 hr = EnableTheming(TRUE);
1383 ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
1384 is_theme_active = IsThemeActive();
1385 ok(!is_theme_active || broken(is_theme_active), /* Win8+ can no longer disable theming */
1386 "Expected theming inactive.\n");
1388 /* Restore system metrics */
1389 ls = RegSetValueExW(hkey, L"Scrollbar", 0, REG_SZ, (BYTE *)old_color_string,
1390 (lstrlenW(old_color_string) + 1) * sizeof(WCHAR));
1391 ok(!ls, "RegSetValueExW failed, ls %#lx.\n", ls);
1392 ret = SystemParametersInfoW(SPI_SETFLATMENU, 0, (void *)(INT_PTR)old_flat_menu,
1393 SPIF_UPDATEINIFILE);
1394 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1395 ret = SystemParametersInfoW(SPI_SETGRADIENTCAPTIONS, 0,
1396 (void *)(INT_PTR)old_gradient_caption, SPIF_UPDATEINIFILE);
1397 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1398 ret = SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(old_ncm), &old_ncm,
1399 SPIF_UPDATEINIFILE);
1400 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1401 ret = SystemParametersInfoW(SPI_SETICONTITLELOGFONT, sizeof(old_logfont), &old_logfont,
1402 SPIF_UPDATEINIFILE);
1403 ok(ret, "SystemParametersInfoW failed, error %lu.\n", GetLastError());
1405 RegCloseKey(hkey);
1406 if (pSetThreadDpiAwarenessContext)
1407 pSetThreadDpiAwarenessContext(old_context);
1410 else
1412 hr = EnableTheming(FALSE);
1413 ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
1414 ok(!IsThemeActive(), "Expected theming inactive.\n");
1416 hr = EnableTheming(TRUE);
1417 ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
1418 ok(!IsThemeActive(), "Expected theming inactive.\n");
1420 hr = EnableTheming(FALSE);
1421 ok(hr == S_OK, "EnableTheming failed, hr %#lx.\n", hr);
1422 ok(!IsThemeActive(), "Expected theming inactive.\n");
1426 static void test_GetThemeIntList(void)
1428 INTLIST intlist;
1429 HTHEME theme;
1430 HRESULT hr;
1431 HWND hwnd;
1433 if (!pGetThemeIntList)
1435 win_skip("GetThemeIntList is unavailable.\n");
1436 return;
1439 hwnd = CreateWindowA("static", "", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
1440 theme = OpenThemeData(hwnd, L"Button");
1441 if (!theme)
1443 skip("Theming is not active.\n");
1444 DestroyWindow(hwnd);
1445 return;
1448 /* Check properties */
1449 /* TMT_TRANSITIONDURATIONS is a vista+ property */
1450 hr = pGetThemeIntList(theme, BP_PUSHBUTTON, PBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
1451 if (LOBYTE(LOWORD(GetVersion())) < 6)
1452 ok(hr == E_PROP_ID_UNSUPPORTED, "Expected %#lx, got %#lx.\n", E_PROP_ID_UNSUPPORTED, hr);
1453 else
1454 ok(hr == S_OK, "GetThemeIntList failed, hr %#lx.\n", hr);
1456 CloseThemeData(theme);
1457 DestroyWindow(hwnd);
1460 static void test_GetThemeTransitionDuration(void)
1462 int from_state, to_state, expected;
1463 INTLIST intlist;
1464 DWORD duration;
1465 HTHEME theme;
1466 HRESULT hr;
1467 HWND hwnd;
1469 if (!pGetThemeTransitionDuration || !pGetThemeIntList)
1471 win_skip("GetThemeTransitionDuration or GetThemeIntList is unavailable.\n");
1472 return;
1475 hwnd = CreateWindowA("static", "", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
1476 theme = OpenThemeData(hwnd, L"Button");
1477 if (!theme)
1479 skip("Theming is not active.\n");
1480 DestroyWindow(hwnd);
1481 return;
1484 /* Invalid parameter tests */
1485 duration = 0xdeadbeef;
1486 hr = pGetThemeTransitionDuration(NULL, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1487 TMT_TRANSITIONDURATIONS, &duration);
1488 ok(hr == E_HANDLE, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1489 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
1491 /* Crash on Wine. HTHEME is not a pointer that can be directly referenced. */
1492 if (strcmp(winetest_platform, "wine"))
1494 duration = 0xdeadbeef;
1495 hr = pGetThemeTransitionDuration((HTHEME)0xdeadbeef, BP_PUSHBUTTON, PBS_NORMAL,
1496 PBS_DEFAULTED_ANIMATING, TMT_TRANSITIONDURATIONS, &duration);
1497 todo_wine
1498 ok(hr == E_HANDLE, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1499 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
1502 duration = 0xdeadbeef;
1503 hr = pGetThemeTransitionDuration(theme, 0xdeadbeef, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1504 TMT_TRANSITIONDURATIONS, &duration);
1505 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1506 ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
1508 duration = 0xdeadbeef;
1509 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL - 1, PBS_DEFAULTED_ANIMATING,
1510 TMT_TRANSITIONDURATIONS, &duration);
1511 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1512 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
1514 duration = 0xdeadbeef;
1515 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_DEFAULTED_ANIMATING + 1,
1516 PBS_DEFAULTED_ANIMATING, TMT_TRANSITIONDURATIONS, &duration);
1517 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1518 ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
1520 duration = 0xdeadbeef;
1521 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_NORMAL - 1,
1522 TMT_TRANSITIONDURATIONS, &duration);
1523 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1524 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#lx.\n", 0xdeadbeef, duration);
1526 duration = 0xdeadbeef;
1527 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING + 1,
1528 TMT_TRANSITIONDURATIONS, &duration);
1529 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1530 ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
1532 duration = 0xdeadbeef;
1533 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1534 TMT_BACKGROUND, &duration);
1535 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1536 ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
1538 duration = 0xdeadbeef;
1539 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1540 0xdeadbeef, &duration);
1541 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1542 ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
1544 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1545 TMT_TRANSITIONDURATIONS, NULL);
1546 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1548 /* Parts that don't have TMT_TRANSITIONDURATIONS */
1549 hr = GetThemeIntList(theme, BP_GROUPBOX, GBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
1550 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeIntList failed, hr %#lx.\n", hr);
1552 duration = 0xdeadbeef;
1553 hr = pGetThemeTransitionDuration(theme, BP_GROUPBOX, GBS_NORMAL, GBS_DISABLED,
1554 TMT_TRANSITIONDURATIONS, &duration);
1555 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1556 ok(duration == 0, "Expected duration %#x, got %#lx.\n", 0, duration);
1558 /* Test parsing TMT_TRANSITIONDURATIONS property. TMT_TRANSITIONDURATIONS is a vista+ property */
1559 if (LOBYTE(LOWORD(GetVersion())) < 6)
1560 goto done;
1562 hr = pGetThemeIntList(theme, BP_PUSHBUTTON, PBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
1563 ok(hr == S_OK, "GetThemeIntList failed, hr %#lx.\n", hr);
1564 /* The first value is the theme part state count. The following are the values from every state
1565 * to every state. So the total value count should be 1 + state ^ 2 */
1566 expected = PBS_DEFAULTED_ANIMATING - PBS_NORMAL + 1;
1567 ok(intlist.iValues[0] == expected, "Expected the first value %d, got %d.\n", expected,
1568 intlist.iValues[0]);
1569 expected = 1 + intlist.iValues[0] * intlist.iValues[0];
1570 ok(intlist.iValueCount == expected, "Expected value count %d, got %d.\n", expected,
1571 intlist.iValueCount);
1572 if (hr == S_OK)
1574 for (from_state = PBS_NORMAL; from_state <= PBS_DEFAULTED_ANIMATING; ++from_state)
1576 for (to_state = PBS_NORMAL; to_state <= PBS_DEFAULTED_ANIMATING; ++to_state)
1578 winetest_push_context("from state %d to %d", from_state, to_state);
1580 duration = 0xdeadbeef;
1581 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, from_state, to_state,
1582 TMT_TRANSITIONDURATIONS, &duration);
1583 ok(hr == S_OK, "GetThemeTransitionDuration failed, hr %#lx.\n", hr);
1584 expected = intlist.iValues[1 + intlist.iValues[0] * (from_state - 1) + (to_state - 1)];
1585 ok(duration == expected, "Expected duration %d, got %ld.\n", expected, duration);
1587 winetest_pop_context();
1592 done:
1593 CloseThemeData(theme);
1594 DestroyWindow(hwnd);
1597 static const struct message DrawThemeParentBackground_seq[] =
1599 {WM_ERASEBKGND, sent},
1600 {WM_PRINTCLIENT, sent | lparam, 0, PRF_CLIENT},
1604 static LRESULT WINAPI test_DrawThemeParentBackground_proc(HWND hwnd, UINT message, WPARAM wp,
1605 LPARAM lp)
1607 static LONG defwndproc_counter;
1608 struct message msg = {0};
1609 LRESULT lr;
1610 POINT org;
1611 BOOL ret;
1613 switch (message)
1615 /* Test that DrawThemeParentBackground() doesn't change brush origins */
1616 case WM_ERASEBKGND:
1617 case WM_PRINTCLIENT:
1618 ret = GetBrushOrgEx((HDC)wp, &org);
1619 ok(ret, "GetBrushOrgEx failed, error %ld.\n", GetLastError());
1620 ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
1621 break;
1623 default:
1624 break;
1627 msg.message = message;
1628 msg.flags = sent | wparam | lparam;
1629 if (defwndproc_counter)
1630 msg.flags |= defwinproc;
1631 msg.wParam = wp;
1632 msg.lParam = lp;
1633 add_message(sequences, PARENT_SEQ_INDEX, &msg);
1635 InterlockedIncrement(&defwndproc_counter);
1636 lr = DefWindowProcA(hwnd, message, wp, lp);
1637 InterlockedDecrement(&defwndproc_counter);
1638 return lr;
1641 static void test_DrawThemeParentBackground(void)
1643 HWND child, parent;
1644 WNDCLASSA cls;
1645 HRESULT hr;
1646 POINT org;
1647 RECT rect;
1648 BOOL ret;
1649 HDC hdc;
1651 memset(&cls, 0, sizeof(cls));
1652 cls.hInstance = GetModuleHandleA(0);
1653 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
1654 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
1655 cls.lpfnWndProc = test_DrawThemeParentBackground_proc;
1656 cls.lpszClassName = "TestDrawThemeParentBackgroundClass";
1657 RegisterClassA(&cls);
1659 parent = CreateWindowA("TestDrawThemeParentBackgroundClass", "parent", WS_POPUP | WS_VISIBLE, 0,
1660 0, 100, 100, 0, 0, 0, 0);
1661 ok(parent != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
1662 child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, parent, 0, 0,
1663 NULL);
1664 ok(child != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
1665 flush_events();
1666 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1668 hdc = GetDC(child);
1669 ret = GetBrushOrgEx(hdc, &org);
1670 ok(ret, "GetBrushOrgEx failed, error %ld.\n", GetLastError());
1671 ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
1673 hr = DrawThemeParentBackground(child, hdc, NULL);
1674 ok(SUCCEEDED(hr), "DrawThemeParentBackground failed, hr %#lx.\n", hr);
1675 ok_sequence(sequences, PARENT_SEQ_INDEX, DrawThemeParentBackground_seq,
1676 "DrawThemeParentBackground parent", FALSE);
1677 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1679 GetClientRect(child, &rect);
1680 hr = DrawThemeParentBackground(child, hdc, &rect);
1681 ok(SUCCEEDED(hr), "DrawThemeParentBackground failed, hr %#lx.\n", hr);
1682 ok_sequence(sequences, PARENT_SEQ_INDEX, DrawThemeParentBackground_seq,
1683 "DrawThemeParentBackground parent", FALSE);
1684 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1686 ReleaseDC(child, hdc);
1687 DestroyWindow(parent);
1688 UnregisterClassA("TestDrawThemeParentBackgroundClass", GetModuleHandleA(0));
1691 struct test_EnableThemeDialogTexture_param
1693 const CHAR *class_name;
1694 DWORD style;
1697 static const struct message empty_seq[] =
1702 static const struct message wm_erasebkgnd_seq[] =
1704 {WM_ERASEBKGND, sent},
1705 {WM_CTLCOLORDLG, sent},
1709 static const struct message wm_ctlcolormsgbox_seq[] =
1711 {WM_CTLCOLORMSGBOX, sent},
1715 static const struct message wm_ctlcolorbtn_seq[] =
1717 {WM_CTLCOLORBTN, sent},
1721 static const struct message wm_ctlcolordlg_seq[] =
1723 {WM_CTLCOLORDLG, sent},
1727 static const struct message wm_ctlcolorstatic_seq[] =
1729 {WM_CTLCOLORSTATIC, sent},
1733 static HWND dialog_child;
1734 static DWORD dialog_init_flag;
1735 static BOOL handle_WM_ERASEBKGND;
1736 static BOOL handle_WM_CTLCOLORSTATIC;
1738 static INT_PTR CALLBACK test_EnableThemeDialogTexture_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
1740 struct test_EnableThemeDialogTexture_param *param;
1741 struct message message = {0};
1743 message.message = msg;
1744 message.flags = sent | wparam | lparam;
1745 message.wParam = wp;
1746 message.lParam = lp;
1747 add_message(sequences, PARENT_SEQ_INDEX, &message);
1749 switch (msg)
1751 case WM_INITDIALOG:
1752 param = (struct test_EnableThemeDialogTexture_param *)lp;
1753 dialog_child = CreateWindowA(param->class_name, "child",
1754 param->style | WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, hwnd,
1755 (HMENU)100, 0, NULL);
1756 ok(dialog_child != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
1757 if (dialog_init_flag)
1758 EnableThemeDialogTexture(hwnd, dialog_init_flag);
1759 return TRUE;
1761 case WM_ERASEBKGND:
1763 if (!handle_WM_ERASEBKGND)
1764 return FALSE;
1766 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 0);
1767 return TRUE;
1770 case WM_CTLCOLORSTATIC:
1771 return (INT_PTR)(handle_WM_CTLCOLORSTATIC ? GetSysColorBrush(COLOR_MENU) : 0);
1773 case WM_CLOSE:
1774 DestroyWindow(dialog_child);
1775 dialog_child = NULL;
1776 return TRUE;
1778 default:
1779 return FALSE;
1783 static void test_EnableThemeDialogTexture(void)
1785 struct test_EnableThemeDialogTexture_param param;
1786 HWND dialog, child, hwnd, hwnd2;
1787 int mode, old_mode, count, i, j;
1788 COLORREF color, old_color;
1789 HBRUSH brush, brush2;
1790 HDC child_hdc, hdc;
1791 LOGBRUSH log_brush;
1792 char buffer[32];
1793 ULONG_PTR proc;
1794 WNDCLASSA cls;
1795 HTHEME theme;
1796 DWORD error;
1797 BITMAP bmp;
1798 HRESULT hr;
1799 LRESULT lr;
1800 POINT org;
1801 SIZE size;
1802 UINT msg;
1803 BOOL ret;
1805 struct
1807 DLGTEMPLATE template;
1808 WORD menu;
1809 WORD class;
1810 WORD title;
1811 } temp = {{0}};
1813 static const DWORD flags[] =
1815 ETDT_DISABLE,
1816 ETDT_ENABLE,
1817 ETDT_USETABTEXTURE,
1818 ETDT_USEAEROWIZARDTABTEXTURE,
1819 ETDT_ENABLETAB,
1820 ETDT_ENABLEAEROWIZARDTAB,
1821 /* Bad flags */
1823 ETDT_DISABLE | ETDT_ENABLE,
1824 ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB
1827 static const struct invalid_flag_test
1829 DWORD flag;
1830 BOOL enabled;
1832 invalid_flag_tests[] =
1834 {0, FALSE},
1835 {ETDT_DISABLE | ETDT_ENABLE, FALSE},
1836 {ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB, TRUE},
1837 {ETDT_USETABTEXTURE | ETDT_USEAEROWIZARDTABTEXTURE, TRUE},
1838 {ETDT_VALIDBITS, FALSE},
1839 {~ETDT_VALIDBITS, FALSE},
1840 {~ETDT_VALIDBITS | ETDT_DISABLE, FALSE}
1843 static const struct class_test
1845 struct test_EnableThemeDialogTexture_param param;
1846 BOOL texture_enabled;
1848 class_tests[] =
1850 {{ANIMATE_CLASSA}},
1851 {{WC_BUTTONA, BS_PUSHBUTTON}, TRUE},
1852 {{WC_BUTTONA, BS_DEFPUSHBUTTON}, TRUE},
1853 {{WC_BUTTONA, BS_CHECKBOX}, TRUE},
1854 {{WC_BUTTONA, BS_AUTOCHECKBOX}, TRUE},
1855 {{WC_BUTTONA, BS_RADIOBUTTON}, TRUE},
1856 {{WC_BUTTONA, BS_3STATE}, TRUE},
1857 {{WC_BUTTONA, BS_AUTO3STATE}, TRUE},
1858 {{WC_BUTTONA, BS_GROUPBOX}, TRUE},
1859 {{WC_BUTTONA, BS_USERBUTTON}, TRUE},
1860 {{WC_BUTTONA, BS_AUTORADIOBUTTON}, TRUE},
1861 {{WC_BUTTONA, BS_PUSHBOX}, TRUE},
1862 {{WC_BUTTONA, BS_OWNERDRAW}, TRUE},
1863 {{WC_BUTTONA, BS_SPLITBUTTON}, TRUE},
1864 {{WC_BUTTONA, BS_DEFSPLITBUTTON}, TRUE},
1865 {{WC_BUTTONA, BS_COMMANDLINK}, TRUE},
1866 {{WC_BUTTONA, BS_DEFCOMMANDLINK}, TRUE},
1867 {{WC_COMBOBOXA}},
1868 {{WC_COMBOBOXEXA}},
1869 {{DATETIMEPICK_CLASSA}},
1870 {{WC_EDITA}},
1871 {{WC_HEADERA}},
1872 {{HOTKEY_CLASSA}},
1873 {{WC_IPADDRESSA}},
1874 {{WC_LISTBOXA}},
1875 {{WC_LISTVIEWA}},
1876 {{MONTHCAL_CLASSA}},
1877 {{WC_NATIVEFONTCTLA}},
1878 {{WC_PAGESCROLLERA}},
1879 {{PROGRESS_CLASSA}},
1880 {{REBARCLASSNAMEA}},
1881 {{WC_STATICA, SS_LEFT}, TRUE},
1882 {{WC_STATICA, SS_ICON}, TRUE},
1883 {{WC_STATICA, SS_BLACKRECT}, TRUE},
1884 {{WC_STATICA, SS_OWNERDRAW}, TRUE},
1885 {{WC_STATICA, SS_BITMAP}, TRUE},
1886 {{WC_STATICA, SS_ENHMETAFILE}, TRUE},
1887 {{WC_STATICA, SS_ETCHEDHORZ}, TRUE},
1888 {{STATUSCLASSNAMEA}},
1889 {{"SysLink"}},
1890 {{WC_TABCONTROLA}},
1891 {{TOOLBARCLASSNAMEA}},
1892 {{TOOLTIPS_CLASSA}},
1893 {{TRACKBAR_CLASSA}},
1894 {{WC_TREEVIEWA}},
1895 {{UPDOWN_CLASSA}},
1896 {{WC_SCROLLBARA}},
1899 static const struct message_test
1901 UINT msg;
1902 const struct message *msg_seq;
1904 message_tests[] =
1906 {WM_ERASEBKGND, wm_erasebkgnd_seq},
1907 {WM_CTLCOLORMSGBOX, wm_ctlcolormsgbox_seq},
1908 {WM_CTLCOLORBTN, wm_ctlcolorbtn_seq},
1909 {WM_CTLCOLORDLG, wm_ctlcolordlg_seq},
1910 {WM_CTLCOLORSTATIC, wm_ctlcolorstatic_seq},
1913 if (!IsThemeActive())
1915 skip("Theming is inactive.\n");
1916 return;
1919 memset(&cls, 0, sizeof(cls));
1920 cls.lpfnWndProc = DefWindowProcA;
1921 cls.hInstance = GetModuleHandleA(NULL);
1922 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
1923 cls.hbrBackground = GetStockObject(GRAY_BRUSH);
1924 cls.lpszClassName = "TestEnableThemeDialogTextureClass";
1925 RegisterClassA(&cls);
1927 temp.template.style = WS_CHILD | WS_VISIBLE;
1928 temp.template.cx = 100;
1929 temp.template.cy = 100;
1930 param.class_name = cls.lpszClassName;
1931 param.style = 0;
1932 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
1933 test_EnableThemeDialogTexture_proc, (LPARAM)&param);
1934 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
1935 child = GetDlgItem(dialog, 100);
1936 ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
1937 child_hdc = GetDC(child);
1939 /* Test that dialog procedure is unchanged */
1940 proc = GetWindowLongPtrA(dialog, DWLP_DLGPROC);
1941 ok(proc == (ULONG_PTR)test_EnableThemeDialogTexture_proc, "Unexpected proc %#Ix.\n", proc);
1943 /* Test dialog texture is disabled by default. EnableThemeDialogTexture() needs to be called */
1944 ret = IsThemeDialogTextureEnabled(dialog);
1945 ok(!ret, "Expected theme dialog texture disabled.\n");
1946 ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
1948 /* Test ETDT_ENABLE | ETDT_USETABTEXTURE doesn't take effect immediately */
1949 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
1950 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
1951 ret = IsThemeDialogTextureEnabled(dialog);
1952 ok(ret, "Expected theme dialog texture enabled.\n");
1954 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1955 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1956 GetSysColorBrush(COLOR_BTNFACE), brush);
1957 ret = GetBrushOrgEx(child_hdc, &org);
1958 ok(ret, "GetBrushOrgEx failed, error %lu.\n", GetLastError());
1959 ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
1961 /* Test WM_THEMECHANGED doesn't make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
1962 lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
1963 ok(lr == 0, "WM_THEMECHANGED failed.\n");
1964 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1965 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1966 GetSysColorBrush(COLOR_BTNFACE), brush);
1968 /* Test WM_ERASEBKGND make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
1969 lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
1970 ok(lr != 0, "WM_ERASEBKGND failed.\n");
1971 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1972 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected brush changed.\n");
1974 /* Test disabling theme dialog texture should change the brush immediately */
1975 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1976 hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
1977 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
1978 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1979 ok(brush2 != brush, "Expected a different brush.\n");
1980 ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1981 GetSysColorBrush(COLOR_BTNFACE), brush2);
1983 /* Test re-enabling theme dialog texture with ETDT_ENABLE doesn't change the brush */
1984 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1985 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE);
1986 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
1987 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1988 ok(brush2 == brush, "Expected the same brush.\n");
1989 ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1990 GetSysColorBrush(COLOR_BTNFACE), brush2);
1992 /* Test adding ETDT_USETABTEXTURE should change the brush immediately */
1993 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1994 hr = EnableThemeDialogTexture(dialog, ETDT_USETABTEXTURE);
1995 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
1996 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1997 ok(brush2 != brush, "Expected a different brush.\n");
1999 /* Test ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE should change the brush immediately */
2000 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2001 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE);
2002 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
2003 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2004 /* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
2005 if (LOBYTE(LOWORD(GetVersion())) < 6)
2006 ok(brush2 == brush, "Expected the same brush.\n");
2007 else
2008 ok(brush2 != brush, "Expected a different brush.\n");
2010 hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
2011 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
2012 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
2013 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
2015 /* Test that the dialog procedure should take precedence over DefDlgProc() for WM_CTLCOLORSTATIC */
2016 handle_WM_CTLCOLORSTATIC = TRUE;
2017 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2018 ok(brush == GetSysColorBrush(COLOR_MENU), "Expected brush %p, got %p.\n",
2019 GetSysColorBrush(COLOR_MENU), brush);
2020 handle_WM_CTLCOLORSTATIC = FALSE;
2022 /* Test that the dialog procedure should take precedence over DefDlgProc() for WM_ERASEBKGND */
2023 handle_WM_ERASEBKGND = TRUE;
2024 lr = SendMessageW(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
2025 ok(lr == 0, "Expected 0, got %#Ix.\n", lr);
2026 handle_WM_ERASEBKGND = FALSE;
2028 /* Test that dialog doesn't have theme handle opened for itself */
2029 ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
2031 theme = OpenThemeData(NULL, L"Tab");
2032 ok(theme != NULL, "OpenThemeData failed.\n");
2033 size.cx = 0;
2034 size.cy = 0;
2035 hr = GetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
2036 ok(hr == S_OK, "GetThemePartSize failed, hr %#lx.\n", hr);
2037 CloseThemeData(theme);
2039 /* Test which WM_CTLCOLOR* message uses tab background as dialog texture */
2040 for (msg = WM_CTLCOLORMSGBOX; msg <= WM_CTLCOLORSTATIC; ++msg)
2042 winetest_push_context("msg %#x", msg);
2044 /* Test that some WM_CTLCOLOR* messages change brush origin when dialog texture is on */
2045 ret = SetBrushOrgEx(child_hdc, 0, 0, NULL);
2046 ok(ret, "SetBrushOrgEx failed, error %lu.\n", GetLastError());
2047 SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2048 ret = GetBrushOrgEx(child_hdc, &org);
2049 ok(ret, "GetBrushOrgEx failed, error %lu.\n", GetLastError());
2050 /* WM_CTLCOLOREDIT, WM_CTLCOLORLISTBOX and WM_CTLCOLORSCROLLBAR don't use tab background */
2051 if (msg == WM_CTLCOLOREDIT || msg == WM_CTLCOLORLISTBOX || msg == WM_CTLCOLORSCROLLBAR)
2053 ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
2054 winetest_pop_context();
2055 continue;
2057 else
2059 ok(org.x == -1 && org.y == -2, "Expected (-1,-2), got %s.\n", wine_dbgstr_point(&org));
2062 /* Test that some WM_CTLCOLOR* messages change background mode when dialog texture is on */
2063 old_mode = SetBkMode(child_hdc, OPAQUE);
2064 ok(old_mode != 0, "SetBkMode failed.\n");
2065 SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2066 mode = SetBkMode(child_hdc, old_mode);
2067 ok(mode == TRANSPARENT, "Expected mode %#x, got %#x.\n", TRANSPARENT, mode);
2069 /* Test that some WM_CTLCOLOR* messages change background color when dialog texture is on */
2070 old_color = SetBkColor(child_hdc, 0xaa5511);
2071 ok(old_color != CLR_INVALID, "SetBkColor failed.\n");
2072 SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2073 color = SetBkColor(child_hdc, old_color);
2074 ok(color == GetSysColor(COLOR_BTNFACE), "Expected background color %#lx, got %#lx.\n",
2075 GetSysColor(COLOR_BTNFACE), color);
2077 /* Test that the returned brush is a pattern brush created from the tab body */
2078 brush = (HBRUSH)SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2079 memset(&log_brush, 0, sizeof(log_brush));
2080 count = GetObjectA(brush, sizeof(log_brush), &log_brush);
2081 ok(count == sizeof(log_brush), "GetObjectA failed, error %lu.\n", GetLastError());
2082 ok(log_brush.lbColor == 0, "Expected brush color %#x, got %#lx.\n", 0, log_brush.lbColor);
2083 ok(log_brush.lbStyle == BS_PATTERN, "Expected brush style %#x, got %#x.\n", BS_PATTERN,
2084 log_brush.lbStyle);
2086 memset(&bmp, 0, sizeof(bmp));
2087 count = GetObjectA((HBITMAP)log_brush.lbHatch, sizeof(bmp), &bmp);
2088 ok(count == sizeof(bmp), "GetObjectA failed, error %lu.\n", GetLastError());
2089 ok(bmp.bmWidth == size.cx, "Expected width %ld, got %d.\n", size.cx, bmp.bmWidth);
2090 ok(bmp.bmHeight == size.cy, "Expected height %ld, got %d.\n", size.cy, bmp.bmHeight);
2092 /* Test that DefDlgProcA/W() are hooked for some WM_CTLCOLOR* messages */
2093 brush = (HBRUSH)SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2094 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
2095 brush2 = (HBRUSH)DefDlgProcW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2096 ok(brush2 == brush, "Expected the same brush.\n");
2097 brush2 = (HBRUSH)DefDlgProcA(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2098 ok(brush2 == brush, "Expected the same brush.\n");
2100 /* Test that DefWindowProcA/W() are also hooked for some WM_CTLCOLOR* messages */
2101 brush = (HBRUSH)SendMessageW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2102 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
2103 if (msg != WM_CTLCOLORDLG)
2105 brush2 = (HBRUSH)DefWindowProcW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2106 todo_wine
2107 ok(brush2 == brush, "Expected the same brush.\n");
2108 brush2 = (HBRUSH)DefWindowProcA(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2109 todo_wine
2110 ok(brush2 == brush, "Expected the same brush.\n");
2112 else
2114 brush2 = (HBRUSH)DefWindowProcW(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2115 ok(brush2 != brush, "Expected a different brush.\n");
2116 brush2 = (HBRUSH)DefWindowProcA(dialog, msg, (WPARAM)child_hdc, (LPARAM)child);
2117 ok(brush2 != brush, "Expected a different brush.\n");
2120 winetest_pop_context();
2123 /* Test that DefWindowProcA/W() are not hooked for WM_ERASEBKGND. So the background is still
2124 * drawn with hbrBackground, which in this case, is GRAY_BRUSH.
2126 * This test means it could be that both DefWindowProc() and DefDlgProc() are hooked for
2127 * WM_CTLCOLORSTATIC and only DefDlgProc() is hooked for WM_ERASEBKGND. Or it could mean
2128 * DefWindowProc() is hooked for WM_CTLCOLORSTATIC and DefDlgProc() is hooked for WM_ERASEBKGND.
2129 * Considering the dialog theming needs a WM_ERASEBKGND to activate, it would be weird for let
2130 * only DefWindowProc() to hook WM_CTLCOLORSTATIC. For example, what's the point of hooking
2131 * WM_CTLCOLORSTATIC in DefWindowProc() for a feature that can only be activated in
2132 * DefDlgProc()? So I tend to believe both DefWindowProc() and DefDlgProc() are hooked for
2133 * WM_CTLCOLORSTATIC */
2134 hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, 0, 0,
2135 0, NULL);
2136 ok(hwnd != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
2137 hwnd2 = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 10, 10, 20, 20, hwnd, NULL, 0,
2138 NULL);
2139 hr = EnableThemeDialogTexture(hwnd, ETDT_ENABLETAB);
2140 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
2141 ret = IsThemeDialogTextureEnabled(hwnd);
2142 ok(ret, "Wrong dialog texture status.\n");
2143 flush_events();
2145 hdc = GetDC(hwnd);
2146 color = GetPixel(hdc, 0, 0);
2147 ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
2148 "Expected color %#x, got %#lx.\n", 0x808080, color);
2149 color = GetPixel(hdc, 50, 50);
2150 ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
2151 "Expected color %#x, got %#lx.\n", 0x808080, color);
2152 color = GetPixel(hdc, 99, 99);
2153 ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
2154 "Expected color %#x, got %#lx.\n", 0x808080, color);
2155 ReleaseDC(hwnd, hdc);
2157 /* Test EnableThemeDialogTexture() doesn't work for non-dialog windows */
2158 hdc = GetDC(hwnd2);
2159 brush = (HBRUSH)SendMessageW(hwnd, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd2);
2160 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
2161 ReleaseDC(hwnd2, hdc);
2163 DestroyWindow(hwnd);
2165 /* Test that the brush is not a system object and has only one reference and shouldn't be freed */
2166 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2167 ret = DeleteObject(brush);
2168 ok(ret, "DeleteObject failed, error %lu.\n", GetLastError());
2169 SetLastError(0xdeadbeef);
2170 ret = GetObjectA(brush, sizeof(log_brush), &log_brush);
2171 error = GetLastError();
2172 ok(!ret || broken(ret) /* XP */, "GetObjectA succeeded.\n");
2173 todo_wine
2174 ok(error == ERROR_INVALID_PARAMETER || broken(error == 0xdeadbeef) /* XP */,
2175 "Expected error %u, got %lu.\n", ERROR_INVALID_PARAMETER, error);
2176 ret = DeleteObject(brush);
2177 ok(!ret || broken(ret) /* XP */, "DeleteObject succeeded.\n");
2179 /* Should still report the same brush handle after the brush handle was freed */
2180 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2181 ok(brush2 == brush, "Expected the same brush.\n");
2183 /* Test WM_THEMECHANGED can update the brush now that ETDT_ENABLE | ETDT_USETABTEXTURE is in
2184 * effect. This test needs to be ran last as it affect other tests for the same dialog for
2185 * unknown reason, causing the brush not to update */
2186 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2187 lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
2188 ok(lr == 0, "WM_THEMECHANGED failed.\n");
2189 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2190 ok(brush2 != brush, "Expected a different brush.\n");
2192 ReleaseDC(child, child_hdc);
2193 EndDialog(dialog, 0);
2195 /* Test invalid flags */
2196 for (i = 0; i < ARRAY_SIZE(invalid_flag_tests); ++i)
2198 winetest_push_context("%d flag %#lx", i, invalid_flag_tests[i].flag);
2200 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2201 test_EnableThemeDialogTexture_proc, (LPARAM)&param);
2202 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
2203 hr = EnableThemeDialogTexture(dialog, invalid_flag_tests[i].flag);
2204 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
2205 ret = IsThemeDialogTextureEnabled(dialog);
2206 ok(ret == invalid_flag_tests[i].enabled, "Wrong dialog texture status.\n");
2207 EndDialog(dialog, 0);
2209 winetest_pop_context();
2212 /* Test different flag combinations */
2213 for (i = 0; i < ARRAY_SIZE(flags); ++i)
2215 for (j = 0; j < ARRAY_SIZE(flags); ++j)
2217 /* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
2218 if (LOBYTE(LOWORD(GetVersion())) < 6
2219 && ((flags[i] | flags[j]) & ETDT_USEAEROWIZARDTABTEXTURE))
2220 continue;
2222 winetest_push_context("%#lx to %#lx", flags[i], flags[j]);
2224 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2225 test_EnableThemeDialogTexture_proc, (LPARAM)&param);
2226 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
2227 flush_events();
2228 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2230 hr = EnableThemeDialogTexture(dialog, flags[i]);
2231 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
2232 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
2233 "EnableThemeDialogTexture first flag", FALSE);
2234 ret = IsThemeDialogTextureEnabled(dialog);
2235 /* Non-zero flags without ETDT_DISABLE enables dialog texture */
2236 ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
2238 child = GetDlgItem(dialog, 100);
2239 ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
2240 child_hdc = GetDC(child);
2241 lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
2242 ok(lr != 0, "WM_ERASEBKGND failed.\n");
2243 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2244 if (flags[i] == ETDT_ENABLETAB || flags[i] == ETDT_ENABLEAEROWIZARDTAB)
2245 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2246 else
2247 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2248 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2250 hr = EnableThemeDialogTexture(dialog, flags[j]);
2251 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#lx.\n", hr);
2252 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
2253 "EnableThemeDialogTexture second flag", FALSE);
2254 ret = IsThemeDialogTextureEnabled(dialog);
2255 /* If the flag is zero, it will have previous dialog texture status */
2256 if (flags[j])
2257 ok(ret == !(flags[j] & ETDT_DISABLE), "Wrong dialog texture status.\n");
2258 else
2259 ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
2260 lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
2261 ok(lr != 0, "WM_ERASEBKGND failed.\n");
2262 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2263 /* Dialog texture is turned on when the flag contains ETDT_ENABLETAB or
2264 * ETDT_ENABLEAEROWIZARDTAB. The flag can be turned on in multiple steps, but you can't
2265 * do things like set ETDT_ENABLETAB and then ETDT_USEAEROWIZARDTABTEXTURE */
2266 if (((flags[j] == ETDT_ENABLETAB || flags[j] == ETDT_ENABLEAEROWIZARDTAB)
2267 || ((((flags[i] | flags[j]) & ETDT_ENABLETAB) == ETDT_ENABLETAB
2268 || ((flags[i] | flags[j]) & ETDT_ENABLEAEROWIZARDTAB) == ETDT_ENABLEAEROWIZARDTAB)
2269 && !((flags[i] | flags[j]) & ETDT_DISABLE)))
2270 && (((flags[i] | flags[j]) & (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)) != (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)))
2271 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2272 else
2273 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2275 ReleaseDC(child, child_hdc);
2276 EndDialog(dialog, 0);
2278 winetest_pop_context();
2282 /* Test that the dialog procedure should set ETDT_USETABTEXTURE/ETDT_USEAEROWIZARDTABTEXTURE and
2283 * find out which comctl32 class should set ETDT_ENABLE to turn on dialog texture */
2284 for (i = 0; i < ARRAY_SIZE(class_tests); ++i)
2286 winetest_push_context("%s %#lx", wine_dbgstr_a(class_tests[i].param.class_name),
2287 class_tests[i].param.style);
2289 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2290 test_EnableThemeDialogTexture_proc,
2291 (LPARAM)&class_tests[i].param);
2292 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
2293 /* GetDlgItem() fails to get the child control if the child is a tooltip */
2294 child = dialog_child;
2295 ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
2296 child_hdc = GetDC(child);
2298 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2299 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2301 ReleaseDC(child, child_hdc);
2302 EndDialog(dialog, 0);
2304 dialog_init_flag = ETDT_ENABLE;
2305 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2306 test_EnableThemeDialogTexture_proc,
2307 (LPARAM)&class_tests[i].param);
2308 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
2309 child = dialog_child;
2310 ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
2311 child_hdc = GetDC(child);
2313 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2314 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2316 ReleaseDC(child, child_hdc);
2317 EndDialog(dialog, 0);
2319 dialog_init_flag = ETDT_USETABTEXTURE;
2320 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2321 test_EnableThemeDialogTexture_proc,
2322 (LPARAM)&class_tests[i].param);
2323 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
2324 child = dialog_child;
2325 ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
2326 child_hdc = GetDC(child);
2327 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2328 if (class_tests[i].texture_enabled)
2329 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2330 else
2331 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2332 ReleaseDC(child, child_hdc);
2333 EndDialog(dialog, 0);
2335 if (LOBYTE(LOWORD(GetVersion())) < 6)
2337 dialog_init_flag = 0;
2338 winetest_pop_context();
2339 continue;
2342 dialog_init_flag = ETDT_USEAEROWIZARDTABTEXTURE;
2343 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2344 test_EnableThemeDialogTexture_proc,
2345 (LPARAM)&class_tests[i].param);
2346 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
2347 child = dialog_child;
2348 ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
2349 child_hdc = GetDC(child);
2350 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2351 if (class_tests[i].texture_enabled)
2352 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2353 else
2354 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2355 ReleaseDC(child, child_hdc);
2356 EndDialog(dialog, 0);
2357 dialog_init_flag = 0;
2359 winetest_pop_context();
2362 /* Test that EnableThemeDialogTexture() is called from child controls for its parent */
2363 hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
2364 0, 0, NULL);
2365 ok(hwnd != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
2366 ret = IsThemeDialogTextureEnabled(hwnd);
2367 ok(!ret, "Wrong dialog texture status.\n");
2368 child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0,
2369 NULL);
2370 ok(child != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
2371 ret = IsThemeDialogTextureEnabled(hwnd);
2372 ok(ret, "Wrong dialog texture status.\n");
2374 /* Test that if you move the child control to another window, it doesn't enables tab texture for
2375 * the new parent */
2376 hwnd2 = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
2377 0, 0, NULL);
2378 ok(hwnd2 != NULL, "CreateWindowA failed, error %ld.\n", GetLastError());
2379 ret = IsThemeDialogTextureEnabled(hwnd2);
2380 ok(!ret, "Wrong dialog texture status.\n");
2382 SetParent(child, hwnd2);
2383 ok(GetParent(child) == hwnd2, "Wrong parent.\n");
2384 ret = IsThemeDialogTextureEnabled(hwnd2);
2385 ok(!ret, "Wrong dialog texture status.\n");
2386 InvalidateRect(child, NULL, TRUE);
2387 flush_events();
2388 ret = IsThemeDialogTextureEnabled(hwnd2);
2389 ok(!ret, "Wrong dialog texture status.\n");
2391 DestroyWindow(hwnd2);
2392 DestroyWindow(hwnd);
2394 /* Test that application dialog procedures should be called only once for each message */
2395 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2396 test_EnableThemeDialogTexture_proc,
2397 (LPARAM)&param);
2398 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %ld.\n", GetLastError());
2399 child = dialog_child;
2400 ok(child != NULL, "Failed to get child control, error %ld.\n", GetLastError());
2401 child_hdc = GetDC(child);
2402 for (i = 0; i < ARRAY_SIZE(message_tests); ++i)
2404 sprintf(buffer, "message %#x\n", message_tests[i].msg);
2405 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2406 SendMessageW(dialog, message_tests[i].msg, (WPARAM)child_hdc, (LPARAM)child);
2407 ok_sequence(sequences, PARENT_SEQ_INDEX, message_tests[i].msg_seq, buffer, FALSE);
2409 ReleaseDC(child, child_hdc);
2410 EndDialog(dialog, 0);
2412 UnregisterClassA(cls.lpszClassName, GetModuleHandleA(NULL));
2415 static void test_DrawThemeBackgroundEx(void)
2417 static const int width = 10, height = 10;
2418 static const RECT rect = {0, 0, 10, 10};
2419 HBITMAP bitmap, old_bitmap;
2420 BOOL transparent, found;
2421 BITMAPINFO bitmap_info;
2422 int i, glyph_type;
2423 BYTE *bits, *ptr;
2424 HTHEME htheme;
2425 void *proc;
2426 HDC mem_dc;
2427 HRESULT hr;
2428 HWND hwnd;
2430 proc = GetProcAddress(GetModuleHandleA("uxtheme.dll"), MAKEINTRESOURCEA(47));
2431 ok(proc == (void *)pDrawThemeBackgroundEx, "Expected DrawThemeBackgroundEx() at ordinal 47.\n");
2433 hwnd = CreateWindowA(WC_STATICA, "", WS_POPUP, 0, 0, 1, 1, 0, 0, 0, NULL);
2434 ok(hwnd != NULL, "CreateWindowA failed, error %#lx.\n", GetLastError());
2435 htheme = OpenThemeData(hwnd, L"Spin");
2436 if (!htheme)
2438 skip("Theming is inactive.\n");
2439 DestroyWindow(hwnd);
2440 return;
2443 bitmap_info.bmiHeader.biSize = sizeof(bitmap_info.bmiHeader);
2444 bitmap_info.bmiHeader.biWidth = width;
2445 bitmap_info.bmiHeader.biHeight = height;
2446 bitmap_info.bmiHeader.biPlanes = 1;
2447 bitmap_info.bmiHeader.biBitCount = 32;
2448 bitmap_info.bmiHeader.biCompression = BI_RGB;
2449 bitmap = CreateDIBSection(0, &bitmap_info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
2450 mem_dc = CreateCompatibleDC(NULL);
2451 old_bitmap = SelectObject(mem_dc, bitmap);
2453 /* Drawing opaque background with transparent glyphs should discard the alpha values from the glyphs */
2454 transparent = IsThemeBackgroundPartiallyTransparent(htheme, SPNP_UP, UPS_NORMAL);
2455 ok(!transparent, "Expected spin button background opaque.\n");
2456 hr = GetThemeBool(htheme, SPNP_UP, UPS_NORMAL, TMT_TRANSPARENT, &transparent);
2457 ok(hr == E_PROP_ID_UNSUPPORTED, "Got unexpected hr %#lx.\n", hr);
2458 transparent = FALSE;
2459 hr = GetThemeBool(htheme, SPNP_UP, UPS_NORMAL, TMT_GLYPHTRANSPARENT, &transparent);
2460 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2461 ok(transparent, "Expected spin button glyph transparent.\n");
2463 memset(bits, 0xa5, width * height * sizeof(int));
2464 hr = DrawThemeBackgroundEx(htheme, mem_dc, SPNP_UP, UPS_NORMAL, &rect, NULL);
2465 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2466 ptr = bits;
2467 for (i = 0; i < width * height; ++i)
2469 if (ptr[3] != 0xff)
2470 break;
2472 ptr += 4;
2474 ok(i == width * height || broken(ptr[3] == 0) /* Spin button glyphs on XP don't use alpha */,
2475 "Unexpected alpha value %#x at (%d,%d).\n", ptr[3], i % height, i / height);
2477 /* Drawing transparent background without glyphs should keep the alpha values */
2478 CloseThemeData(htheme);
2479 htheme = OpenThemeData(hwnd, L"Scrollbar");
2480 transparent = IsThemeBackgroundPartiallyTransparent(htheme, SBP_SIZEBOX, SZB_RIGHTALIGN);
2481 ok(transparent, "Expected scrollbar sizebox transparent.\n");
2482 transparent = FALSE;
2483 hr = GetThemeBool(htheme, SBP_SIZEBOX, SZB_RIGHTALIGN, TMT_TRANSPARENT, &transparent);
2484 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2485 ok(transparent, "Expected scrollbar sizebox transparent.\n");
2486 hr = GetThemeEnumValue(htheme, SBP_SIZEBOX, SZB_RIGHTALIGN, TMT_GLYPHTYPE, &glyph_type);
2487 ok(hr == E_PROP_ID_UNSUPPORTED, "Got unexpected hr %#lx.\n", hr);
2489 memset(bits, 0xa5, width * height * sizeof(int));
2490 hr = DrawThemeBackgroundEx(htheme, mem_dc, SBP_SIZEBOX, SZB_RIGHTALIGN, &rect, NULL);
2491 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2492 found = FALSE;
2493 ptr = bits;
2494 for (i = 0; i < width * height; ++i)
2496 if (ptr[3] == 0xa5)
2498 found = TRUE;
2499 break;
2502 ptr += 4;
2504 ok(found, "Expected alpha values found.\n");
2506 /* Drawing transparent background with transparent glyphs should keep alpha values */
2507 CloseThemeData(htheme);
2508 htheme = OpenThemeData(hwnd, L"Header");
2509 if (IsThemePartDefined(htheme, HP_HEADERDROPDOWN, 0))
2511 transparent = IsThemeBackgroundPartiallyTransparent(htheme, HP_HEADERDROPDOWN, HDDS_NORMAL);
2512 ok(transparent, "Expected header dropdown transparent.\n");
2513 transparent = FALSE;
2514 hr = GetThemeBool(htheme, HP_HEADERDROPDOWN, HDDS_NORMAL, TMT_TRANSPARENT, &transparent);
2515 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2516 ok(transparent, "Expected header dropdown background transparent.\n");
2517 transparent = FALSE;
2518 hr = GetThemeBool(htheme, HP_HEADERDROPDOWN, HDDS_NORMAL, TMT_GLYPHTRANSPARENT, &transparent);
2519 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2520 ok(transparent, "Expected header dropdown glyph transparent.\n");
2522 memset(bits, 0xa5, width * height * sizeof(int));
2523 hr = DrawThemeBackgroundEx(htheme, mem_dc, HP_HEADERDROPDOWN, HDDS_NORMAL, &rect, NULL);
2524 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2525 found = FALSE;
2526 ptr = bits;
2527 for (i = 0; i < width * height; ++i)
2529 if (ptr[3] == 0xa5)
2531 found = TRUE;
2532 break;
2535 ptr += 4;
2537 ok(found, "Expected alpha values found.\n");
2539 else
2541 skip("Failed to get header dropdown parts.\n");
2544 SelectObject(mem_dc, old_bitmap);
2545 DeleteDC(mem_dc);
2546 DeleteObject(bitmap);
2547 CloseThemeData(htheme);
2548 DestroyWindow(hwnd);
2551 static void test_GetThemeBackgroundRegion(void)
2553 HTHEME htheme;
2554 HRGN region;
2555 HRESULT hr;
2556 HWND hwnd;
2557 RECT rect;
2558 int ret;
2560 hwnd = CreateWindowA(WC_STATICA, "", WS_POPUP, 0, 0, 1, 1, 0, 0, 0, NULL);
2561 ok(hwnd != NULL, "CreateWindowA failed, error %#lx.\n", GetLastError());
2562 htheme = OpenThemeData(hwnd, L"Rebar");
2563 if (!htheme)
2565 skip("Theming is inactive.\n");
2566 DestroyWindow(hwnd);
2567 return;
2570 hr = GetThemeEnumValue(htheme, RP_BAND, 0, TMT_BGTYPE, &ret);
2571 ok(hr == S_OK, "Got unexpected hr %#lx,\n", hr);
2572 ok(ret == BT_NONE, "Got expected type %d.\n", ret);
2574 SetRect(&rect, 0, 0, 10, 10);
2575 region = (HRGN)0xdeadbeef;
2576 hr = GetThemeBackgroundRegion(htheme, NULL, RP_BAND, 0, &rect, &region);
2577 ok(hr == E_UNEXPECTED || broken(hr == S_OK) /* < Win10 */, "Got unexpected hr %#lx.\n", hr);
2578 ok(region == (HRGN)0xdeadbeef, "Got unexpected region.\n");
2580 CloseThemeData(htheme);
2581 DestroyWindow(hwnd);
2584 static void test_theme(void)
2586 BOOL transparent;
2587 HTHEME htheme;
2588 HRESULT hr;
2589 HWND hwnd;
2591 if (!IsThemeActive())
2593 skip("Theming is inactive.\n");
2594 return;
2597 hwnd = CreateWindowA(WC_STATICA, "", WS_POPUP, 0, 0, 1, 1, 0, 0, 0, NULL);
2598 ok(!!hwnd, "CreateWindowA failed, error %#lx.\n", GetLastError());
2600 /* Test that scrollbar arrow parts are transparent */
2601 htheme = OpenThemeData(hwnd, L"ScrollBar");
2602 ok(!!htheme, "OpenThemeData failed.\n");
2604 transparent = FALSE;
2605 hr = GetThemeBool(htheme, SBP_ARROWBTN, 0, TMT_TRANSPARENT, &transparent);
2606 /* XP does use opaque scrollbar arrow parts and TMT_TRANSPARENT is FALSE */
2607 if (LOBYTE(LOWORD(GetVersion())) < 6)
2609 ok(hr == E_PROP_ID_UNSUPPORTED, "Got unexpected hr %#lx.\n", hr);
2611 transparent = IsThemeBackgroundPartiallyTransparent(htheme, SBP_ARROWBTN, 0);
2612 ok(!transparent, "Expected opaque.\n");
2614 /* > XP use opaque scrollbar arrow parts, but TMT_TRANSPARENT is TRUE */
2615 else
2617 ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
2618 ok(transparent, "Expected transparent.\n");
2620 transparent = IsThemeBackgroundPartiallyTransparent(htheme, SBP_ARROWBTN, 0);
2621 ok(transparent, "Expected transparent.\n");
2623 CloseThemeData(htheme);
2625 DestroyWindow(hwnd);
2628 static void test_ShouldSystemUseDarkMode(void)
2630 DWORD light_theme, light_theme_size = sizeof(light_theme), last_error;
2631 BOOL result;
2632 LSTATUS ls;
2634 if (!pShouldSystemUseDarkMode)
2636 win_skip("ShouldSystemUseDarkMode() is unavailable.\n");
2637 return;
2640 ls = RegGetValueW(HKEY_CURRENT_USER,
2641 L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
2642 L"SystemUsesLightTheme", RRF_RT_REG_DWORD, NULL, &light_theme, &light_theme_size);
2643 if (ls == ERROR_FILE_NOT_FOUND)
2645 skip("SystemUsesLightTheme registry value not found.\n");
2646 return;
2648 ok(ls == 0, "RegGetValue failed: %ld.\n", ls);
2650 SetLastError(0xdeadbeef);
2651 result = pShouldSystemUseDarkMode();
2652 last_error = GetLastError();
2653 ok(last_error == 0xdeadbeef, "ShouldSystemUseDarkMode set last error: %ld.\n", last_error);
2654 ok(result == !light_theme, "Expected value %d, got %d.\n", !light_theme, result);
2657 static void test_ShouldAppsUseDarkMode(void)
2659 DWORD light_theme, light_theme_size = sizeof(light_theme), last_error;
2660 BOOL result;
2661 LSTATUS ls;
2663 if (!pShouldAppsUseDarkMode)
2665 win_skip("ShouldAppsUseDarkMode() is unavailable\n");
2666 return;
2669 ls = RegGetValueW(HKEY_CURRENT_USER,
2670 L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
2671 L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, &light_theme, &light_theme_size);
2672 if (ls == ERROR_FILE_NOT_FOUND)
2674 skip("AppsUseLightTheme registry value not found.\n");
2675 return;
2677 ok(ls == 0, "RegGetValue failed: %ld.\n", ls);
2679 SetLastError(0xdeadbeef);
2680 result = pShouldAppsUseDarkMode();
2681 last_error = GetLastError();
2682 ok(last_error == 0xdeadbeef, "ShouldAppsUseDarkMode set last error: %ld.\n", last_error);
2683 ok(result == !light_theme, "Expected value %d, got %d\n", !light_theme, result);
2686 START_TEST(system)
2688 ULONG_PTR ctx_cookie;
2689 HANDLE ctx;
2691 init_funcs();
2692 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
2694 test_IsThemed();
2695 test_IsThemePartDefined();
2696 test_GetWindowTheme();
2697 test_SetWindowTheme();
2698 test_OpenThemeData();
2699 test_OpenThemeDataEx();
2700 test_OpenThemeDataForDpi();
2701 test_GetCurrentThemeName();
2702 test_GetThemePartSize();
2703 test_CloseThemeData();
2704 test_buffered_paint();
2705 test_GetThemeIntList();
2706 test_GetThemeTransitionDuration();
2707 test_DrawThemeParentBackground();
2708 test_DrawThemeBackgroundEx();
2709 test_GetThemeBackgroundRegion();
2710 test_theme();
2711 test_ShouldSystemUseDarkMode();
2712 test_ShouldAppsUseDarkMode();
2714 if (load_v6_module(&ctx_cookie, &ctx))
2716 test_EnableThemeDialogTexture();
2718 unload_v6_module(ctx_cookie, ctx);
2721 /* Test EnableTheming() in the end because it may disable theming */
2722 test_EnableTheming();