d3d11/tests: Add some tests for ID3DUserDefinedAnnotation.
[wine.git] / dlls / uxtheme / tests / system.c
blobafc05b8549fd5b02f5e73c35fcf89438abba208d
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 *pEndBufferedPaint)(HPAINTBUFFER, BOOL);
44 static HRESULT (WINAPI *pGetBufferedPaintBits)(HPAINTBUFFER, RGBQUAD **, int *);
45 static HDC (WINAPI *pGetBufferedPaintDC)(HPAINTBUFFER);
46 static HDC (WINAPI *pGetBufferedPaintTargetDC)(HPAINTBUFFER);
47 static HRESULT (WINAPI *pGetBufferedPaintTargetRect)(HPAINTBUFFER, RECT *);
48 static HRESULT (WINAPI *pGetThemeIntList)(HTHEME, int, int, int, INTLIST *);
49 static HRESULT (WINAPI *pGetThemeTransitionDuration)(HTHEME, int, int, int, int, DWORD *);
51 static LONG (WINAPI *pDisplayConfigGetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER *);
52 static LONG (WINAPI *pDisplayConfigSetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER *);
53 static BOOL (WINAPI *pGetDpiForMonitorInternal)(HMONITOR, UINT, UINT *, UINT *);
54 static DPI_AWARENESS_CONTEXT (WINAPI *pSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
56 static NTSTATUS (WINAPI *pD3DKMTCloseAdapter)(const D3DKMT_CLOSEADAPTER *);
57 static NTSTATUS (WINAPI *pD3DKMTOpenAdapterFromGdiDisplayName)(D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME *);
59 /* For message tests */
60 enum seq_index
62 PARENT_SEQ_INDEX,
63 CHILD_SEQ_INDEX,
64 NUM_MSG_SEQUENCES
67 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
69 static void init_funcs(void)
71 HMODULE user32 = GetModuleHandleA("user32.dll");
72 HMODULE gdi32 = GetModuleHandleA("gdi32.dll");
73 HMODULE uxtheme = GetModuleHandleA("uxtheme.dll");
75 #define GET_PROC(module, func) \
76 p##func = (void *)GetProcAddress(module, #func); \
77 if (!p##func) \
78 trace("GetProcAddress(%s, %s) failed.\n", #module, #func);
80 GET_PROC(uxtheme, BeginBufferedPaint)
81 GET_PROC(uxtheme, BufferedPaintClear)
82 GET_PROC(uxtheme, EndBufferedPaint)
83 GET_PROC(uxtheme, GetBufferedPaintBits)
84 GET_PROC(uxtheme, GetBufferedPaintDC)
85 GET_PROC(uxtheme, GetBufferedPaintTargetDC)
86 GET_PROC(uxtheme, GetBufferedPaintTargetRect)
87 GET_PROC(uxtheme, GetThemeIntList)
88 GET_PROC(uxtheme, GetThemeTransitionDuration)
89 GET_PROC(uxtheme, OpenThemeDataEx)
90 GET_PROC(uxtheme, OpenThemeDataForDpi)
92 GET_PROC(user32, DisplayConfigGetDeviceInfo)
93 GET_PROC(user32, DisplayConfigSetDeviceInfo)
94 GET_PROC(user32, GetDpiForMonitorInternal)
95 GET_PROC(user32, SetThreadDpiAwarenessContext)
97 GET_PROC(gdi32, D3DKMTCloseAdapter)
98 GET_PROC(gdi32, D3DKMTOpenAdapterFromGdiDisplayName)
100 #undef GET_PROC
103 /* Try to make sure pending X events have been processed before continuing */
104 static void flush_events(void)
106 MSG msg;
107 int diff = 200;
108 int min_timeout = 100;
109 DWORD time = GetTickCount() + diff;
111 while (diff > 0)
113 if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT)
114 break;
115 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
116 DispatchMessageA(&msg);
117 diff = time - GetTickCount();
121 static unsigned int get_primary_monitor_effective_dpi(void)
123 DPI_AWARENESS_CONTEXT old_context;
124 UINT dpi_x = 0, dpi_y = 0;
125 POINT point = {0, 0};
126 HMONITOR monitor;
128 if (pSetThreadDpiAwarenessContext && pGetDpiForMonitorInternal)
130 old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
131 monitor = MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY);
132 pGetDpiForMonitorInternal(monitor, 0, &dpi_x, &dpi_y);
133 pSetThreadDpiAwarenessContext(old_context);
134 return dpi_y;
137 return USER_DEFAULT_SCREEN_DPI;
140 static int get_dpi_scale_index(int dpi)
142 static const int scales[] = {100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500};
143 int scale, scale_idx;
145 scale = dpi * 100 / 96;
146 for (scale_idx = 0; scale_idx < ARRAY_SIZE(scales); ++scale_idx)
148 if (scales[scale_idx] == scale)
149 return scale_idx;
152 return -1;
155 static BOOL set_primary_monitor_effective_dpi(unsigned int primary_dpi)
157 D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter_gdi_desc;
158 DISPLAYCONFIG_GET_SOURCE_DPI_SCALE get_scale_req;
159 DISPLAYCONFIG_SET_SOURCE_DPI_SCALE set_scale_req;
160 int current_scale_idx, target_scale_idx;
161 D3DKMT_CLOSEADAPTER close_adapter_desc;
162 BOOL ret = FALSE;
163 LONG error;
165 #define CHECK_FUNC(func) \
166 if (!p##func) \
168 skip("%s() is unavailable.\n", #func); \
169 return FALSE; \
172 CHECK_FUNC(D3DKMTCloseAdapter)
173 CHECK_FUNC(D3DKMTOpenAdapterFromGdiDisplayName)
174 CHECK_FUNC(DisplayConfigGetDeviceInfo)
175 CHECK_FUNC(DisplayConfigSetDeviceInfo)
177 #undef CHECK_FUNC
179 lstrcpyW(open_adapter_gdi_desc.DeviceName, L"\\\\.\\DISPLAY1");
180 if (pD3DKMTOpenAdapterFromGdiDisplayName(&open_adapter_gdi_desc) == STATUS_PROCEDURE_NOT_FOUND)
182 win_skip("D3DKMTOpenAdapterFromGdiDisplayName() is unavailable.\n");
183 return FALSE;
186 get_scale_req.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_DPI_SCALE;
187 get_scale_req.header.size = sizeof(get_scale_req);
188 get_scale_req.header.adapterId = open_adapter_gdi_desc.AdapterLuid;
189 get_scale_req.header.id = open_adapter_gdi_desc.VidPnSourceId;
190 error = pDisplayConfigGetDeviceInfo(&get_scale_req.header);
191 if (error != NO_ERROR)
193 skip("DisplayConfigGetDeviceInfo failed, returned %d.\n", error);
194 goto failed;
197 current_scale_idx = get_dpi_scale_index(get_primary_monitor_effective_dpi());
198 if (current_scale_idx == -1)
200 skip("Failed to find current scale index.\n");
201 goto failed;
204 target_scale_idx = get_dpi_scale_index(primary_dpi);
205 if (target_scale_idx < get_scale_req.minRelativeScaleStep
206 || target_scale_idx > get_scale_req.maxRelativeScaleStep)
208 skip("DPI %d is not available.\n", primary_dpi);
209 goto failed;
212 set_scale_req.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_SOURCE_DPI_SCALE;
213 set_scale_req.header.size = sizeof(set_scale_req);
214 set_scale_req.header.adapterId = open_adapter_gdi_desc.AdapterLuid;
215 set_scale_req.header.id = open_adapter_gdi_desc.VidPnSourceId;
216 set_scale_req.relativeScaleStep = get_scale_req.curRelativeScaleStep + (target_scale_idx - current_scale_idx);
217 error = pDisplayConfigSetDeviceInfo(&set_scale_req.header);
218 if (error == NO_ERROR)
219 ret = get_primary_monitor_effective_dpi() == primary_dpi;
220 flush_events();
222 failed:
223 close_adapter_desc.hAdapter = open_adapter_gdi_desc.hAdapter;
224 pD3DKMTCloseAdapter(&close_adapter_desc);
225 return ret;
228 static LRESULT WINAPI TestSetWindowThemeParentProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
230 static LONG defwndproc_counter;
231 struct message msg = {0};
232 LRESULT ret;
234 /* Only care about WM_THEMECHANGED */
235 if (message != WM_THEMECHANGED)
236 return DefWindowProcA(hwnd, message, wParam, lParam);
238 msg.message = message;
239 msg.flags = sent | wparam | lparam;
240 if (defwndproc_counter)
241 msg.flags |= defwinproc;
242 msg.wParam = wParam;
243 msg.lParam = lParam;
244 add_message(sequences, PARENT_SEQ_INDEX, &msg);
246 InterlockedIncrement(&defwndproc_counter);
247 ret = DefWindowProcA(hwnd, message, wParam, lParam);
248 InterlockedDecrement(&defwndproc_counter);
249 return ret;
252 static LRESULT WINAPI TestSetWindowThemeChildProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
254 static LONG defwndproc_counter;
255 struct message msg = {0};
256 LRESULT ret;
258 /* Only care about WM_THEMECHANGED */
259 if (message != WM_THEMECHANGED)
260 return DefWindowProcA(hwnd, message, wParam, lParam);
262 msg.message = message;
263 msg.flags = sent | wparam | lparam;
264 if (defwndproc_counter)
265 msg.flags |= defwinproc;
266 msg.wParam = wParam;
267 msg.lParam = lParam;
268 add_message(sequences, CHILD_SEQ_INDEX, &msg);
270 InterlockedIncrement(&defwndproc_counter);
271 ret = DefWindowProcA(hwnd, message, wParam, lParam);
272 InterlockedDecrement(&defwndproc_counter);
273 return ret;
276 static void test_IsThemed(void)
278 BOOL bThemeActive;
279 BOOL bAppThemed;
280 BOOL bTPDefined;
282 bThemeActive = IsThemeActive();
283 trace("Theming is %s\n", (bThemeActive) ? "active" : "inactive");
285 bAppThemed = IsAppThemed();
286 trace("Test executable is %s\n", (bAppThemed) ? "themed" : "not themed");
288 SetLastError(0xdeadbeef);
289 bTPDefined = IsThemePartDefined(NULL, 0 , 0);
290 ok( bTPDefined == FALSE, "Expected FALSE\n");
291 ok( GetLastError() == E_HANDLE,
292 "Expected E_HANDLE, got 0x%08x\n",
293 GetLastError());
296 static void test_GetWindowTheme(void)
298 HTHEME hTheme;
299 HWND hWnd;
301 SetLastError(0xdeadbeef);
302 hTheme = GetWindowTheme(NULL);
303 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
304 ok( GetLastError() == E_HANDLE, "Expected E_HANDLE, got 0x%08x\n", GetLastError() );
306 /* Only do the bare minimum to get a valid hwnd */
307 hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
308 ok(hWnd != NULL, "Failed to create a test window.\n");
310 SetLastError(0xdeadbeef);
311 hTheme = GetWindowTheme(hWnd);
312 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
313 ok( GetLastError() == 0xdeadbeef,
314 "Expected 0xdeadbeef, got 0x%08x\n",
315 GetLastError());
317 DestroyWindow(hWnd);
320 static const struct message SetWindowThemeSeq[] =
322 {WM_THEMECHANGED, sent},
326 static const struct message EmptySeq[] =
331 static void test_SetWindowTheme(void)
333 WNDCLASSA cls = {0};
334 HWND hWnd, child;
335 HTHEME hTheme;
336 HRESULT hRes;
337 MSG msg;
339 hRes = SetWindowTheme(NULL, NULL, NULL);
340 ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08x\n", hRes);
342 /* Test that WM_THEMECHANGED is sent and not posted to windows */
343 cls.hInstance = GetModuleHandleA(0);
344 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
345 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
346 cls.lpfnWndProc = TestSetWindowThemeParentProcA;
347 cls.lpszClassName = "TestSetWindowThemeParentClass";
348 RegisterClassA(&cls);
350 cls.lpfnWndProc = TestSetWindowThemeChildProcA;
351 cls.lpszClassName = "TestSetWindowThemeChildClass";
352 RegisterClassA(&cls);
354 hWnd = CreateWindowA("TestSetWindowThemeParentClass", "parent",
355 WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, 0, 0, 0, NULL);
356 ok(!!hWnd, "Failed to create a parent window.\n");
357 child = CreateWindowA("TestSetWindowThemeChildClass", "child", WS_CHILD | WS_VISIBLE, 0, 0, 50,
358 50, hWnd, 0, 0, NULL);
359 ok(!!child, "Failed to create a child window.\n");
360 flush_events();
361 flush_sequences(sequences, NUM_MSG_SEQUENCES);
363 hRes = SetWindowTheme(hWnd, NULL, NULL);
364 ok(hRes == S_OK, "Expected %#x, got %#x.\n", S_OK, hRes);
365 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
367 struct message recv_msg = {0};
369 if (msg.message == WM_THEMECHANGED)
371 recv_msg.message = msg.message;
372 recv_msg.flags = posted | wparam | lparam;
373 recv_msg.wParam = msg.wParam;
374 recv_msg.lParam = msg.lParam;
375 add_message(sequences, msg.hwnd == hWnd ? PARENT_SEQ_INDEX : CHILD_SEQ_INDEX, &recv_msg);
377 DispatchMessageA(&msg);
379 ok_sequence(sequences, PARENT_SEQ_INDEX, SetWindowThemeSeq, "SetWindowTheme parent", FALSE);
380 ok_sequence(sequences, CHILD_SEQ_INDEX, EmptySeq, "SetWindowTheme child", FALSE);
381 DestroyWindow(hWnd);
382 UnregisterClassA("TestSetWindowThemeParentClass", GetModuleHandleA(0));
383 UnregisterClassA("TestSetWindowThemeChildClass", GetModuleHandleA(0));
385 /* Only do the bare minimum to get a valid hwnd */
386 hWnd = CreateWindowExA(0, "button", "test", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
387 ok(hWnd != NULL, "Failed to create a test window.\n");
389 hRes = SetWindowTheme(hWnd, NULL, NULL);
390 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
392 if (IsThemeActive())
394 hTheme = OpenThemeData(hWnd, L"Button");
395 ok(!!hTheme, "OpenThemeData failed.\n");
396 CloseThemeData(hTheme);
398 hRes = SetWindowTheme(hWnd, L"deadbeef", NULL);
399 ok(hRes == S_OK, "Expected S_OK, got 0x%08x.\n", hRes);
401 hTheme = OpenThemeData(hWnd, L"Button");
402 ok(!!hTheme, "OpenThemeData failed.\n");
403 CloseThemeData(hTheme);
405 else
407 skip("No active theme, skipping rest of SetWindowTheme tests.\n");
410 DestroyWindow(hWnd);
413 static void test_OpenThemeData(void)
415 HTHEME hTheme, hTheme2;
416 HWND hWnd;
417 BOOL bThemeActive;
418 HRESULT hRes;
419 BOOL bTPDefined;
421 const WCHAR szInvalidClassList[] = L"DEADBEEF";
422 const WCHAR szButtonClassList[] = L"Button";
423 const WCHAR szButtonClassList2[] = L"bUtToN";
424 const WCHAR szClassList[] = L"Button;ListBox";
426 bThemeActive = IsThemeActive();
428 /* All NULL */
429 SetLastError(0xdeadbeef);
430 hTheme = OpenThemeData(NULL, NULL);
431 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
432 ok( GetLastError() == E_POINTER,
433 "Expected GLE() to be E_POINTER, got 0x%08x\n",
434 GetLastError());
436 /* A NULL hWnd and an invalid classlist */
437 SetLastError(0xdeadbeef);
438 hTheme = OpenThemeData(NULL, szInvalidClassList);
439 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
440 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
441 E_PROP_ID_UNSUPPORTED, GetLastError() );
443 SetLastError(0xdeadbeef);
444 hTheme = OpenThemeData(NULL, szClassList);
445 if (bThemeActive)
447 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
448 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
450 else
452 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
453 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
454 E_PROP_ID_UNSUPPORTED, GetLastError() );
457 /* Only do the bare minimum to get a valid hdc */
458 hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
459 if (!hWnd) return;
461 SetLastError(0xdeadbeef);
462 hTheme = OpenThemeData(hWnd, NULL);
463 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
464 ok( GetLastError() == E_POINTER,
465 "Expected GLE() to be E_POINTER, got 0x%08x\n",
466 GetLastError());
468 SetLastError(0xdeadbeef);
469 hTheme = OpenThemeData(hWnd, szInvalidClassList);
470 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
471 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
472 E_PROP_ID_UNSUPPORTED, GetLastError() );
474 if (!bThemeActive)
476 SetLastError(0xdeadbeef);
477 hTheme = OpenThemeData(hWnd, szButtonClassList);
478 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
479 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
480 E_PROP_ID_UNSUPPORTED, GetLastError() );
481 skip("No active theme, skipping rest of OpenThemeData tests\n");
482 return;
485 /* Only do the next checks if we have an active theme */
487 SetLastError(0xdeadbeef);
488 hTheme = OpenThemeData(hWnd, szButtonClassList);
489 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
490 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
492 /* Test with bUtToN instead of Button */
493 SetLastError(0xdeadbeef);
494 hTheme = OpenThemeData(hWnd, szButtonClassList2);
495 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
496 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
498 SetLastError(0xdeadbeef);
499 hTheme = OpenThemeData(hWnd, szClassList);
500 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
501 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
503 /* GetWindowTheme should return the last handle opened by OpenThemeData */
504 SetLastError(0xdeadbeef);
505 hTheme2 = GetWindowTheme(hWnd);
506 ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n",
507 hTheme, hTheme2);
508 ok( GetLastError() == 0xdeadbeef,
509 "Expected 0xdeadbeef, got 0x%08x\n",
510 GetLastError());
512 hRes = CloseThemeData(hTheme);
513 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
515 /* Close a second time */
516 hRes = CloseThemeData(hTheme);
517 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
519 /* See if closing makes a difference for GetWindowTheme */
520 SetLastError(0xdeadbeef);
521 hTheme2 = NULL;
522 hTheme2 = GetWindowTheme(hWnd);
523 ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n",
524 hTheme, hTheme2);
525 ok( GetLastError() == 0xdeadbeef,
526 "Expected 0xdeadbeef, got 0x%08x\n",
527 GetLastError());
529 SetLastError(0xdeadbeef);
530 bTPDefined = IsThemePartDefined(hTheme, 0 , 0);
531 todo_wine
533 ok( bTPDefined == FALSE, "Expected FALSE\n");
534 ok( GetLastError() == ERROR_SUCCESS,
535 "Expected ERROR_SUCCESS, got 0x%08x\n",
536 GetLastError());
539 DestroyWindow(hWnd);
542 static void test_OpenThemeDataEx(void)
544 HTHEME hTheme;
545 HWND hWnd;
546 BOOL bThemeActive;
548 const WCHAR szInvalidClassList[] = L"DEADBEEF";
549 const WCHAR szButtonClassList[] = L"Button";
550 const WCHAR szButtonClassList2[] = L"bUtToN";
551 const WCHAR szClassList[] = L"Button;ListBox";
553 if (!pOpenThemeDataEx)
555 win_skip("OpenThemeDataEx not available\n");
556 return;
559 bThemeActive = IsThemeActive();
561 /* All NULL */
562 SetLastError(0xdeadbeef);
563 hTheme = pOpenThemeDataEx(NULL, NULL, 0);
564 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
565 ok( GetLastError() == E_POINTER,
566 "Expected GLE() to be E_POINTER, got 0x%08x\n",
567 GetLastError());
569 /* A NULL hWnd and an invalid classlist without flags */
570 SetLastError(0xdeadbeef);
571 hTheme = pOpenThemeDataEx(NULL, szInvalidClassList, 0);
572 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
573 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
574 E_PROP_ID_UNSUPPORTED, GetLastError() );
576 SetLastError(0xdeadbeef);
577 hTheme = pOpenThemeDataEx(NULL, szClassList, 0);
578 if (bThemeActive)
580 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
581 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
583 else
585 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
586 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
587 E_PROP_ID_UNSUPPORTED, GetLastError() );
590 /* Only do the bare minimum to get a valid hdc */
591 hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL);
592 if (!hWnd) return;
594 SetLastError(0xdeadbeef);
595 hTheme = pOpenThemeDataEx(hWnd, NULL, 0);
596 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
597 ok( GetLastError() == E_POINTER,
598 "Expected GLE() to be E_POINTER, got 0x%08x\n",
599 GetLastError());
601 SetLastError(0xdeadbeef);
602 hTheme = pOpenThemeDataEx(hWnd, szInvalidClassList, 0);
603 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
604 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
605 E_PROP_ID_UNSUPPORTED, GetLastError() );
607 if (!bThemeActive)
609 SetLastError(0xdeadbeef);
610 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0);
611 ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme);
612 ok( GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected 0x%08x, got 0x%08x\n",
613 E_PROP_ID_UNSUPPORTED, GetLastError() );
614 skip("No active theme, skipping rest of OpenThemeDataEx tests\n");
615 return;
618 /* Only do the next checks if we have an active theme */
620 SetLastError(0xdeadbeef);
621 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0);
622 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
623 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
625 SetLastError(0xdeadbeef);
626 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, OTD_FORCE_RECT_SIZING);
627 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
628 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
630 SetLastError(0xdeadbeef);
631 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, OTD_NONCLIENT);
632 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
633 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
635 SetLastError(0xdeadbeef);
636 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList, 0x3);
637 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
638 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
640 /* Test with bUtToN instead of Button */
641 SetLastError(0xdeadbeef);
642 hTheme = pOpenThemeDataEx(hWnd, szButtonClassList2, 0);
643 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
644 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
646 SetLastError(0xdeadbeef);
647 hTheme = pOpenThemeDataEx(hWnd, szClassList, 0);
648 ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n");
649 ok( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got 0x%08x\n", GetLastError() );
651 DestroyWindow(hWnd);
654 static void test_OpenThemeDataForDpi(void)
656 BOOL is_theme_active;
657 HTHEME htheme;
659 if (!pOpenThemeDataForDpi)
661 win_skip("OpenThemeDataForDpi is unavailable.\n");
662 return;
665 is_theme_active = IsThemeActive();
666 SetLastError(0xdeadbeef);
667 htheme = pOpenThemeDataForDpi(NULL, WC_BUTTONW, 96);
668 if (is_theme_active)
670 ok(!!htheme, "Got a NULL handle.\n");
671 ok(GetLastError() == NO_ERROR, "Expected error %u, got %u.\n", NO_ERROR, GetLastError());
672 CloseThemeData(htheme);
674 else
676 ok(!htheme, "Got a non-NULL handle.\n");
677 ok(GetLastError() == E_PROP_ID_UNSUPPORTED, "Expected error %u, got %u.\n",
678 E_PROP_ID_UNSUPPORTED, GetLastError());
682 static void test_GetCurrentThemeName(void)
684 BOOL bThemeActive;
685 HRESULT hRes;
686 WCHAR currentTheme[MAX_PATH];
687 WCHAR currentColor[MAX_PATH];
688 WCHAR currentSize[MAX_PATH];
690 bThemeActive = IsThemeActive();
692 /* All NULLs */
693 hRes = GetCurrentThemeName(NULL, 0, NULL, 0, NULL, 0);
694 if (bThemeActive)
695 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
696 else
697 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
699 /* Number of characters given is 0 */
700 hRes = GetCurrentThemeName(currentTheme, 0, NULL, 0, NULL, 0);
701 if (bThemeActive)
702 ok( hRes == S_OK || broken(hRes == E_FAIL /* WinXP SP1 */), "Expected S_OK, got 0x%08x\n", hRes);
703 else
704 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
706 hRes = GetCurrentThemeName(currentTheme, 2, NULL, 0, NULL, 0);
707 if (bThemeActive)
708 todo_wine
709 ok(hRes == E_NOT_SUFFICIENT_BUFFER ||
710 broken(hRes == E_FAIL /* WinXP SP1 */),
711 "Expected E_NOT_SUFFICIENT_BUFFER, got 0x%08x\n", hRes);
712 else
713 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
715 /* The same is true if the number of characters is too small for Color and/or Size */
716 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor, 2,
717 currentSize, ARRAY_SIZE(currentSize));
718 if (bThemeActive)
719 todo_wine
720 ok(hRes == E_NOT_SUFFICIENT_BUFFER ||
721 broken(hRes == E_FAIL /* WinXP SP1 */),
722 "Expected E_NOT_SUFFICIENT_BUFFER, got 0x%08x\n", hRes);
723 else
724 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
726 /* Given number of characters is correct */
727 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), NULL, 0, NULL, 0);
728 if (bThemeActive)
729 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
730 else
731 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
733 /* Given number of characters for the theme name is too large */
734 hRes = GetCurrentThemeName(currentTheme, sizeof(currentTheme), NULL, 0, NULL, 0);
735 if (bThemeActive)
736 ok( hRes == E_POINTER || hRes == S_OK, "Expected E_POINTER or S_OK, got 0x%08x\n", hRes);
737 else
738 ok( hRes == E_PROP_ID_UNSUPPORTED ||
739 hRes == E_POINTER, /* win2k3 */
740 "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
742 /* The too large case is only for the theme name, not for color name or size name */
743 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
744 sizeof(currentTheme), currentSize, ARRAY_SIZE(currentSize));
745 if (bThemeActive)
746 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
747 else
748 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
750 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
751 ARRAY_SIZE(currentTheme), currentSize, sizeof(currentSize));
752 if (bThemeActive)
753 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
754 else
755 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
757 /* Correct call */
758 hRes = GetCurrentThemeName(currentTheme, ARRAY_SIZE(currentTheme), currentColor,
759 ARRAY_SIZE(currentColor), currentSize, ARRAY_SIZE(currentSize));
760 if (bThemeActive)
761 ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes);
762 else
763 ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes);
766 static void test_CloseThemeData(void)
768 HRESULT hRes;
770 hRes = CloseThemeData(NULL);
771 ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08x\n", hRes);
772 hRes = CloseThemeData(INVALID_HANDLE_VALUE);
773 ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08x\n", hRes);
776 static void test_buffer_dc_props(HDC hdc, const RECT *rect)
778 static const XFORM ident = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
779 XFORM xform;
780 POINT org;
781 RECT box;
782 BOOL ret;
784 ret = GetWorldTransform(hdc, &xform);
785 ok(ret, "Failed to get world transform\n");
786 ok(!memcmp(&xform, &ident, sizeof(xform)), "Unexpected world transform\n");
788 ret = GetViewportOrgEx(hdc, &org);
789 ok(ret, "Failed to get vport origin\n");
790 ok(org.x == 0 && org.y == 0, "Unexpected vport origin\n");
792 ret = GetWindowOrgEx(hdc, &org);
793 ok(ret, "Failed to get vport origin\n");
794 ok(org.x == rect->left && org.y == rect->top, "Unexpected window origin\n");
796 ret = GetClipBox(hdc, &box);
797 ok(ret, "Failed to get clip box\n");
798 ok(box.left == rect->left && box.top == rect->top, "Unexpected clip box\n");
800 ok(GetGraphicsMode(hdc) == GM_COMPATIBLE, "wrong graphics mode\n");
803 static void test_buffered_paint(void)
805 HDC target, src, hdc, screen_dc;
806 BP_PAINTPARAMS params = { 0 };
807 BP_BUFFERFORMAT format;
808 HPAINTBUFFER buffer;
809 RECT rect, rect2;
810 RGBQUAD *bits;
811 HBITMAP hbm;
812 HRESULT hr;
813 int row;
815 if (!pBeginBufferedPaint)
817 win_skip("Buffered painting API is not supported.\n");
818 return;
821 buffer = pBeginBufferedPaint(NULL, NULL, BPBF_COMPATIBLEBITMAP,
822 NULL, NULL);
823 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
825 target = CreateCompatibleDC(0);
826 buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
827 NULL, NULL);
828 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
830 params.cbSize = sizeof(params);
831 buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
832 &params, NULL);
833 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
835 src = (void *)0xdeadbeef;
836 buffer = pBeginBufferedPaint(target, NULL, BPBF_COMPATIBLEBITMAP,
837 &params, &src);
838 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
839 ok(src == NULL, "Unexpected buffered dc %p\n", src);
841 /* target rect is mandatory */
842 SetRectEmpty(&rect);
843 src = (void *)0xdeadbeef;
844 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
845 &params, &src);
846 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
847 ok(src == NULL, "Unexpected buffered dc %p\n", src);
849 /* inverted rectangle */
850 SetRect(&rect, 10, 0, 5, 5);
851 src = (void *)0xdeadbeef;
852 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
853 &params, &src);
854 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
855 ok(src == NULL, "Unexpected buffered dc %p\n", src);
857 SetRect(&rect, 0, 10, 5, 0);
858 src = (void *)0xdeadbeef;
859 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
860 &params, &src);
861 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
862 ok(src == NULL, "Unexpected buffered dc %p\n", src);
864 /* valid rectangle, no target dc */
865 SetRect(&rect, 0, 0, 5, 5);
866 src = (void *)0xdeadbeef;
867 buffer = pBeginBufferedPaint(NULL, &rect, BPBF_COMPATIBLEBITMAP,
868 &params, &src);
869 ok(buffer == NULL, "Unexpected buffer %p\n", buffer);
870 ok(src == NULL, "Unexpected buffered dc %p\n", src);
872 SetRect(&rect, 0, 0, 5, 5);
873 src = NULL;
874 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
875 &params, &src);
876 ok(buffer != NULL, "Unexpected buffer %p\n", buffer);
877 ok(src != NULL, "Expected buffered dc\n");
878 hr = pEndBufferedPaint(buffer, FALSE);
879 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
881 SetRect(&rect, 0, 0, 5, 5);
882 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP,
883 &params, &src);
884 ok(buffer != NULL, "Unexpected buffer %p\n", buffer);
886 /* clearing */
887 hr = pBufferedPaintClear(NULL, NULL);
888 todo_wine
889 ok(hr == E_FAIL, "Unexpected return code %#x\n", hr);
891 hr = pBufferedPaintClear(buffer, NULL);
892 todo_wine
893 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
895 /* access buffer attributes */
896 hdc = pGetBufferedPaintDC(buffer);
897 ok(hdc == src, "Unexpected hdc, %p, buffered dc %p\n", hdc, src);
899 hdc = pGetBufferedPaintTargetDC(buffer);
900 ok(hdc == target, "Unexpected target hdc %p, original %p\n", hdc, target);
902 hr = pGetBufferedPaintTargetRect(NULL, NULL);
903 ok(hr == E_POINTER, "Unexpected return code %#x\n", hr);
905 hr = pGetBufferedPaintTargetRect(buffer, NULL);
906 ok(hr == E_POINTER, "Unexpected return code %#x\n", hr);
908 hr = pGetBufferedPaintTargetRect(NULL, &rect2);
909 ok(hr == E_FAIL, "Unexpected return code %#x\n", hr);
911 SetRectEmpty(&rect2);
912 hr = pGetBufferedPaintTargetRect(buffer, &rect2);
913 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
914 ok(EqualRect(&rect, &rect2), "Wrong target rect\n");
916 hr = pEndBufferedPaint(buffer, FALSE);
917 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
919 /* invalid buffer handle */
920 hr = pEndBufferedPaint(NULL, FALSE);
921 ok(hr == E_INVALIDARG, "Unexpected return code %#x\n", hr);
923 hdc = pGetBufferedPaintDC(NULL);
924 ok(hdc == NULL, "Unexpected hdc %p\n", hdc);
926 hdc = pGetBufferedPaintTargetDC(NULL);
927 ok(hdc == NULL, "Unexpected target hdc %p\n", hdc);
929 hr = pGetBufferedPaintTargetRect(NULL, &rect2);
930 ok(hr == E_FAIL, "Unexpected return code %#x\n", hr);
932 hr = pGetBufferedPaintTargetRect(NULL, NULL);
933 ok(hr == E_POINTER, "Unexpected return code %#x\n", hr);
935 bits = (void *)0xdeadbeef;
936 row = 10;
937 hr = pGetBufferedPaintBits(NULL, &bits, &row);
938 ok(hr == E_FAIL, "Unexpected return code %#x\n", hr);
939 ok(row == 10, "Unexpected row count %d\n", row);
940 ok(bits == (void *)0xdeadbeef, "Unexpected data pointer %p\n", bits);
942 hr = pGetBufferedPaintBits(NULL, NULL, NULL);
943 ok(hr == E_POINTER, "Unexpected return code %#x\n", hr);
945 hr = pGetBufferedPaintBits(NULL, &bits, NULL);
946 ok(hr == E_POINTER, "Unexpected return code %#x\n", hr);
948 hr = pGetBufferedPaintBits(NULL, NULL, &row);
949 ok(hr == E_POINTER, "Unexpected return code %#x\n", hr);
951 screen_dc = GetDC(0);
953 hdc = CreateCompatibleDC(screen_dc);
954 ok(hdc != NULL, "Failed to create a DC\n");
955 hbm = CreateCompatibleBitmap(screen_dc, 64, 64);
956 ok(hbm != NULL, "Failed to create a bitmap\n");
957 SelectObject(hdc, hbm);
959 ReleaseDC(0, screen_dc);
961 SetRect(&rect, 1, 2, 34, 56);
963 buffer = pBeginBufferedPaint(hdc, &rect, BPBF_COMPATIBLEBITMAP, NULL, &src);
964 test_buffer_dc_props(src, &rect);
965 hr = pEndBufferedPaint(buffer, FALSE);
966 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
968 DeleteObject(hbm);
969 DeleteDC(hdc);
971 buffer = pBeginBufferedPaint(target, &rect, BPBF_COMPATIBLEBITMAP, NULL, &src);
972 test_buffer_dc_props(src, &rect);
973 hr = pEndBufferedPaint(buffer, FALSE);
974 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
976 /* access buffer bits */
977 for (format = BPBF_COMPATIBLEBITMAP; format <= BPBF_TOPDOWNMONODIB; format++)
979 buffer = pBeginBufferedPaint(target, &rect, format, &params, &src);
981 /* only works for DIB buffers */
982 bits = NULL;
983 row = 0;
984 hr = pGetBufferedPaintBits(buffer, &bits, &row);
985 if (format == BPBF_COMPATIBLEBITMAP)
986 ok(hr == E_FAIL, "Unexpected return code %#x\n", hr);
987 else
989 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
990 ok(bits != NULL, "Bitmap bits %p\n", bits);
991 ok(row >= (rect.right - rect.left), "format %d: bitmap width %d\n", format, row);
994 hr = pEndBufferedPaint(buffer, FALSE);
995 ok(hr == S_OK, "Unexpected return code %#x\n", hr);
998 DeleteDC(target);
1001 static void test_GetThemePartSize(void)
1003 static const DWORD enabled = 1;
1004 DPI_AWARENESS_CONTEXT old_context;
1005 unsigned int old_dpi, current_dpi;
1006 HTHEME htheme = NULL;
1007 HWND hwnd = NULL;
1008 SIZE size, size2;
1009 HKEY key = NULL;
1010 HRESULT hr;
1011 HDC hdc;
1013 if (!pSetThreadDpiAwarenessContext)
1015 win_skip("SetThreadDpiAwarenessContext is unavailable.\n");
1016 return;
1019 /* Set IgnorePerProcessSystemDPIToast to 1 to disable "fix blurry apps popup" on Windows 10,
1020 * which may interfere other tests because it steals focus */
1021 RegOpenKeyA(HKEY_CURRENT_USER, "Control Panel\\Desktop", &key);
1022 RegSetValueExA(key, "IgnorePerProcessSystemDPIToast", 0, REG_DWORD, (const BYTE *)&enabled,
1023 sizeof(enabled));
1025 old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
1026 current_dpi = get_primary_monitor_effective_dpi();
1027 old_dpi = current_dpi;
1028 /* DPI needs to be 50% larger than 96 to avoid the effect of TrueSizeStretchMark */
1029 if (current_dpi < 192 && !set_primary_monitor_effective_dpi(192))
1031 skip("Failed to set primary monitor dpi to 192.\n");
1032 goto done;
1035 hwnd = CreateWindowA("Button", "Test", WS_POPUP, 100, 100, 100, 100, NULL, NULL, NULL, NULL);
1036 htheme = OpenThemeData(hwnd, WC_BUTTONW);
1037 if (!htheme)
1039 skip("Theming is inactive.\n");
1040 goto done;
1043 hdc = GetDC(hwnd);
1044 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size);
1045 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1046 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
1047 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1048 ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n",
1049 size.cx, size.cy, size2.cx, size2.cy);
1050 ReleaseDC(hwnd, hdc);
1052 /* Test that theme part size doesn't change even if DPI is changed */
1053 if (!set_primary_monitor_effective_dpi(96))
1055 skip("Failed to set primary monitor dpi to 96.\n");
1056 goto done;
1059 hdc = GetDC(hwnd);
1060 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
1061 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1062 ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx,
1063 size.cy, size2.cx, size2.cy);
1064 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
1065 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1066 ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx,
1067 size.cy, size2.cx, size2.cy);
1069 /* Test that theme part size changes after DPI is changed and theme handle is reopened.
1070 * If DPI awareness context is not DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, theme part size
1071 * still doesn't change, even after the theme handle is reopened. */
1072 CloseThemeData(htheme);
1073 htheme = OpenThemeData(hwnd, WC_BUTTONW);
1075 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
1076 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1077 ok(size2.cx != size.cx || size2.cy != size.cy, "Expected size not equal to %dx%d.\n", size.cx,
1078 size.cy);
1079 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
1080 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1081 ok(size2.cx != size.cx || size2.cy != size.cy, "Expected size not equal to %dx%d.\n", size.cx,
1082 size.cy);
1083 ReleaseDC(hwnd, hdc);
1085 /* Test that OpenThemeData() without a window will assume the DPI is 96 */
1086 if (!set_primary_monitor_effective_dpi(192))
1088 skip("Failed to set primary monitor dpi to 192.\n");
1089 goto done;
1092 CloseThemeData(htheme);
1093 htheme = OpenThemeData(NULL, WC_BUTTONW);
1094 size = size2;
1096 hdc = GetDC(hwnd);
1097 hr = GetThemePartSize(htheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
1098 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1099 ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx,
1100 size.cy, size2.cx, size2.cy);
1101 hr = GetThemePartSize(htheme, NULL, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_DRAW, &size2);
1102 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1103 ok(size2.cx == size.cx && size2.cy == size.cy, "Expected size %dx%d, got %dx%d.\n", size.cx,
1104 size.cy, size2.cx, size2.cy);
1105 ReleaseDC(hwnd, hdc);
1107 done:
1108 if (hwnd)
1109 DestroyWindow(hwnd);
1110 if (htheme)
1111 CloseThemeData(htheme);
1112 if (get_primary_monitor_effective_dpi() != old_dpi)
1113 set_primary_monitor_effective_dpi(old_dpi);
1114 if (key)
1116 RegDeleteValueA(key, "IgnorePerProcessSystemDPIToast");
1117 RegCloseKey(key);
1119 pSetThreadDpiAwarenessContext(old_context);
1122 static void test_EnableTheming(void)
1124 WCHAR old_color_string[13], new_color_string[13], color_string[13];
1125 BOOL old_gradient_caption, new_gradient_caption, gradient_caption;
1126 BOOL old_flat_menu, new_flat_menu, flat_menu;
1127 LOGFONTW old_logfont, new_logfont, logfont;
1128 NONCLIENTMETRICSW old_ncm, new_ncm, ncm;
1129 DPI_AWARENESS_CONTEXT old_context = 0;
1130 COLORREF old_color, new_color;
1131 BOOL is_theme_active, ret;
1132 DWORD size, length;
1133 HRESULT hr;
1134 LSTATUS ls;
1135 HKEY hkey;
1137 if (IsThemeActive())
1139 hr = EnableTheming(TRUE);
1140 ok(hr == S_OK, "EnableTheming failed, hr %#x.\n", hr);
1141 ok(IsThemeActive(), "Expected theming active.\n");
1143 /* Only run in interactive mode because once theming is disabled, it can't be turned back on
1144 * via EnableTheming() */
1145 if (winetest_interactive)
1147 if (pSetThreadDpiAwarenessContext)
1149 old_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
1151 else if (get_primary_monitor_effective_dpi() != 96)
1153 skip("DPI isn't 96, skipping.\n");
1154 return;
1157 /* Read current system metrics */
1158 old_color = GetSysColor(COLOR_SCROLLBAR);
1159 swprintf(old_color_string, ARRAY_SIZE(old_color_string), L"%d %d %d",
1160 GetRValue(old_color), GetGValue(old_color), GetBValue(old_color));
1162 memset(&old_ncm, 0, sizeof(old_ncm));
1163 old_ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth);
1164 ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(old_ncm), &old_ncm, 0);
1165 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1167 memset(&old_logfont, 0, sizeof(old_logfont));
1168 ret = SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(old_logfont), &old_logfont, 0);
1169 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1170 ret = SystemParametersInfoW(SPI_GETFLATMENU, 0, &old_flat_menu, 0);
1171 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1172 ret = SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &old_gradient_caption, 0);
1173 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1175 /* Write new system metrics to the registry */
1176 new_color = ~old_color;
1177 new_flat_menu = !old_flat_menu;
1178 new_gradient_caption = !old_gradient_caption;
1179 memcpy(&new_ncm, &old_ncm, sizeof(new_ncm));
1180 new_ncm.iScrollWidth += 5;
1181 memcpy(&new_logfont, &old_logfont, sizeof(new_logfont));
1182 new_logfont.lfWidth += 5;
1184 ls = RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\Colors", 0, KEY_ALL_ACCESS,
1185 &hkey);
1186 ok(!ls, "RegOpenKeyExW failed, ls %#x.\n", ls);
1188 length = swprintf(new_color_string, ARRAY_SIZE(new_color_string), L"%d %d %d",
1189 GetRValue(new_color), GetGValue(new_color), GetBValue(new_color));
1190 ls = RegSetValueExW(hkey, L"Scrollbar", 0, REG_SZ, (BYTE *)new_color_string,
1191 (length + 1) * sizeof(WCHAR));
1192 ok(!ls, "RegSetValueExW failed, ls %#x.\n", ls);
1194 ret = SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(new_ncm), &new_ncm,
1195 SPIF_UPDATEINIFILE);
1196 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1197 ret = SystemParametersInfoW(SPI_SETICONTITLELOGFONT, sizeof(new_logfont), &new_logfont,
1198 SPIF_UPDATEINIFILE);
1199 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1200 ret = SystemParametersInfoW(SPI_SETFLATMENU, 0, (void *)(INT_PTR)new_flat_menu,
1201 SPIF_UPDATEINIFILE);
1202 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1203 ret = SystemParametersInfoW(SPI_SETGRADIENTCAPTIONS, 0,
1204 (void *)(INT_PTR)new_gradient_caption, SPIF_UPDATEINIFILE);
1205 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1207 /* Change theming state */
1208 hr = EnableTheming(FALSE);
1209 ok(hr == S_OK, "EnableTheming failed, hr %#x.\n", hr);
1210 is_theme_active = IsThemeActive();
1211 ok(!is_theme_active || broken(is_theme_active), /* Win8+ can no longer disable theming */
1212 "Expected theming inactive.\n");
1214 /* Test that system metrics are unchanged */
1215 size = sizeof(color_string);
1216 ls = RegQueryValueExW(hkey, L"Scrollbar", NULL, NULL, (BYTE *)color_string, &size);
1217 ok(!ls, "RegQueryValueExW failed, ls %#x.\n", ls);
1218 ok(!lstrcmpW(color_string, new_color_string), "Expected %s, got %s.\n",
1219 wine_dbgstr_w(new_color_string), wine_dbgstr_w(color_string));
1221 ret = SystemParametersInfoW(SPI_GETFLATMENU, 0, &flat_menu, 0);
1222 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1223 ok(flat_menu == new_flat_menu, "Expected %d, got %d.\n", new_flat_menu, flat_menu);
1224 ret = SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &gradient_caption, 0);
1225 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1226 ok(gradient_caption == new_gradient_caption, "Expected %d, got %d.\n",
1227 new_gradient_caption, gradient_caption);
1229 memset(&ncm, 0, sizeof(ncm));
1230 ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSW, iPaddedBorderWidth);
1231 ret = SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
1232 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1233 ok(!memcmp(&ncm, &new_ncm, sizeof(ncm)), "Expected non-client metrics unchanged.\n");
1235 memset(&logfont, 0, sizeof(logfont));
1236 ret = SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(logfont), &logfont, 0);
1237 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1238 ok(!memcmp(&logfont, &new_logfont, sizeof(logfont)),
1239 "Expected icon title font unchanged.\n");
1241 /* Test that theming cannot be turned on via EnableTheming() */
1242 hr = EnableTheming(TRUE);
1243 ok(hr == S_OK, "EnableTheming failed, hr %#x.\n", hr);
1244 is_theme_active = IsThemeActive();
1245 ok(!is_theme_active || broken(is_theme_active), /* Win8+ can no longer disable theming */
1246 "Expected theming inactive.\n");
1248 /* Restore system metrics */
1249 ls = RegSetValueExW(hkey, L"Scrollbar", 0, REG_SZ, (BYTE *)old_color_string,
1250 (lstrlenW(old_color_string) + 1) * sizeof(WCHAR));
1251 ok(!ls, "RegSetValueExW failed, ls %#x.\n", ls);
1252 ret = SystemParametersInfoW(SPI_SETFLATMENU, 0, (void *)(INT_PTR)old_flat_menu,
1253 SPIF_UPDATEINIFILE);
1254 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1255 ret = SystemParametersInfoW(SPI_SETGRADIENTCAPTIONS, 0,
1256 (void *)(INT_PTR)old_gradient_caption, SPIF_UPDATEINIFILE);
1257 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1258 ret = SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, sizeof(old_ncm), &old_ncm,
1259 SPIF_UPDATEINIFILE);
1260 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1261 ret = SystemParametersInfoW(SPI_SETICONTITLELOGFONT, sizeof(old_logfont), &old_logfont,
1262 SPIF_UPDATEINIFILE);
1263 ok(ret, "SystemParametersInfoW failed, error %u.\n", GetLastError());
1265 RegCloseKey(hkey);
1266 if (pSetThreadDpiAwarenessContext)
1267 pSetThreadDpiAwarenessContext(old_context);
1270 else
1272 hr = EnableTheming(FALSE);
1273 ok(hr == S_OK, "EnableTheming failed, hr %#x.\n", hr);
1274 ok(!IsThemeActive(), "Expected theming inactive.\n");
1276 hr = EnableTheming(TRUE);
1277 ok(hr == S_OK, "EnableTheming failed, hr %#x.\n", hr);
1278 ok(!IsThemeActive(), "Expected theming inactive.\n");
1280 hr = EnableTheming(FALSE);
1281 ok(hr == S_OK, "EnableTheming failed, hr %#x.\n", hr);
1282 ok(!IsThemeActive(), "Expected theming inactive.\n");
1286 static void test_GetThemeIntList(void)
1288 INTLIST intlist;
1289 HTHEME theme;
1290 HRESULT hr;
1291 HWND hwnd;
1293 if (!pGetThemeIntList)
1295 win_skip("GetThemeIntList is unavailable.\n");
1296 return;
1299 hwnd = CreateWindowA("static", "", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
1300 theme = OpenThemeData(hwnd, L"Button");
1301 if (!theme)
1303 skip("Theming is not active.\n");
1304 DestroyWindow(hwnd);
1305 return;
1308 /* Check properties */
1309 /* TMT_TRANSITIONDURATIONS is a vista+ property */
1310 hr = pGetThemeIntList(theme, BP_PUSHBUTTON, PBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
1311 if (LOBYTE(LOWORD(GetVersion())) < 6)
1312 ok(hr == E_PROP_ID_UNSUPPORTED, "Expected %#x, got %#x.\n", E_PROP_ID_UNSUPPORTED, hr);
1313 else
1314 ok(hr == S_OK, "GetThemeIntList failed, hr %#x.\n", hr);
1316 CloseThemeData(theme);
1317 DestroyWindow(hwnd);
1320 static void test_GetThemeTransitionDuration(void)
1322 int from_state, to_state, expected;
1323 INTLIST intlist;
1324 DWORD duration;
1325 HTHEME theme;
1326 HRESULT hr;
1327 HWND hwnd;
1329 if (!pGetThemeTransitionDuration || !pGetThemeIntList)
1331 win_skip("GetThemeTransitionDuration or GetThemeIntList is unavailable.\n");
1332 return;
1335 hwnd = CreateWindowA("static", "", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL);
1336 theme = OpenThemeData(hwnd, L"Button");
1337 if (!theme)
1339 skip("Theming is not active.\n");
1340 DestroyWindow(hwnd);
1341 return;
1344 /* Invalid parameter tests */
1345 duration = 0xdeadbeef;
1346 hr = pGetThemeTransitionDuration(NULL, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1347 TMT_TRANSITIONDURATIONS, &duration);
1348 ok(hr == E_HANDLE, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1349 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#x.\n", 0xdeadbeef, duration);
1351 /* Crash on Wine. HTHEME is not a pointer that can be directly referenced. */
1352 if (strcmp(winetest_platform, "wine"))
1354 duration = 0xdeadbeef;
1355 hr = pGetThemeTransitionDuration((HTHEME)0xdeadbeef, BP_PUSHBUTTON, PBS_NORMAL,
1356 PBS_DEFAULTED_ANIMATING, TMT_TRANSITIONDURATIONS, &duration);
1357 todo_wine
1358 ok(hr == E_HANDLE, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1359 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#x.\n", 0xdeadbeef, duration);
1362 duration = 0xdeadbeef;
1363 hr = pGetThemeTransitionDuration(theme, 0xdeadbeef, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1364 TMT_TRANSITIONDURATIONS, &duration);
1365 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1366 ok(duration == 0, "Expected duration %#x, got %#x.\n", 0, duration);
1368 duration = 0xdeadbeef;
1369 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL - 1, PBS_DEFAULTED_ANIMATING,
1370 TMT_TRANSITIONDURATIONS, &duration);
1371 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1372 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#x.\n", 0xdeadbeef, duration);
1374 duration = 0xdeadbeef;
1375 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_DEFAULTED_ANIMATING + 1,
1376 PBS_DEFAULTED_ANIMATING, TMT_TRANSITIONDURATIONS, &duration);
1377 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1378 ok(duration == 0, "Expected duration %#x, got %#x.\n", 0, duration);
1380 duration = 0xdeadbeef;
1381 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_NORMAL - 1,
1382 TMT_TRANSITIONDURATIONS, &duration);
1383 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1384 ok(duration == 0xdeadbeef, "Expected duration %#x, got %#x.\n", 0xdeadbeef, duration);
1386 duration = 0xdeadbeef;
1387 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING + 1,
1388 TMT_TRANSITIONDURATIONS, &duration);
1389 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1390 ok(duration == 0, "Expected duration %#x, got %#x.\n", 0, duration);
1392 duration = 0xdeadbeef;
1393 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1394 TMT_BACKGROUND, &duration);
1395 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1396 ok(duration == 0, "Expected duration %#x, got %#x.\n", 0, duration);
1398 duration = 0xdeadbeef;
1399 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1400 0xdeadbeef, &duration);
1401 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1402 ok(duration == 0, "Expected duration %#x, got %#x.\n", 0, duration);
1404 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, PBS_NORMAL, PBS_DEFAULTED_ANIMATING,
1405 TMT_TRANSITIONDURATIONS, NULL);
1406 ok(hr == E_INVALIDARG, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1408 /* Parts that don't have TMT_TRANSITIONDURATIONS */
1409 hr = GetThemeIntList(theme, BP_GROUPBOX, GBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
1410 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeIntList failed, hr %#x.\n", hr);
1412 duration = 0xdeadbeef;
1413 hr = pGetThemeTransitionDuration(theme, BP_GROUPBOX, GBS_NORMAL, GBS_DISABLED,
1414 TMT_TRANSITIONDURATIONS, &duration);
1415 ok(hr == E_PROP_ID_UNSUPPORTED, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1416 ok(duration == 0, "Expected duration %#x, got %#x.\n", 0, duration);
1418 /* Test parsing TMT_TRANSITIONDURATIONS property. TMT_TRANSITIONDURATIONS is a vista+ property */
1419 if (LOBYTE(LOWORD(GetVersion())) < 6)
1420 goto done;
1422 hr = pGetThemeIntList(theme, BP_PUSHBUTTON, PBS_NORMAL, TMT_TRANSITIONDURATIONS, &intlist);
1423 ok(hr == S_OK, "GetThemeIntList failed, hr %#x.\n", hr);
1424 /* The first value is the theme part state count. The following are the values from every state
1425 * to every state. So the total value count should be 1 + state ^ 2 */
1426 expected = PBS_DEFAULTED_ANIMATING - PBS_NORMAL + 1;
1427 ok(intlist.iValues[0] == expected, "Expected the first value %d, got %d.\n", expected,
1428 intlist.iValues[0]);
1429 expected = 1 + intlist.iValues[0] * intlist.iValues[0];
1430 ok(intlist.iValueCount == expected, "Expected value count %d, got %d.\n", expected,
1431 intlist.iValueCount);
1432 if (hr == S_OK)
1434 for (from_state = PBS_NORMAL; from_state <= PBS_DEFAULTED_ANIMATING; ++from_state)
1436 for (to_state = PBS_NORMAL; to_state <= PBS_DEFAULTED_ANIMATING; ++to_state)
1438 winetest_push_context("from state %d to %d", from_state, to_state);
1440 duration = 0xdeadbeef;
1441 hr = pGetThemeTransitionDuration(theme, BP_PUSHBUTTON, from_state, to_state,
1442 TMT_TRANSITIONDURATIONS, &duration);
1443 ok(hr == S_OK, "GetThemeTransitionDuration failed, hr %#x.\n", hr);
1444 expected = intlist.iValues[1 + intlist.iValues[0] * (from_state - 1) + (to_state - 1)];
1445 ok(duration == expected, "Expected duration %d, got %d.\n", expected, duration);
1447 winetest_pop_context();
1452 done:
1453 CloseThemeData(theme);
1454 DestroyWindow(hwnd);
1457 static const struct message DrawThemeParentBackground_seq[] =
1459 {WM_ERASEBKGND, sent},
1460 {WM_PRINTCLIENT, sent | lparam, 0, PRF_CLIENT},
1464 static LRESULT WINAPI test_DrawThemeParentBackground_proc(HWND hwnd, UINT message, WPARAM wp,
1465 LPARAM lp)
1467 static LONG defwndproc_counter;
1468 struct message msg = {0};
1469 LRESULT lr;
1470 POINT org;
1471 BOOL ret;
1473 switch (message)
1475 /* Test that DrawThemeParentBackground() doesn't change brush origins */
1476 case WM_ERASEBKGND:
1477 case WM_PRINTCLIENT:
1478 ret = GetBrushOrgEx((HDC)wp, &org);
1479 ok(ret, "GetBrushOrgEx failed, error %d.\n", GetLastError());
1480 ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
1481 break;
1483 default:
1484 break;
1487 msg.message = message;
1488 msg.flags = sent | wparam | lparam;
1489 if (defwndproc_counter)
1490 msg.flags |= defwinproc;
1491 msg.wParam = wp;
1492 msg.lParam = lp;
1493 add_message(sequences, PARENT_SEQ_INDEX, &msg);
1495 InterlockedIncrement(&defwndproc_counter);
1496 lr = DefWindowProcA(hwnd, message, wp, lp);
1497 InterlockedDecrement(&defwndproc_counter);
1498 return lr;
1501 static void test_DrawThemeParentBackground(void)
1503 HWND child, parent;
1504 WNDCLASSA cls;
1505 HRESULT hr;
1506 POINT org;
1507 RECT rect;
1508 BOOL ret;
1509 HDC hdc;
1511 memset(&cls, 0, sizeof(cls));
1512 cls.hInstance = GetModuleHandleA(0);
1513 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
1514 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
1515 cls.lpfnWndProc = test_DrawThemeParentBackground_proc;
1516 cls.lpszClassName = "TestDrawThemeParentBackgroundClass";
1517 RegisterClassA(&cls);
1519 parent = CreateWindowA("TestDrawThemeParentBackgroundClass", "parent", WS_POPUP | WS_VISIBLE, 0,
1520 0, 100, 100, 0, 0, 0, 0);
1521 ok(parent != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
1522 child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, parent, 0, 0,
1523 NULL);
1524 ok(child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
1525 flush_events();
1526 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1528 hdc = GetDC(child);
1529 ret = GetBrushOrgEx(hdc, &org);
1530 ok(ret, "GetBrushOrgEx failed, error %d.\n", GetLastError());
1531 ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
1533 hr = DrawThemeParentBackground(child, hdc, NULL);
1534 ok(SUCCEEDED(hr), "DrawThemeParentBackground failed, hr %#x.\n", hr);
1535 ok_sequence(sequences, PARENT_SEQ_INDEX, DrawThemeParentBackground_seq,
1536 "DrawThemeParentBackground parent", FALSE);
1537 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1539 GetClientRect(child, &rect);
1540 hr = DrawThemeParentBackground(child, hdc, &rect);
1541 ok(SUCCEEDED(hr), "DrawThemeParentBackground failed, hr %#x.\n", hr);
1542 ok_sequence(sequences, PARENT_SEQ_INDEX, DrawThemeParentBackground_seq,
1543 "DrawThemeParentBackground parent", FALSE);
1544 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1546 ReleaseDC(child, hdc);
1547 DestroyWindow(parent);
1548 UnregisterClassA("TestDrawThemeParentBackgroundClass", GetModuleHandleA(0));
1551 struct test_EnableThemeDialogTexture_param
1553 const CHAR *class_name;
1554 DWORD style;
1557 static const struct message empty_seq[] =
1562 static HWND dialog_child;
1563 static DWORD dialog_init_flag;
1564 static BOOL handle_WM_CTLCOLORSTATIC;
1566 static INT_PTR CALLBACK test_EnableThemeDialogTexture_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
1568 struct test_EnableThemeDialogTexture_param *param;
1569 struct message message = {0};
1571 message.message = msg;
1572 message.flags = sent | wparam | lparam;
1573 message.wParam = wp;
1574 message.lParam = lp;
1575 add_message(sequences, PARENT_SEQ_INDEX, &message);
1577 switch (msg)
1579 case WM_INITDIALOG:
1580 param = (struct test_EnableThemeDialogTexture_param *)lp;
1581 dialog_child = CreateWindowA(param->class_name, "child",
1582 param->style | WS_CHILD | WS_VISIBLE, 1, 2, 50, 50, hwnd,
1583 (HMENU)100, 0, NULL);
1584 ok(dialog_child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
1585 if (dialog_init_flag)
1586 EnableThemeDialogTexture(hwnd, dialog_init_flag);
1587 return TRUE;
1589 case WM_CTLCOLORSTATIC:
1590 return (INT_PTR)(handle_WM_CTLCOLORSTATIC ? GetSysColorBrush(COLOR_MENU) : 0);
1592 case WM_CLOSE:
1593 DestroyWindow(dialog_child);
1594 dialog_child = NULL;
1595 return TRUE;
1597 default:
1598 return FALSE;
1602 static void test_EnableThemeDialogTexture(void)
1604 struct test_EnableThemeDialogTexture_param param;
1605 HWND dialog, child, hwnd, hwnd2;
1606 int mode, old_mode, count, i, j;
1607 COLORREF color, old_color;
1608 HBRUSH brush, brush2;
1609 HDC child_hdc, hdc;
1610 LOGBRUSH log_brush;
1611 ULONG_PTR proc;
1612 WNDCLASSA cls;
1613 HTHEME theme;
1614 DWORD error;
1615 BITMAP bmp;
1616 HRESULT hr;
1617 LRESULT lr;
1618 POINT org;
1619 SIZE size;
1620 BOOL ret;
1622 struct
1624 DLGTEMPLATE template;
1625 WORD menu;
1626 WORD class;
1627 WORD title;
1628 } temp = {{0}};
1630 static const DWORD flags[] =
1632 ETDT_DISABLE,
1633 ETDT_ENABLE,
1634 ETDT_USETABTEXTURE,
1635 ETDT_USEAEROWIZARDTABTEXTURE,
1636 ETDT_ENABLETAB,
1637 ETDT_ENABLEAEROWIZARDTAB,
1638 /* Bad flags */
1640 ETDT_DISABLE | ETDT_ENABLE,
1641 ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB
1644 static const struct invalid_flag_test
1646 DWORD flag;
1647 BOOL enabled;
1649 invalid_flag_tests[] =
1651 {0, FALSE},
1652 {ETDT_DISABLE | ETDT_ENABLE, FALSE},
1653 {ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB, TRUE},
1654 {ETDT_USETABTEXTURE | ETDT_USEAEROWIZARDTABTEXTURE, TRUE},
1655 {ETDT_VALIDBITS, FALSE},
1656 {~ETDT_VALIDBITS, FALSE},
1657 {~ETDT_VALIDBITS | ETDT_DISABLE, FALSE}
1660 static const struct class_test
1662 struct test_EnableThemeDialogTexture_param param;
1663 BOOL texture_enabled;
1665 class_tests[] =
1667 {{ANIMATE_CLASSA}},
1668 {{WC_BUTTONA, BS_PUSHBUTTON}, TRUE},
1669 {{WC_BUTTONA, BS_DEFPUSHBUTTON}, TRUE},
1670 {{WC_BUTTONA, BS_CHECKBOX}, TRUE},
1671 {{WC_BUTTONA, BS_AUTOCHECKBOX}, TRUE},
1672 {{WC_BUTTONA, BS_RADIOBUTTON}, TRUE},
1673 {{WC_BUTTONA, BS_3STATE}, TRUE},
1674 {{WC_BUTTONA, BS_AUTO3STATE}, TRUE},
1675 {{WC_BUTTONA, BS_GROUPBOX}, TRUE},
1676 {{WC_BUTTONA, BS_USERBUTTON}, TRUE},
1677 {{WC_BUTTONA, BS_AUTORADIOBUTTON}, TRUE},
1678 {{WC_BUTTONA, BS_PUSHBOX}, TRUE},
1679 {{WC_BUTTONA, BS_OWNERDRAW}, TRUE},
1680 {{WC_BUTTONA, BS_SPLITBUTTON}, TRUE},
1681 {{WC_BUTTONA, BS_DEFSPLITBUTTON}, TRUE},
1682 {{WC_BUTTONA, BS_COMMANDLINK}, TRUE},
1683 {{WC_BUTTONA, BS_DEFCOMMANDLINK}, TRUE},
1684 {{WC_COMBOBOXA}},
1685 {{WC_COMBOBOXEXA}},
1686 {{DATETIMEPICK_CLASSA}},
1687 {{WC_EDITA}},
1688 {{WC_HEADERA}},
1689 {{HOTKEY_CLASSA}},
1690 {{WC_IPADDRESSA}},
1691 {{WC_LISTBOXA}},
1692 {{WC_LISTVIEWA}},
1693 {{MONTHCAL_CLASSA}},
1694 {{WC_NATIVEFONTCTLA}},
1695 {{WC_PAGESCROLLERA}},
1696 {{PROGRESS_CLASSA}},
1697 {{REBARCLASSNAMEA}},
1698 {{WC_STATICA, SS_LEFT}, TRUE},
1699 {{WC_STATICA, SS_ICON}, TRUE},
1700 {{WC_STATICA, SS_BLACKRECT}, TRUE},
1701 {{WC_STATICA, SS_OWNERDRAW}, TRUE},
1702 {{WC_STATICA, SS_BITMAP}, TRUE},
1703 {{WC_STATICA, SS_ENHMETAFILE}, TRUE},
1704 {{WC_STATICA, SS_ETCHEDHORZ}, TRUE},
1705 {{STATUSCLASSNAMEA}},
1706 {{"SysLink"}},
1707 {{WC_TABCONTROLA}},
1708 {{TOOLBARCLASSNAMEA}},
1709 {{TOOLTIPS_CLASSA}},
1710 {{TRACKBAR_CLASSA}},
1711 {{WC_TREEVIEWA}},
1712 {{UPDOWN_CLASSA}},
1713 {{WC_SCROLLBARA}},
1716 if (!IsThemeActive())
1718 skip("Theming is inactive.\n");
1719 return;
1722 memset(&cls, 0, sizeof(cls));
1723 cls.lpfnWndProc = DefWindowProcA;
1724 cls.hInstance = GetModuleHandleA(NULL);
1725 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
1726 cls.hbrBackground = GetStockObject(GRAY_BRUSH);
1727 cls.lpszClassName = "TestEnableThemeDialogTextureClass";
1728 RegisterClassA(&cls);
1730 temp.template.style = WS_CHILD | WS_VISIBLE;
1731 temp.template.cx = 100;
1732 temp.template.cy = 100;
1733 param.class_name = cls.lpszClassName;
1734 param.style = 0;
1735 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
1736 test_EnableThemeDialogTexture_proc, (LPARAM)&param);
1737 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
1738 child = GetDlgItem(dialog, 100);
1739 ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
1740 child_hdc = GetDC(child);
1742 /* Test that dialog procedure is unchanged */
1743 proc = GetWindowLongPtrA(dialog, DWLP_DLGPROC);
1744 ok(proc == (ULONG_PTR)test_EnableThemeDialogTexture_proc, "Unexpected proc %#lx.\n", proc);
1746 /* Test dialog texture is disabled by default. EnableThemeDialogTexture() needs to be called */
1747 ret = IsThemeDialogTextureEnabled(dialog);
1748 ok(!ret, "Expected theme dialog texture disabled.\n");
1749 ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
1751 /* Test ETDT_ENABLE | ETDT_USETABTEXTURE doesn't take effect immediately */
1752 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
1753 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1754 ret = IsThemeDialogTextureEnabled(dialog);
1755 ok(ret, "Expected theme dialog texture enabled.\n");
1757 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1758 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1759 GetSysColorBrush(COLOR_BTNFACE), brush);
1760 ret = GetBrushOrgEx(child_hdc, &org);
1761 ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
1762 ok(org.x == 0 && org.y == 0, "Expected (0,0), got %s.\n", wine_dbgstr_point(&org));
1764 /* Test WM_THEMECHANGED doesn't make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
1765 lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
1766 ok(lr == 0, "WM_THEMECHANGED failed.\n");
1767 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1768 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1769 GetSysColorBrush(COLOR_BTNFACE), brush);
1771 /* Test WM_ERASEBKGND make ETDT_ENABLE | ETDT_USETABTEXTURE take effect */
1772 lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
1773 ok(lr != 0, "WM_ERASEBKGND failed.\n");
1774 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1775 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected brush changed.\n");
1777 /* Test disabling theme dialog texture should change the brush immediately */
1778 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1779 hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
1780 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1781 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1782 ok(brush2 != brush, "Expected a different brush.\n");
1783 ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1784 GetSysColorBrush(COLOR_BTNFACE), brush2);
1786 /* Test re-enabling theme dialog texture with ETDT_ENABLE doesn't change the brush */
1787 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1788 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE);
1789 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1790 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1791 ok(brush2 == brush, "Expected the same brush.\n");
1792 ok(brush2 == GetSysColorBrush(COLOR_BTNFACE), "Expected brush %p, got %p.\n",
1793 GetSysColorBrush(COLOR_BTNFACE), brush2);
1795 /* Test adding ETDT_USETABTEXTURE should change the brush immediately */
1796 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1797 hr = EnableThemeDialogTexture(dialog, ETDT_USETABTEXTURE);
1798 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1799 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1800 ok(brush2 != brush, "Expected a different brush.\n");
1802 /* Test ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE should change the brush immediately */
1803 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1804 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USEAEROWIZARDTABTEXTURE);
1805 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1806 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1807 /* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
1808 if (LOBYTE(LOWORD(GetVersion())) < 6)
1809 ok(brush2 == brush, "Expected the same brush.\n");
1810 else
1811 ok(brush2 != brush, "Expected a different brush.\n");
1813 hr = EnableThemeDialogTexture(dialog, ETDT_DISABLE);
1814 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1815 hr = EnableThemeDialogTexture(dialog, ETDT_ENABLE | ETDT_USETABTEXTURE);
1816 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1818 /* Test that the dialog procedure should take precedence over DefDlgProc() for WM_CTLCOLORSTATIC */
1819 handle_WM_CTLCOLORSTATIC = TRUE;
1820 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1821 ok(brush == GetSysColorBrush(COLOR_MENU), "Expected brush %p, got %p.\n",
1822 GetSysColorBrush(COLOR_MENU), brush);
1823 handle_WM_CTLCOLORSTATIC = FALSE;
1825 /* Test that WM_CTLCOLORSTATIC changes brush origin when dialog texture is on */
1826 ret = SetBrushOrgEx(child_hdc, 0, 0, NULL);
1827 ok(ret, "SetBrushOrgEx failed, error %u.\n", GetLastError());
1828 SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1829 ret = GetBrushOrgEx(child_hdc, &org);
1830 ok(ret, "GetBrushOrgEx failed, error %u.\n", GetLastError());
1831 ok(org.x == -1 && org.y == -2, "Expected (-1,-2), got %s.\n", wine_dbgstr_point(&org));
1833 /* Test that WM_CTLCOLORSTATIC changes background mode when dialog texture is on */
1834 old_mode = SetBkMode(child_hdc, OPAQUE);
1835 ok(old_mode != 0, "SetBkMode failed.\n");
1836 SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1837 mode = SetBkMode(child_hdc, old_mode);
1838 ok(mode == TRANSPARENT, "Expected mode %#x, got %#x.\n", TRANSPARENT, mode);
1840 /* Test that WM_CTLCOLORSTATIC changes background color when dialog texture is on */
1841 old_color = SetBkColor(child_hdc, 0xaa5511);
1842 ok(old_color != CLR_INVALID, "SetBkColor failed.\n");
1843 SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1844 color = SetBkColor(child_hdc, old_color);
1845 ok(color == GetSysColor(COLOR_BTNFACE), "Expected background color %#x, got %#x.\n",
1846 GetSysColor(COLOR_BTNFACE), color);
1848 /* Test that dialog doesn't have theme handle opened for itself */
1849 ok(GetWindowTheme(dialog) == NULL, "Expected NULL theme handle.\n");
1851 /* Test that the returned brush is a pattern brush created from the tab body */
1852 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1853 memset(&log_brush, 0, sizeof(log_brush));
1854 count = GetObjectA(brush, sizeof(log_brush), &log_brush);
1855 ok(count == sizeof(log_brush), "GetObjectA failed, error %u.\n", GetLastError());
1856 ok(log_brush.lbColor == 0, "Expected brush color %#x, got %#x.\n", 0, log_brush.lbColor);
1857 ok(log_brush.lbStyle == BS_PATTERN, "Expected brush style %#x, got %#x.\n", BS_PATTERN,
1858 log_brush.lbStyle);
1860 memset(&bmp, 0, sizeof(bmp));
1861 count = GetObjectA((HBITMAP)log_brush.lbHatch, sizeof(bmp), &bmp);
1862 ok(count == sizeof(bmp), "GetObjectA failed, error %u.\n", GetLastError());
1864 theme = OpenThemeData(NULL, L"Tab");
1865 ok(theme != NULL, "OpenThemeData failed.\n");
1867 size.cx = 0;
1868 size.cy = 0;
1869 hr = GetThemePartSize(theme, NULL, TABP_BODY, 0, NULL, TS_TRUE, &size);
1870 ok(hr == S_OK, "GetThemePartSize failed, hr %#x.\n", hr);
1871 ok(bmp.bmWidth == size.cx, "Expected width %d, got %d.\n", size.cx, bmp.bmWidth);
1872 ok(bmp.bmHeight == size.cy, "Expected height %d, got %d.\n", size.cy, bmp.bmHeight);
1874 CloseThemeData(theme);
1876 /* Test that DefDlgProcA/W() are hooked for WM_CTLCOLORSTATIC */
1877 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1878 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
1879 brush2 = (HBRUSH)DefDlgProcW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1880 ok(brush2 == brush, "Expected the same brush.\n");
1881 brush2 = (HBRUSH)DefDlgProcA(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1882 ok(brush2 == brush, "Expected the same brush.\n");
1884 /* Test that DefWindowProcA/W() are also hooked for WM_CTLCOLORSTATIC */
1885 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1886 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
1887 brush2 = (HBRUSH)DefWindowProcW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1888 todo_wine
1889 ok(brush2 == brush, "Expected the same brush.\n");
1890 brush2 = (HBRUSH)DefWindowProcA(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1891 todo_wine
1892 ok(brush2 == brush, "Expected the same brush.\n");
1894 /* Test that DefWindowProcA/W() are not hooked for WM_ERASEBKGND. So the background is still
1895 * drawn with hbrBackground, which in this case, is GRAY_BRUSH.
1897 * This test means it could be that both DefWindowProc() and DefDlgProc() are hooked for
1898 * WM_CTLCOLORSTATIC and only DefDlgProc() is hooked for WM_ERASEBKGND. Or it could mean
1899 * DefWindowProc() is hooked for WM_CTLCOLORSTATIC and DefDlgProc() is hooked for WM_ERASEBKGND.
1900 * Considering the dialog theming needs a WM_ERASEBKGND to activate, it would be weird for let
1901 * only DefWindowProc() to hook WM_CTLCOLORSTATIC. For example, what's the point of hooking
1902 * WM_CTLCOLORSTATIC in DefWindowProc() for a feature that can only be activated in
1903 * DefDlgProc()? So I tend to believe both DefWindowProc() and DefDlgProc() are hooked for
1904 * WM_CTLCOLORSTATIC */
1905 hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, 0, 0,
1906 0, NULL);
1907 ok(hwnd != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
1908 hwnd2 = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 10, 10, 20, 20, hwnd, NULL, 0,
1909 NULL);
1910 hr = EnableThemeDialogTexture(hwnd, ETDT_ENABLETAB);
1911 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1912 ret = IsThemeDialogTextureEnabled(hwnd);
1913 ok(ret, "Wrong dialog texture status.\n");
1914 flush_events();
1916 hdc = GetDC(hwnd);
1917 color = GetPixel(hdc, 0, 0);
1918 ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
1919 "Expected color %#x, got %#x.\n", 0x808080, color);
1920 color = GetPixel(hdc, 50, 50);
1921 ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
1922 "Expected color %#x, got %#x.\n", 0x808080, color);
1923 color = GetPixel(hdc, 99, 99);
1924 ok(color == 0x808080 || broken(color == 0xffffffff), /* Win 7 may report 0xffffffff */
1925 "Expected color %#x, got %#x.\n", 0x808080, color);
1926 ReleaseDC(hwnd, hdc);
1928 /* Test EnableThemeDialogTexture() doesn't work for non-dialog windows */
1929 hdc = GetDC(hwnd2);
1930 brush = (HBRUSH)SendMessageW(hwnd, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd2);
1931 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected a different brush.\n");
1932 ReleaseDC(hwnd2, hdc);
1934 DestroyWindow(hwnd);
1936 /* Test that the brush is not a system object and has only one reference and shouldn't be freed */
1937 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1938 ret = DeleteObject(brush);
1939 ok(ret, "DeleteObject failed, error %u.\n", GetLastError());
1940 SetLastError(0xdeadbeef);
1941 ret = GetObjectA(brush, sizeof(log_brush), &log_brush);
1942 error = GetLastError();
1943 ok(!ret || broken(ret) /* XP */, "GetObjectA succeeded.\n");
1944 todo_wine
1945 ok(error == ERROR_INVALID_PARAMETER || broken(error == 0xdeadbeef) /* XP */,
1946 "Expected error %u, got %u.\n", ERROR_INVALID_PARAMETER, error);
1947 ret = DeleteObject(brush);
1948 ok(!ret || broken(ret) /* XP */, "DeleteObject succeeded.\n");
1950 /* Should still report the same brush handle after the brush handle was freed */
1951 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1952 ok(brush2 == brush, "Expected the same brush.\n");
1954 /* Test WM_THEMECHANGED can update the brush now that ETDT_ENABLE | ETDT_USETABTEXTURE is in
1955 * effect. This test needs to be ran last as it affect other tests for the same dialog for
1956 * unknown reason, causing the brush not to update */
1957 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1958 lr = SendMessageA(dialog, WM_THEMECHANGED, 0, 0);
1959 ok(lr == 0, "WM_THEMECHANGED failed.\n");
1960 brush2 = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
1961 ok(brush2 != brush, "Expected a different brush.\n");
1963 ReleaseDC(child, child_hdc);
1964 EndDialog(dialog, 0);
1966 /* Test invalid flags */
1967 for (i = 0; i < ARRAY_SIZE(invalid_flag_tests); ++i)
1969 winetest_push_context("%d flag %#x", i, invalid_flag_tests[i].flag);
1971 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
1972 test_EnableThemeDialogTexture_proc, (LPARAM)&param);
1973 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
1974 hr = EnableThemeDialogTexture(dialog, invalid_flag_tests[i].flag);
1975 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
1976 ret = IsThemeDialogTextureEnabled(dialog);
1977 ok(ret == invalid_flag_tests[i].enabled, "Wrong dialog texture status.\n");
1978 EndDialog(dialog, 0);
1980 winetest_pop_context();
1983 /* Test different flag combinations */
1984 for (i = 0; i < ARRAY_SIZE(flags); ++i)
1986 for (j = 0; j < ARRAY_SIZE(flags); ++j)
1988 /* ETDT_USEAEROWIZARDTABTEXTURE is supported only on Vista+ */
1989 if (LOBYTE(LOWORD(GetVersion())) < 6
1990 && ((flags[i] | flags[j]) & ETDT_USEAEROWIZARDTABTEXTURE))
1991 continue;
1993 winetest_push_context("%#x to %#x", flags[i], flags[j]);
1995 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
1996 test_EnableThemeDialogTexture_proc, (LPARAM)&param);
1997 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
1998 flush_events();
1999 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2001 hr = EnableThemeDialogTexture(dialog, flags[i]);
2002 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
2003 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
2004 "EnableThemeDialogTexture first flag", FALSE);
2005 ret = IsThemeDialogTextureEnabled(dialog);
2006 /* Non-zero flags without ETDT_DISABLE enables dialog texture */
2007 ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
2009 child = GetDlgItem(dialog, 100);
2010 ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
2011 child_hdc = GetDC(child);
2012 lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
2013 ok(lr != 0, "WM_ERASEBKGND failed.\n");
2014 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2015 if (flags[i] == ETDT_ENABLETAB || flags[i] == ETDT_ENABLEAEROWIZARDTAB)
2016 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2017 else
2018 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2019 flush_sequences(sequences, NUM_MSG_SEQUENCES);
2021 hr = EnableThemeDialogTexture(dialog, flags[j]);
2022 ok(hr == S_OK, "EnableThemeDialogTexture failed, hr %#x.\n", hr);
2023 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
2024 "EnableThemeDialogTexture second flag", FALSE);
2025 ret = IsThemeDialogTextureEnabled(dialog);
2026 /* If the flag is zero, it will have previous dialog texture status */
2027 if (flags[j])
2028 ok(ret == !(flags[j] & ETDT_DISABLE), "Wrong dialog texture status.\n");
2029 else
2030 ok(ret == (!(flags[i] & ETDT_DISABLE) && flags[i]), "Wrong dialog texture status.\n");
2031 lr = SendMessageA(dialog, WM_ERASEBKGND, (WPARAM)child_hdc, 0);
2032 ok(lr != 0, "WM_ERASEBKGND failed.\n");
2033 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2034 /* Dialog texture is turned on when the flag contains ETDT_ENABLETAB or
2035 * ETDT_ENABLEAEROWIZARDTAB. The flag can be turned on in multiple steps, but you can't
2036 * do things like set ETDT_ENABLETAB and then ETDT_USEAEROWIZARDTABTEXTURE */
2037 if (((flags[j] == ETDT_ENABLETAB || flags[j] == ETDT_ENABLEAEROWIZARDTAB)
2038 || ((((flags[i] | flags[j]) & ETDT_ENABLETAB) == ETDT_ENABLETAB
2039 || ((flags[i] | flags[j]) & ETDT_ENABLEAEROWIZARDTAB) == ETDT_ENABLEAEROWIZARDTAB)
2040 && !((flags[i] | flags[j]) & ETDT_DISABLE)))
2041 && (((flags[i] | flags[j]) & (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)) != (ETDT_ENABLETAB | ETDT_ENABLEAEROWIZARDTAB)))
2042 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2043 else
2044 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2046 ReleaseDC(child, child_hdc);
2047 EndDialog(dialog, 0);
2049 winetest_pop_context();
2053 /* Test that the dialog procedure should set ETDT_USETABTEXTURE/ETDT_USEAEROWIZARDTABTEXTURE and
2054 * find out which comctl32 class should set ETDT_ENABLE to turn on dialog texture */
2055 for (i = 0; i < ARRAY_SIZE(class_tests); ++i)
2057 winetest_push_context("%s %#x", wine_dbgstr_a(class_tests[i].param.class_name),
2058 class_tests[i].param.style);
2060 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2061 test_EnableThemeDialogTexture_proc,
2062 (LPARAM)&class_tests[i].param);
2063 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
2064 /* GetDlgItem() fails to get the child control if the child is a tooltip */
2065 child = dialog_child;
2066 ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
2067 child_hdc = GetDC(child);
2069 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2070 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2072 ReleaseDC(child, child_hdc);
2073 EndDialog(dialog, 0);
2075 dialog_init_flag = ETDT_ENABLE;
2076 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2077 test_EnableThemeDialogTexture_proc,
2078 (LPARAM)&class_tests[i].param);
2079 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
2080 child = dialog_child;
2081 ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
2082 child_hdc = GetDC(child);
2084 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2085 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2087 ReleaseDC(child, child_hdc);
2088 EndDialog(dialog, 0);
2090 dialog_init_flag = ETDT_USETABTEXTURE;
2091 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2092 test_EnableThemeDialogTexture_proc,
2093 (LPARAM)&class_tests[i].param);
2094 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
2095 child = dialog_child;
2096 ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
2097 child_hdc = GetDC(child);
2098 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2099 if (class_tests[i].texture_enabled)
2100 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2101 else
2102 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2103 ReleaseDC(child, child_hdc);
2104 EndDialog(dialog, 0);
2106 if (LOBYTE(LOWORD(GetVersion())) < 6)
2108 dialog_init_flag = 0;
2109 winetest_pop_context();
2110 continue;
2113 dialog_init_flag = ETDT_USEAEROWIZARDTABTEXTURE;
2114 dialog = CreateDialogIndirectParamA(NULL, &temp.template, GetDesktopWindow(),
2115 test_EnableThemeDialogTexture_proc,
2116 (LPARAM)&class_tests[i].param);
2117 ok(dialog != NULL, "CreateDialogIndirectParamA failed, error %d.\n", GetLastError());
2118 child = dialog_child;
2119 ok(child != NULL, "Failed to get child control, error %d.\n", GetLastError());
2120 child_hdc = GetDC(child);
2121 brush = (HBRUSH)SendMessageW(dialog, WM_CTLCOLORSTATIC, (WPARAM)child_hdc, (LPARAM)child);
2122 if (class_tests[i].texture_enabled)
2123 ok(brush != GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture enabled.\n");
2124 else
2125 ok(brush == GetSysColorBrush(COLOR_BTNFACE), "Expected tab texture disabled.\n");
2126 ReleaseDC(child, child_hdc);
2127 EndDialog(dialog, 0);
2128 dialog_init_flag = 0;
2130 winetest_pop_context();
2133 /* Test that EnableThemeDialogTexture() is called from child controls for its parent */
2134 hwnd = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
2135 0, 0, NULL);
2136 ok(hwnd != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
2137 ret = IsThemeDialogTextureEnabled(hwnd);
2138 ok(!ret, "Wrong dialog texture status.\n");
2139 child = CreateWindowA(WC_STATICA, "child", WS_CHILD | WS_VISIBLE, 0, 0, 50, 50, hwnd, 0, 0,
2140 NULL);
2141 ok(child != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
2142 ret = IsThemeDialogTextureEnabled(hwnd);
2143 ok(ret, "Wrong dialog texture status.\n");
2145 /* Test that if you move the child control to another window, it doesn't enables tab texture for
2146 * the new parent */
2147 hwnd2 = CreateWindowA(cls.lpszClassName, "parent", WS_POPUP | WS_VISIBLE, 100, 100, 200, 200, 0,
2148 0, 0, NULL);
2149 ok(hwnd2 != NULL, "CreateWindowA failed, error %d.\n", GetLastError());
2150 ret = IsThemeDialogTextureEnabled(hwnd2);
2151 ok(!ret, "Wrong dialog texture status.\n");
2153 SetParent(child, hwnd2);
2154 ok(GetParent(child) == hwnd2, "Wrong parent.\n");
2155 ret = IsThemeDialogTextureEnabled(hwnd2);
2156 ok(!ret, "Wrong dialog texture status.\n");
2157 InvalidateRect(child, NULL, TRUE);
2158 flush_events();
2159 ret = IsThemeDialogTextureEnabled(hwnd2);
2160 ok(!ret, "Wrong dialog texture status.\n");
2162 DestroyWindow(hwnd2);
2163 DestroyWindow(hwnd);
2165 UnregisterClassA(cls.lpszClassName, GetModuleHandleA(NULL));
2168 START_TEST(system)
2170 ULONG_PTR ctx_cookie;
2171 HANDLE ctx;
2173 init_funcs();
2174 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
2176 /* No real functional theme API tests will be done (yet). The current tests
2177 * only show input/return behaviour
2180 test_IsThemed();
2181 test_GetWindowTheme();
2182 test_SetWindowTheme();
2183 test_OpenThemeData();
2184 test_OpenThemeDataEx();
2185 test_OpenThemeDataForDpi();
2186 test_GetCurrentThemeName();
2187 test_GetThemePartSize();
2188 test_CloseThemeData();
2189 test_buffered_paint();
2190 test_GetThemeIntList();
2191 test_GetThemeTransitionDuration();
2192 test_DrawThemeParentBackground();
2194 if (load_v6_module(&ctx_cookie, &ctx))
2196 test_EnableThemeDialogTexture();
2198 unload_v6_module(ctx_cookie, ctx);
2201 /* Test EnableTheming() in the end because it may disable theming */
2202 test_EnableTheming();