1 /*
2  * Unit tests for DCE support
3  *
4  * Copyright 2005 Alexandre Julliard
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 
30 #include "wine/test.h"
31 
32 #ifndef DCX_USESTYLE
33 #define DCX_USESTYLE         0x00010000
34 #endif
35 
36 static HWND hwnd_cache, hwnd_owndc, hwnd_classdc, hwnd_classdc2, hwnd_parent, hwnd_parentdc;
37 
38 /* test behavior of DC attributes with various GetDC/ReleaseDC combinations */
39 static void test_dc_attributes(void)
40 {
41     HDC hdc, old_hdc;
42     HDC hdcs[20];
43     INT i, rop, def_rop, caps;
44     BOOL found_dc;
45 
46     /* test cache DC */
47 
48     hdc = GetDC( hwnd_cache );
49     def_rop = GetROP2( hdc );
50 
51     SetROP2( hdc, R2_WHITE );
52     rop = GetROP2( hdc );
53     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
54 
55     ok( WindowFromDC( hdc ) == hwnd_cache, "wrong window\n" );
56     ReleaseDC( hwnd_cache, hdc );
57     ok( WindowFromDC( hdc ) == 0, "wrong window\n" );
58     hdc = GetDC( hwnd_cache );
59     rop = GetROP2( hdc );
60     ok( rop == def_rop, "wrong ROP2 %d after release\n", rop );
61     SetROP2( hdc, R2_WHITE );
62     ok( WindowFromDC( hdc ) == hwnd_cache, "wrong window\n" );
63     ReleaseDC( hwnd_cache, hdc );
64     old_hdc = hdc;
65 
66     found_dc = FALSE;
67     for (i = 0; i < 20; i++)
68     {
69         hdc = hdcs[i] = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
70         if (!hdc) break;
71         rop = GetROP2( hdc );
72         ok( rop == def_rop, "wrong ROP2 %d after release %p/%p\n", rop, old_hdc, hdc );
73         if (hdc == old_hdc)
74         {
75             found_dc = TRUE;
76             SetROP2( hdc, R2_WHITE );
77         }
78     }
79     if (!found_dc)
80     {
81         trace( "hdc %p not found in cache using %p\n", old_hdc, hdcs[0] );
82         old_hdc = hdcs[0];
83         SetROP2( old_hdc, R2_WHITE );
84     }
85     while (i > 0) ReleaseDC( hwnd_cache, hdcs[--i] );
86 
87     for (i = 0; i < 20; i++)
88     {
89         hdc = hdcs[i] = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
90         if (!hdc) break;
91         rop = GetROP2( hdc );
92         if (hdc == old_hdc)
93             ok( rop == R2_WHITE || broken( rop == def_rop),  /* win9x doesn't support DCX_NORESETATTRS */
94                 "wrong ROP2 %d after release %p/%p\n", rop, old_hdc, hdc );
95         else
96             ok( rop == def_rop, "wrong ROP2 %d after release %p/%p\n", rop, old_hdc, hdc );
97     }
98     while (i > 0) ReleaseDC( hwnd_cache, hdcs[--i] );
99 
100     for (i = 0; i < 20; i++)
101     {
102         hdc = hdcs[i] = GetDCEx( hwnd_cache, 0, DCX_USESTYLE );
103         if (!hdc) break;
104         rop = GetROP2( hdc );
105         if (hdc == old_hdc)
106         {
107             ok( rop == R2_WHITE || broken( rop == def_rop),
108                 "wrong ROP2 %d after release %p/%p\n", rop, old_hdc, hdc );
109             SetROP2( old_hdc, def_rop );
110         }
111         else
112             ok( rop == def_rop, "wrong ROP2 %d after release %p/%p\n", rop, old_hdc, hdc );
113     }
114     while (i > 0) ReleaseDC( hwnd_cache, hdcs[--i] );
115 
116     /* Released cache DCs are 'disabled' */
117     rop = SetROP2( old_hdc, R2_BLACK );
118     ok( rop == 0, "got %d\n", rop );
119     rop = GetROP2( old_hdc );
120     ok( rop == 0, "got %d\n", rop );
121     caps = GetDeviceCaps( old_hdc, HORZRES );
122     ok( caps == 0, "got %d\n", caps );
123     caps = GetDeviceCaps( old_hdc, VERTRES );
124     ok( caps == 0, "got %d\n", caps );
125     caps = GetDeviceCaps( old_hdc, NUMCOLORS );
126     ok( caps == 0, "got %d\n", caps );
127     ok( WindowFromDC( old_hdc ) == 0, "wrong window\n" );
128 
129     hdc = GetDC(0);
130     caps = GetDeviceCaps( hdc, HORZRES );
131     ok( caps != 0, "got %d\n", caps );
132     caps = GetDeviceCaps( hdc, VERTRES );
133     ok( caps != 0, "got %d\n", caps );
134     caps = GetDeviceCaps( hdc, NUMCOLORS );
135     ok( caps != 0, "got %d\n", caps );
136     ReleaseDC( 0, hdc );
137     caps = GetDeviceCaps( hdc, HORZRES );
138     ok( caps == 0, "got %d\n", caps );
139     caps = GetDeviceCaps( hdc, VERTRES );
140     ok( caps == 0, "got %d\n", caps );
141     caps = GetDeviceCaps( hdc, NUMCOLORS );
142     ok( caps == 0, "got %d\n", caps );
143 
144     /* test own DC */
145 
146     hdc = GetDC( hwnd_owndc );
147     SetROP2( hdc, R2_WHITE );
148     rop = GetROP2( hdc );
149     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
150 
151     old_hdc = hdc;
152     ok( WindowFromDC( hdc ) == hwnd_owndc, "wrong window\n" );
153     ReleaseDC( hwnd_owndc, hdc );
154     ok( WindowFromDC( hdc ) == hwnd_owndc, "wrong window\n" );
155     hdc = GetDC( hwnd_owndc );
156     ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
157     rop = GetROP2( hdc );
158     ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
159     ok( WindowFromDC( hdc ) == hwnd_owndc, "wrong window\n" );
160     ReleaseDC( hwnd_owndc, hdc );
161     rop = GetROP2( hdc );
162     ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop );
163 
164     /* test class DC */
165 
166     hdc = GetDC( hwnd_classdc );
167     SetROP2( hdc, R2_WHITE );
168     rop = GetROP2( hdc );
169     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
170 
171     old_hdc = hdc;
172     ok( WindowFromDC( hdc ) == hwnd_classdc, "wrong window\n" );
173     ReleaseDC( hwnd_classdc, hdc );
174     ok( WindowFromDC( hdc ) == hwnd_classdc, "wrong window\n" );
175     hdc = GetDC( hwnd_classdc );
176     ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
177     rop = GetROP2( hdc );
178     ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
179     ok( WindowFromDC( hdc ) == hwnd_classdc, "wrong window\n" );
180     ReleaseDC( hwnd_classdc, hdc );
181     rop = GetROP2( hdc );
182     ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop );
183 
184     /* test class DC with 2 windows */
185 
186     old_hdc = GetDC( hwnd_classdc );
187     SetROP2( old_hdc, R2_BLACK );
188     ok( WindowFromDC( old_hdc ) == hwnd_classdc, "wrong window\n" );
189     hdc = GetDC( hwnd_classdc2 );
190     ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
191     rop = GetROP2( hdc );
192     ok( rop == R2_BLACK, "wrong ROP2 %d for other window\n", rop );
193     ok( WindowFromDC( hdc ) == hwnd_classdc2, "wrong window\n" );
194     ReleaseDC( hwnd_classdc, old_hdc );
195     ReleaseDC( hwnd_classdc, hdc );
196     ok( WindowFromDC( hdc ) == hwnd_classdc2, "wrong window\n" );
197     rop = GetROP2( hdc );
198     ok( rop == R2_BLACK, "wrong ROP2 %d after release\n", rop );
199 }
200 
201 
202 /* test behavior with various invalid parameters */
203 static void test_parameters(void)
204 {
205     HDC hdc;
206 
207     hdc = GetDC( hwnd_cache );
208     ok( ReleaseDC( hwnd_owndc, hdc ), "ReleaseDC with wrong window should succeed\n" );
209 
210     hdc = GetDC( hwnd_cache );
211     ok( !ReleaseDC( hwnd_cache, 0 ), "ReleaseDC with wrong HDC should fail\n" );
212     ok( ReleaseDC( hwnd_cache, hdc ), "correct ReleaseDC should succeed\n" );
213     ok( !ReleaseDC( hwnd_cache, hdc ), "second ReleaseDC should fail\n" );
214 
215     hdc = GetDC( hwnd_owndc );
216     ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" );
217     hdc = GetDC( hwnd_owndc );
218     ok( ReleaseDC( hwnd_owndc, hdc ), "correct ReleaseDC should succeed\n" );
219     ok( ReleaseDC( hwnd_owndc, hdc ), "second ReleaseDC should succeed\n" );
220 
221     hdc = GetDC( hwnd_classdc );
222     ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" );
223     hdc = GetDC( hwnd_classdc );
224     ok( ReleaseDC( hwnd_classdc, hdc ), "correct ReleaseDC should succeed\n" );
225     ok( ReleaseDC( hwnd_classdc, hdc ), "second ReleaseDC should succeed\n" );
226 }
227 
228 
229 static void test_dc_visrgn(void)
230 {
231     HDC old_hdc, hdc;
232     HRGN hrgn, hrgn2;
233     RECT rect, parent_rect;
234 
235     /* cache DC */
236 
237     SetRect( &rect, 10, 10, 20, 20 );
238     MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 );
239     hrgn = CreateRectRgnIndirect( &rect );
240     hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
241     SetRectEmpty( &rect );
242     GetClipBox( hdc, &rect );
243     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
244         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
245     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
246     ReleaseDC( hwnd_cache, hdc );
247     ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
248 
249     /* cache DC with NORESETATTRS */
250 
251     SetRect( &rect, 10, 10, 20, 20 );
252     MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 );
253     hrgn = CreateRectRgnIndirect( &rect );
254     hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE | DCX_NORESETATTRS );
255     SetRectEmpty( &rect );
256     GetClipBox( hdc, &rect );
257     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
258         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
259     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
260     ReleaseDC( hwnd_cache, hdc );
261     ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
262     hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
263     SetRectEmpty( &rect );
264     GetClipBox( hdc, &rect );
265     ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
266         "clip box should have been reset %s\n", wine_dbgstr_rect( &rect ));
267     ReleaseDC( hwnd_cache, hdc );
268 
269     /* window DC */
270 
271     SetRect( &rect, 10, 10, 20, 20 );
272     MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 );
273     hrgn = CreateRectRgnIndirect( &rect );
274     hdc = GetDCEx( hwnd_owndc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
275     SetRectEmpty( &rect );
276     GetClipBox( hdc, &rect );
277     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
278         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
279     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
280     ReleaseDC( hwnd_owndc, hdc );
281     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
282     SetRectEmpty( &rect );
283     GetClipBox( hdc, &rect );
284     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
285         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
286     hdc = GetDCEx( hwnd_owndc, 0, DCX_USESTYLE );
287     SetRectEmpty( &rect );
288     GetClipBox( hdc, &rect );
289     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
290         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
291     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
292     ReleaseDC( hwnd_owndc, hdc );
293     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
294 
295     SetRect( &rect, 20, 20, 30, 30 );
296     MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 );
297     hrgn2 = CreateRectRgnIndirect( &rect );
298     hdc = GetDCEx( hwnd_owndc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE );
299     ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
300     SetRectEmpty( &rect );
301     GetClipBox( hdc, &rect );
302     ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30,
303         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
304     ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
305     ReleaseDC( hwnd_owndc, hdc );
306     ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
307     hdc = GetDCEx( hwnd_owndc, 0, DCX_EXCLUDERGN | DCX_USESTYLE );
308     ok( GetRgnBox( hrgn2, &rect ) == ERROR, "region must no longer be valid\n" );
309     SetRectEmpty( &rect );
310     GetClipBox( hdc, &rect );
311     ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
312         "clip box should have been reset %s\n", wine_dbgstr_rect( &rect ));
313     ReleaseDC( hwnd_owndc, hdc );
314 
315     /* class DC */
316 
317     SetRect( &rect, 10, 10, 20, 20 );
318     MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 );
319     hrgn = CreateRectRgnIndirect( &rect );
320     hdc = GetDCEx( hwnd_classdc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
321     SetRectEmpty( &rect );
322     GetClipBox( hdc, &rect );
323     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
324         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
325     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
326     ReleaseDC( hwnd_classdc, hdc );
327     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
328     SetRectEmpty( &rect );
329     GetClipBox( hdc, &rect );
330     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
331         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
332 
333     hdc = GetDCEx( hwnd_classdc, 0, DCX_USESTYLE );
334     SetRectEmpty( &rect );
335     GetClipBox( hdc, &rect );
336     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
337         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
338     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
339     ReleaseDC( hwnd_classdc, hdc );
340     ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
341 
342     SetRect( &rect, 20, 20, 30, 30 );
343     MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 );
344     hrgn2 = CreateRectRgnIndirect( &rect );
345     hdc = GetDCEx( hwnd_classdc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE );
346     ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
347     SetRectEmpty( &rect );
348     GetClipBox( hdc, &rect );
349     ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30,
350         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
351     ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
352 
353     old_hdc = hdc;
354     hdc = GetDCEx( hwnd_classdc2, 0, DCX_USESTYLE );
355     ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc );
356     ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
357     SetRectEmpty( &rect );
358     GetClipBox( hdc, &rect );
359     ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
360         "clip box should have been reset %s\n", wine_dbgstr_rect( &rect ));
361     ReleaseDC( hwnd_classdc2, hdc );
362     ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
363     hdc = GetDCEx( hwnd_classdc2, 0, DCX_EXCLUDERGN | DCX_USESTYLE );
364     ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
365     ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
366         "clip box must have been reset %s\n", wine_dbgstr_rect( &rect ));
367     ReleaseDC( hwnd_classdc2, hdc );
368 
369     /* parent DC */
370     hdc = GetDC( hwnd_parentdc );
371     GetClipBox( hdc, &rect );
372     ReleaseDC( hwnd_parentdc, hdc );
373 
374     hdc = GetDC( hwnd_parent );
375     GetClipBox( hdc, &parent_rect );
376     ReleaseDC( hwnd_parent, hdc );
377 
378     ok( EqualRect( &rect, &parent_rect ), "rect = %s, expected %s\n", wine_dbgstr_rect( &rect ),
379         wine_dbgstr_rect( &parent_rect ));
380 }
381 
382 
383 /* test various BeginPaint/EndPaint behaviors */
384 static void test_begin_paint(void)
385 {
386     HDC old_hdc, hdc;
387     RECT rect, parent_rect;
388     PAINTSTRUCT ps;
389     COLORREF cr;
390 
391     /* cache DC */
392 
393     /* clear update region */
394     RedrawWindow( hwnd_cache, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
395     SetRect( &rect, 10, 10, 20, 20 );
396     RedrawWindow( hwnd_cache, &rect, 0, RDW_INVALIDATE );
397     hdc = BeginPaint( hwnd_cache, &ps );
398     SetRectEmpty( &rect );
399     GetClipBox( hdc, &rect );
400     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
401         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
402     EndPaint( hwnd_cache, &ps );
403 
404     /* window DC */
405 
406     RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
407     SetRect( &rect, 10, 10, 20, 20 );
408     RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE );
409     hdc = BeginPaint( hwnd_owndc, &ps );
410     SetRectEmpty( &rect );
411     GetClipBox( hdc, &rect );
412     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
413         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
414     ReleaseDC( hwnd_owndc, hdc );
415     SetRectEmpty( &rect );
416     GetClipBox( hdc, &rect );
417     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
418         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
419     ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" );
420     SetRectEmpty( &rect );
421     GetClipBox( hdc, &rect );
422     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
423         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
424     EndPaint( hwnd_owndc, &ps );
425     SetRectEmpty( &rect );
426     GetClipBox( hdc, &rect );
427     ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
428         "clip box should have been reset %s\n", wine_dbgstr_rect( &rect ));
429     RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
430     SetRect( &rect, 10, 10, 20, 20 );
431     RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE|RDW_ERASE );
432     ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" );
433     SetRectEmpty( &rect );
434     GetClipBox( hdc, &rect );
435     ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
436         "clip box should be the whole window %s\n", wine_dbgstr_rect( &rect ));
437     RedrawWindow( hwnd_owndc, NULL, 0, RDW_ERASENOW );
438     SetRectEmpty( &rect );
439     GetClipBox( hdc, &rect );
440     ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
441         "clip box should still be the whole window %s\n", wine_dbgstr_rect( &rect ));
442 
443     /* class DC */
444 
445     RedrawWindow( hwnd_classdc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
446     SetRect( &rect, 10, 10, 20, 20 );
447     RedrawWindow( hwnd_classdc, &rect, 0, RDW_INVALIDATE );
448     hdc = BeginPaint( hwnd_classdc, &ps );
449     SetRectEmpty( &rect );
450     GetClipBox( hdc, &rect );
451     ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
452         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
453 
454     old_hdc = hdc;
455     hdc = GetDC( hwnd_classdc2 );
456     ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc );
457     SetRectEmpty( &rect );
458     GetClipBox( hdc, &rect );
459     ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
460         "clip box should have been reset %s\n", wine_dbgstr_rect( &rect ));
461     ReleaseDC( hwnd_classdc2, hdc );
462     EndPaint( hwnd_classdc, &ps );
463 
464     /* parent DC */
465     RedrawWindow( hwnd_parent, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
466     RedrawWindow( hwnd_parentdc, NULL, 0, RDW_INVALIDATE );
467     hdc = BeginPaint( hwnd_parentdc, &ps );
468     GetClipBox( hdc, &rect );
469     cr = SetPixel( hdc, 10, 10, RGB(255, 0, 0) );
470     ok( cr != -1, "error drawing outside of window client area\n" );
471     EndPaint( hwnd_parentdc, &ps );
472     GetClientRect( hwnd_parent, &parent_rect );
473 
474     ok( rect.left == parent_rect.left, "rect.left = %d, expected %d\n", rect.left, parent_rect.left );
475     ok( rect.top == parent_rect.top, "rect.top = %d, expected %d\n", rect.top, parent_rect.top );
476     todo_wine ok( rect.right == parent_rect.right, "rect.right = %d, expected %d\n", rect.right, parent_rect.right );
477     todo_wine ok( rect.bottom == parent_rect.bottom, "rect.bottom = %d, expected %d\n", rect.bottom, parent_rect.bottom );
478 
479     hdc = GetDC( hwnd_parent );
480     todo_wine ok( GetPixel( hdc, 10, 10 ) == cr, "error drawing outside of window client area\n" );
481     ReleaseDC( hwnd_parent, hdc );
482 }
483 
484 /* test ScrollWindow with window DCs */
485 static void test_scroll_window(void)
486 {
487     PAINTSTRUCT ps;
488     HDC hdc;
489     RECT clip, rect;
490 
491     /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
492 
493     UpdateWindow( hwnd_owndc );
494     SetRect( &clip, 25, 25, 50, 50 );
495     ScrollWindow( hwnd_owndc, -5, -10, NULL, &clip );
496     hdc = BeginPaint( hwnd_owndc, &ps );
497     SetRectEmpty( &rect );
498     GetClipBox( hdc, &rect );
499     ok( rect.left >= 25 && rect.top >= 25 && rect.right <= 50 && rect.bottom <= 50,
500         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
501     EndPaint( hwnd_owndc, &ps );
502 
503     SetViewportExtEx( hdc, 2, 3, NULL );
504     SetViewportOrgEx( hdc, 30, 20, NULL );
505 
506     ScrollWindow( hwnd_owndc, -5, -10, NULL, &clip );
507     hdc = BeginPaint( hwnd_owndc, &ps );
508     SetRectEmpty( &rect );
509     GetClipBox( hdc, &rect );
510     ok( rect.left >= 25 && rect.top >= 25 && rect.right <= 50 && rect.bottom <= 50,
511         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
512     EndPaint( hwnd_owndc, &ps );
513 
514     ScrollWindowEx( hwnd_owndc, -5, -10, NULL, &clip, 0, NULL, SW_INVALIDATE | SW_ERASE );
515     hdc = BeginPaint( hwnd_owndc, &ps );
516     SetRectEmpty( &rect );
517     GetClipBox( hdc, &rect );
518     ok( rect.left >= -5 && rect.top >= 5 && rect.right <= 20 && rect.bottom <= 30,
519         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
520     EndPaint( hwnd_owndc, &ps );
521 
522     SetViewportExtEx( hdc, 1, 1, NULL );
523     SetViewportOrgEx( hdc, 0, 0, NULL );
524 
525     ScrollWindowEx( hwnd_owndc, -5, -10, NULL, &clip, 0, NULL, SW_INVALIDATE | SW_ERASE );
526     hdc = BeginPaint( hwnd_owndc, &ps );
527     SetRectEmpty( &rect );
528     GetClipBox( hdc, &rect );
529     ok( rect.left >= 25 && rect.top >= 25 && rect.right <= 50 && rect.bottom <= 50,
530         "invalid clip box %s\n", wine_dbgstr_rect( &rect ));
531     EndPaint( hwnd_owndc, &ps );
532 }
533 
534 static void test_invisible_create(void)
535 {
536     HWND hwnd_owndc = CreateWindowA("owndc_class", NULL, WS_OVERLAPPED,
537                                     0, 200, 100, 100,
538                                     0, 0, GetModuleHandleA(0), NULL );
539     HDC dc1, dc2;
540 
541     dc1 = GetDC(hwnd_owndc);
542     dc2 = GetDC(hwnd_owndc);
543 
544     ok(dc1 == dc2, "expected owndc dcs to match\n");
545 
546     ReleaseDC(hwnd_owndc, dc2);
547     ReleaseDC(hwnd_owndc, dc1);
548     DestroyWindow(hwnd_owndc);
549 }
550 
551 static void test_dc_layout(void)
552 {
553     DWORD (WINAPI *pSetLayout)(HDC hdc, DWORD layout);
554     DWORD (WINAPI *pGetLayout)(HDC hdc);
555     HWND hwnd_cache_rtl, hwnd_owndc_rtl, hwnd_classdc_rtl, hwnd_classdc2_rtl;
556     HDC hdc;
557     DWORD layout;
558     HMODULE mod = GetModuleHandleA("gdi32.dll");
559 
560     pGetLayout = (void *)GetProcAddress( mod, "GetLayout" );
561     pSetLayout = (void *)GetProcAddress( mod, "SetLayout" );
562     if (!pGetLayout || !pSetLayout)
563     {
564         win_skip( "Don't have SetLayout\n" );
565         return;
566     }
567 
568     hdc = GetDC( hwnd_cache );
569     pSetLayout( hdc, LAYOUT_RTL );
570     layout = pGetLayout( hdc );
571     ReleaseDC( hwnd_cache, hdc );
572     if (!layout)
573     {
574         win_skip( "SetLayout not supported\n" );
575         return;
576     }
577 
578     hwnd_cache_rtl = CreateWindowExA(WS_EX_LAYOUTRTL, "cache_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
579                                      0, 0, 100, 100, 0, 0, GetModuleHandleA(0), NULL );
580     hwnd_owndc_rtl = CreateWindowExA(WS_EX_LAYOUTRTL, "owndc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
581                                      0, 200, 100, 100, 0, 0, GetModuleHandleA(0), NULL );
582     hwnd_classdc_rtl = CreateWindowExA(WS_EX_LAYOUTRTL, "classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
583                                        200, 0, 100, 100, 0, 0, GetModuleHandleA(0), NULL );
584     hwnd_classdc2_rtl = CreateWindowExA(WS_EX_LAYOUTRTL, "classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
585                                         200, 200, 100, 100, 0, 0, GetModuleHandleA(0), NULL );
586     hdc = GetDC( hwnd_cache_rtl );
587     layout = pGetLayout( hdc );
588 
589     ok( layout == LAYOUT_RTL, "wrong layout %x\n", layout );
590     pSetLayout( hdc, 0 );
591     ReleaseDC( hwnd_cache_rtl, hdc );
592     hdc = GetDC( hwnd_owndc_rtl );
593     layout = pGetLayout( hdc );
594     ok( layout == LAYOUT_RTL, "wrong layout %x\n", layout );
595     ReleaseDC( hwnd_cache_rtl, hdc );
596 
597     hdc = GetDC( hwnd_cache );
598     layout = pGetLayout( hdc );
599     ok( layout == 0, "wrong layout %x\n", layout );
600     ReleaseDC( hwnd_cache, hdc );
601 
602     hdc = GetDC( hwnd_owndc_rtl );
603     layout = pGetLayout( hdc );
604     ok( layout == LAYOUT_RTL, "wrong layout %x\n", layout );
605     pSetLayout( hdc, 0 );
606     ReleaseDC( hwnd_owndc_rtl, hdc );
607     hdc = GetDC( hwnd_owndc_rtl );
608     layout = pGetLayout( hdc );
609     ok( layout == LAYOUT_RTL, "wrong layout %x\n", layout );
610     ReleaseDC( hwnd_owndc_rtl, hdc );
611 
612     hdc = GetDC( hwnd_classdc_rtl );
613     layout = pGetLayout( hdc );
614     ok( layout == LAYOUT_RTL, "wrong layout %x\n", layout );
615     pSetLayout( hdc, 0 );
616     ReleaseDC( hwnd_classdc_rtl, hdc );
617     hdc = GetDC( hwnd_classdc2_rtl );
618     layout = pGetLayout( hdc );
619     ok( layout == LAYOUT_RTL, "wrong layout %x\n", layout );
620     ReleaseDC( hwnd_classdc2_rtl, hdc );
621     hdc = GetDC( hwnd_classdc );
622     layout = pGetLayout( hdc );
623     ok( layout == LAYOUT_RTL, "wrong layout %x\n", layout );
624     ReleaseDC( hwnd_classdc_rtl, hdc );
625 
626     DestroyWindow(hwnd_classdc2_rtl);
627     DestroyWindow(hwnd_classdc_rtl);
628     DestroyWindow(hwnd_owndc_rtl);
629     DestroyWindow(hwnd_cache_rtl);
630 }
631 
632 static void test_destroyed_window(void)
633 {
634     HDC dc, old_dc;
635     HDC hdcs[30];
636     int i, rop;
637 
638     dc = GetDC( hwnd_cache );
639     SetROP2( dc, R2_WHITE );
640     rop = GetROP2( dc );
641     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
642     ok( WindowFromDC( dc ) == hwnd_cache, "wrong window\n" );
643     old_dc = dc;
644 
645     DestroyWindow( hwnd_cache );
646     rop = GetROP2( dc );
647     ok( rop == 0, "wrong ROP2 %d\n", rop );
648     ok( WindowFromDC( dc ) == 0, "wrong window\n" );
649     ok( !ReleaseDC( hwnd_cache, dc ), "ReleaseDC succeeded\n" );
650     dc = GetDC( hwnd_cache );
651     ok( !dc, "Got a non-NULL DC (%p) for a destroyed window\n", dc );
652 
653     for (i = 0; i < 30; i++)
654     {
655         dc = hdcs[i] = GetDCEx( hwnd_parent, 0, DCX_CACHE | DCX_USESTYLE );
656         if (dc == old_dc) break;
657     }
658     ok( i < 30, "DC for destroyed window not reused\n" );
659     while (i > 0) ReleaseDC( hwnd_parent, hdcs[--i] );
660 
661     dc = GetDC( hwnd_classdc );
662     SetROP2( dc, R2_WHITE );
663     rop = GetROP2( dc );
664     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
665     ok( WindowFromDC( dc ) == hwnd_classdc, "wrong window\n" );
666     old_dc = dc;
667 
668     dc = GetDC( hwnd_classdc2 );
669     ok( old_dc == dc, "wrong DC\n" );
670     rop = GetROP2( dc );
671     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
672     ok( WindowFromDC( dc ) == hwnd_classdc2, "wrong window\n" );
673     DestroyWindow( hwnd_classdc2 );
674 
675     rop = GetROP2( dc );
676     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
677     ok( WindowFromDC( dc ) == 0, "wrong window\n" );
678     ok( !ReleaseDC( hwnd_classdc2, dc ), "ReleaseDC succeeded\n" );
679     dc = GetDC( hwnd_classdc2 );
680     ok( !dc, "Got a non-NULL DC (%p) for a destroyed window\n", dc );
681 
682     dc = GetDC( hwnd_classdc );
683     ok( dc != 0, "Got NULL DC\n" );
684     rop = GetROP2( dc );
685     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
686     ok( WindowFromDC( dc ) == hwnd_classdc, "wrong window\n" );
687     DestroyWindow( hwnd_classdc );
688 
689     rop = GetROP2( dc );
690     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
691     ok( WindowFromDC( dc ) == 0, "wrong window\n" );
692     ok( !ReleaseDC( hwnd_classdc, dc ), "ReleaseDC succeeded\n" );
693     dc = GetDC( hwnd_classdc );
694     ok( !dc, "Got a non-NULL DC (%p) for a destroyed window\n", dc );
695 
696     dc = GetDC( hwnd_owndc );
697     ok( dc != 0, "Got NULL DC\n" );
698     rop = GetROP2( dc );
699     ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
700     ok( WindowFromDC( dc ) == hwnd_owndc, "wrong window\n" );
701     DestroyWindow( hwnd_owndc );
702 
703     rop = GetROP2( dc );
704     ok( rop == 0, "wrong ROP2 %d\n", rop );
705     ok( WindowFromDC( dc ) == 0, "wrong window\n" );
706     ok( !ReleaseDC( hwnd_owndc, dc ), "ReleaseDC succeeded\n" );
707     dc = GetDC( hwnd_owndc );
708     ok( !dc, "Got a non-NULL DC (%p) for a destroyed window\n", dc );
709 
710     DestroyWindow( hwnd_parent );
711 }
712 
713 START_TEST(dce)
714 {
715     WNDCLASSA cls;
716 
717     cls.style = CS_DBLCLKS;
718     cls.lpfnWndProc = DefWindowProcA;
719     cls.cbClsExtra = 0;
720     cls.cbWndExtra = 0;
721     cls.hInstance = GetModuleHandleA(0);
722     cls.hIcon = 0;
723     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
724     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
725     cls.lpszMenuName = NULL;
726     cls.lpszClassName = "cache_class";
727     RegisterClassA(&cls);
728     cls.style = CS_DBLCLKS | CS_OWNDC;
729     cls.lpszClassName = "owndc_class";
730     RegisterClassA(&cls);
731     cls.style = CS_DBLCLKS | CS_CLASSDC;
732     cls.lpszClassName = "classdc_class";
733     RegisterClassA(&cls);
734     cls.style = CS_PARENTDC;
735     cls.lpszClassName = "parentdc_class";
736     RegisterClassA(&cls);
737 
738     hwnd_cache = CreateWindowA("cache_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
739                                0, 0, 100, 100,
740                                0, 0, GetModuleHandleA(0), NULL );
741     hwnd_owndc = CreateWindowA("owndc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
742                                0, 200, 100, 100,
743                                0, 0, GetModuleHandleA(0), NULL );
744     hwnd_classdc = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
745                                  200, 0, 100, 100,
746                                  0, 0, GetModuleHandleA(0), NULL );
747     hwnd_classdc2 = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
748                                   200, 200, 100, 100,
749                                   0, 0, GetModuleHandleA(0), NULL );
750     hwnd_parent = CreateWindowA("static", NULL, WS_OVERLAPPED | WS_VISIBLE,
751                                 400, 0, 100, 100, 0, 0, 0, NULL );
752     hwnd_parentdc = CreateWindowA("parentdc_class", NULL, WS_CHILD | WS_VISIBLE,
753                                   0, 0, 1, 1, hwnd_parent, 0, 0, NULL );
754 
755     test_dc_attributes();
756     test_parameters();
757     test_dc_visrgn();
758     test_begin_paint();
759     test_scroll_window();
760     test_invisible_create();
761     test_dc_layout();
762     /* this should be last */
763     test_destroyed_window();
764 }
765