gdi32: Add support for solid diagonal lines.
[wine] / dlls / gdi32 / tests / pen.c
1 /*
2  * Unit test suite for pens
3  *
4  * Copyright 2006 Dmitry Timoshkov
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 <stdarg.h>
22 #include <assert.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28
29 #include "wine/test.h"
30
31 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
32 #define expect2(expected, alt, got) ok(got == expected || got == alt, \
33                                        "Expected %.8x or %.8x, got %.8x\n", expected, alt, got)
34
35 static void test_logpen(void)
36 {
37     static const struct
38     {
39         UINT style;
40         INT width;
41         COLORREF color;
42         UINT ret_style;
43         INT ret_width;
44         COLORREF ret_color;
45     } pen[] = {
46         { PS_SOLID, -123, RGB(0x12,0x34,0x56), PS_SOLID, 123, RGB(0x12,0x34,0x56) },
47         { PS_SOLID, 0, RGB(0x12,0x34,0x56), PS_SOLID, 0, RGB(0x12,0x34,0x56) },
48         { PS_SOLID, 123, RGB(0x12,0x34,0x56), PS_SOLID, 123, RGB(0x12,0x34,0x56) },
49         { PS_DASH, 123, RGB(0x12,0x34,0x56), PS_DASH, 123, RGB(0x12,0x34,0x56) },
50         { PS_DOT, 123, RGB(0x12,0x34,0x56), PS_DOT, 123, RGB(0x12,0x34,0x56) },
51         { PS_DASHDOT, 123, RGB(0x12,0x34,0x56), PS_DASHDOT, 123, RGB(0x12,0x34,0x56) },
52         { PS_DASHDOTDOT, 123, RGB(0x12,0x34,0x56), PS_DASHDOTDOT, 123, RGB(0x12,0x34,0x56) },
53         { PS_NULL, -123, RGB(0x12,0x34,0x56), PS_NULL, 1, 0 },
54         { PS_NULL, 123, RGB(0x12,0x34,0x56), PS_NULL, 1, 0 },
55         { PS_INSIDEFRAME, 123, RGB(0x12,0x34,0x56), PS_INSIDEFRAME, 123, RGB(0x12,0x34,0x56) },
56         { PS_USERSTYLE, 123, RGB(0x12,0x34,0x56), PS_SOLID, 123, RGB(0x12,0x34,0x56) },
57         { PS_ALTERNATE, 123, RGB(0x12,0x34,0x56), PS_SOLID, 123, RGB(0x12,0x34,0x56) }
58     };
59     INT i, size;
60     HPEN hpen;
61     LOGPEN lp;
62     EXTLOGPEN elp;
63     LOGBRUSH lb;
64     DWORD_PTR unset_hatch;
65     DWORD obj_type, user_style[2] = { 0xabc, 0xdef };
66     struct
67     {
68         EXTLOGPEN elp;
69         DWORD style_data[10];
70     } ext_pen;
71
72     for (i = 0; i < sizeof(pen)/sizeof(pen[0]); i++)
73     {
74         trace("%d: testing style %u\n", i, pen[i].style);
75
76         /********************** cosmetic pens **********************/
77         /* CreatePenIndirect behaviour */
78         lp.lopnStyle = pen[i].style,
79         lp.lopnWidth.x = pen[i].width;
80         lp.lopnWidth.y = 11; /* just in case */
81         lp.lopnColor = pen[i].color;
82         SetLastError(0xdeadbeef);
83         hpen = CreatePenIndirect(&lp);
84         ok(hpen != 0, "CreatePen error %d\n", GetLastError());
85
86         obj_type = GetObjectType(hpen);
87         ok(obj_type == OBJ_PEN, "wrong object type %u\n", obj_type);
88
89         memset(&lp, 0xb0, sizeof(lp));
90         SetLastError(0xdeadbeef);
91         size = GetObject(hpen, sizeof(lp), &lp);
92         ok(size == sizeof(lp), "GetObject returned %d, error %d\n", size, GetLastError());
93
94         if (pen[i].style == PS_USERSTYLE || pen[i].style == PS_ALTERNATE)
95         {
96            if (lp.lopnStyle == pen[i].style)
97            {
98                win_skip("Skipping PS_USERSTYLE and PS_ALTERNATE tests on Win9x\n");
99                continue;
100            }
101         }
102         ok(lp.lopnStyle == pen[i].ret_style, "expected %u, got %u\n", pen[i].ret_style, lp.lopnStyle);
103         ok(lp.lopnWidth.x == pen[i].ret_width, "expected %u, got %d\n", pen[i].ret_width, lp.lopnWidth.x);
104         ok(lp.lopnWidth.y == 0 ||
105             broken(lp.lopnWidth.y == 0xb), /* Win9x */
106             "expected 0, got %d\n", lp.lopnWidth.y);
107         ok(lp.lopnColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, lp.lopnColor);
108
109         DeleteObject(hpen);
110
111         /* CreatePen behaviour */
112         SetLastError(0xdeadbeef);
113         hpen = CreatePen(pen[i].style, pen[i].width, pen[i].color);
114         ok(hpen != 0, "CreatePen error %d\n", GetLastError());
115
116         obj_type = GetObjectType(hpen);
117         ok(obj_type == OBJ_PEN, "wrong object type %u\n", obj_type);
118
119         /* check what's the real size of the object */
120         size = GetObject(hpen, 0, NULL);
121         ok(size == sizeof(lp), "GetObject returned %d, error %d\n", size, GetLastError());
122
123         /* ask for truncated data */
124         memset(&lp, 0xb0, sizeof(lp));
125         SetLastError(0xdeadbeef);
126         size = GetObject(hpen, sizeof(lp.lopnStyle), &lp);
127         ok(!size ||
128             broken(size == sizeof(lp.lopnStyle)), /* Win9x */
129             "GetObject should fail: size %d, error %d\n", size, GetLastError());
130
131         /* see how larger buffer sizes are handled */
132         memset(&lp, 0xb0, sizeof(lp));
133         SetLastError(0xdeadbeef);
134         size = GetObject(hpen, sizeof(lp) * 4, &lp);
135         ok(size == sizeof(lp), "GetObject returned %d, error %d\n", size, GetLastError());
136
137         /* see how larger buffer sizes are handled */
138         memset(&elp, 0xb0, sizeof(elp));
139         SetLastError(0xdeadbeef);
140         size = GetObject(hpen, sizeof(elp) * 2, &elp);
141         ok(size == sizeof(lp), "GetObject returned %d, error %d\n", size, GetLastError());
142
143         memset(&lp, 0xb0, sizeof(lp));
144         SetLastError(0xdeadbeef);
145         size = GetObject(hpen, sizeof(lp), &lp);
146         ok(size == sizeof(lp), "GetObject returned %d, error %d\n", size, GetLastError());
147
148         ok(lp.lopnStyle == pen[i].ret_style, "expected %u, got %u\n", pen[i].ret_style, lp.lopnStyle);
149         ok(lp.lopnWidth.x == pen[i].ret_width, "expected %u, got %d\n", pen[i].ret_width, lp.lopnWidth.x);
150         ok(lp.lopnWidth.y == 0, "expected 0, got %d\n", lp.lopnWidth.y);
151         ok(lp.lopnColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, lp.lopnColor);
152
153         memset(&elp, 0xb0, sizeof(elp));
154         SetLastError(0xdeadbeef);
155         size = GetObject(hpen, sizeof(elp), &elp);
156
157         /* for some reason XP differentiates PS_NULL here */
158         if (pen[i].style == PS_NULL)
159         {
160             ok(hpen == GetStockObject(NULL_PEN), "hpen should be a stock NULL_PEN\n");
161             ok(size == sizeof(EXTLOGPEN), "GetObject returned %d, error %d\n", size, GetLastError());
162             ok(elp.elpPenStyle == pen[i].ret_style, "expected %u, got %u\n", pen[i].ret_style, elp.elpPenStyle);
163             ok(elp.elpWidth == 0, "expected 0, got %u\n", elp.elpWidth);
164             ok(elp.elpColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, elp.elpColor);
165             ok(elp.elpBrushStyle == BS_SOLID, "expected BS_SOLID, got %u\n", elp.elpBrushStyle);
166             ok(elp.elpHatch == 0, "expected 0, got %p\n", (void *)elp.elpHatch);
167             ok(elp.elpNumEntries == 0, "expected 0, got %x\n", elp.elpNumEntries);
168         }
169         else
170         {
171             ok(size == sizeof(LOGPEN), "GetObject returned %d, error %d\n", size, GetLastError());
172             memcpy(&lp, &elp, sizeof(lp));
173             ok(lp.lopnStyle == pen[i].ret_style, "expected %u, got %u\n", pen[i].ret_style, lp.lopnStyle);
174             ok(lp.lopnWidth.x == pen[i].ret_width, "expected %u, got %d\n", pen[i].ret_width, lp.lopnWidth.x);
175             ok(lp.lopnWidth.y == 0, "expected 0, got %d\n", lp.lopnWidth.y);
176             ok(lp.lopnColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, lp.lopnColor);
177         }
178
179         DeleteObject(hpen);
180
181         /********** cosmetic pens created by ExtCreatePen ***********/
182         lb.lbStyle = BS_SOLID;
183         lb.lbColor = pen[i].color;
184         lb.lbHatch = HS_CROSS; /* just in case */
185         SetLastError(0xdeadbeef);
186         hpen = ExtCreatePen(pen[i].style, pen[i].width, &lb, 2, user_style);
187         if (pen[i].style != PS_USERSTYLE)
188         {
189             ok(hpen == 0, "ExtCreatePen should fail\n");
190             ok(GetLastError() == ERROR_INVALID_PARAMETER,
191                "wrong last error value %d\n", GetLastError());
192             SetLastError(0xdeadbeef);
193             hpen = ExtCreatePen(pen[i].style, pen[i].width, &lb, 0, NULL);
194             if (pen[i].style != PS_NULL)
195             {
196                 ok(hpen == 0, "ExtCreatePen with width != 1 should fail\n");
197                 ok(GetLastError() == ERROR_INVALID_PARAMETER,
198                    "wrong last error value %d\n", GetLastError());
199
200                 SetLastError(0xdeadbeef);
201                 hpen = ExtCreatePen(pen[i].style, 1, &lb, 0, NULL);
202             }
203         }
204         else
205         {
206             ok(hpen == 0, "ExtCreatePen with width != 1 should fail\n");
207             ok(GetLastError() == ERROR_INVALID_PARAMETER,
208                "wrong last error value %d\n", GetLastError());
209             SetLastError(0xdeadbeef);
210             hpen = ExtCreatePen(pen[i].style, 1, &lb, 2, user_style);
211         }
212         if (pen[i].style == PS_INSIDEFRAME)
213         {
214             /* This style is applicable only for geometric pens */
215             ok(hpen == 0, "ExtCreatePen should fail\n");
216             goto test_geometric_pens;
217         }
218         ok(hpen != 0, "ExtCreatePen error %d\n", GetLastError());
219
220         obj_type = GetObjectType(hpen);
221         /* for some reason XP differentiates PS_NULL here */
222         if (pen[i].style == PS_NULL)
223         {
224             ok(obj_type == OBJ_PEN, "wrong object type %u\n", obj_type);
225             ok(hpen == GetStockObject(NULL_PEN), "hpen should be a stock NULL_PEN\n");
226         }
227         else
228             ok(obj_type == OBJ_EXTPEN, "wrong object type %u\n", obj_type);
229
230         /* check what's the real size of the object */
231         SetLastError(0xdeadbeef);
232         size = GetObject(hpen, 0, NULL);
233         switch (pen[i].style)
234         {
235         case PS_NULL:
236             ok(size == sizeof(LOGPEN),
237                "GetObject returned %d, error %d\n", size, GetLastError());
238             break;
239
240         case PS_USERSTYLE:
241             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry) + sizeof(user_style) ||
242                broken(size == 0 && GetLastError() == ERROR_INVALID_PARAMETER), /* Win9x */
243                "GetObject returned %d, error %d\n", size, GetLastError());
244             break;
245
246         default:
247             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry) ||
248                broken(size == sizeof(LOGPEN)) || /* Win9x */
249                broken(pen[i].style == PS_ALTERNATE &&
250                       size == 0 &&
251                       GetLastError() == ERROR_INVALID_PARAMETER), /* Win9x */
252                "GetObject returned %d, error %d\n", size, GetLastError());
253             break;
254         }
255
256         /* ask for truncated data */
257         memset(&elp, 0xb0, sizeof(elp));
258         SetLastError(0xdeadbeef);
259         size = GetObject(hpen, sizeof(elp.elpPenStyle), &elp);
260         ok(!size ||
261             broken(size == sizeof(elp.elpPenStyle)), /* Win9x */
262             "GetObject should fail: size %d, error %d\n", size, GetLastError());
263
264         /* see how larger buffer sizes are handled */
265         memset(&ext_pen, 0xb0, sizeof(ext_pen));
266         SetLastError(0xdeadbeef);
267         size = GetObject(hpen, sizeof(ext_pen), &ext_pen.elp);
268         switch (pen[i].style)
269         {
270         case PS_NULL:
271             ok(size == sizeof(LOGPEN),
272                "GetObject returned %d, error %d\n", size, GetLastError());
273             memcpy(&lp, &ext_pen.elp, sizeof(lp));
274             ok(lp.lopnStyle == pen[i].ret_style, "expected %u, got %u\n", pen[i].ret_style, lp.lopnStyle);
275             ok(lp.lopnWidth.x == pen[i].ret_width, "expected %u, got %d\n", pen[i].ret_width, lp.lopnWidth.x);
276             ok(lp.lopnWidth.y == 0, "expected 0, got %d\n", lp.lopnWidth.y);
277             ok(lp.lopnColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, lp.lopnColor);
278
279             /* for PS_NULL it also works this way */
280             memset(&elp, 0xb0, sizeof(elp));
281             memset(&unset_hatch, 0xb0, sizeof(unset_hatch));
282             SetLastError(0xdeadbeef);
283             size = GetObject(hpen, sizeof(elp), &elp);
284             ok(size == sizeof(EXTLOGPEN),
285                 "GetObject returned %d, error %d\n", size, GetLastError());
286             ok(ext_pen.elp.elpHatch == unset_hatch, "expected 0xb0b0b0b0, got %p\n", (void *)ext_pen.elp.elpHatch);
287             ok(ext_pen.elp.elpNumEntries == 0xb0b0b0b0, "expected 0xb0b0b0b0, got %x\n", ext_pen.elp.elpNumEntries);
288             break;
289
290         case PS_USERSTYLE:
291             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry) + sizeof(user_style),
292                "GetObject returned %d, error %d\n", size, GetLastError());
293             ok(ext_pen.elp.elpHatch == HS_CROSS, "expected HS_CROSS, got %p\n", (void *)ext_pen.elp.elpHatch);
294             ok(ext_pen.elp.elpNumEntries == 2, "expected 0, got %x\n", ext_pen.elp.elpNumEntries);
295             ok(ext_pen.elp.elpStyleEntry[0] == 0xabc, "expected 0xabc, got %x\n", ext_pen.elp.elpStyleEntry[0]);
296             ok(ext_pen.elp.elpStyleEntry[1] == 0xdef, "expected 0xdef, got %x\n", ext_pen.elp.elpStyleEntry[1]);
297             break;
298
299         default:
300             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry) ||
301                broken(size == sizeof(LOGPEN)) || /* Win9x */
302                broken(pen[i].style == PS_ALTERNATE &&
303                       size == 0 &&
304                       GetLastError() == ERROR_INVALID_PARAMETER), /* Win9x */
305                "GetObject returned %d, error %d\n", size, GetLastError());
306             ok(ext_pen.elp.elpHatch == HS_CROSS, "expected HS_CROSS, got %p\n", (void *)ext_pen.elp.elpHatch);
307             ok(ext_pen.elp.elpNumEntries == 0, "expected 0, got %x\n", ext_pen.elp.elpNumEntries);
308             break;
309         }
310
311 if (pen[i].style == PS_USERSTYLE)
312 {
313     todo_wine
314         ok(ext_pen.elp.elpPenStyle == pen[i].style, "expected %x, got %x\n", pen[i].style, ext_pen.elp.elpPenStyle);
315 }
316 else
317         ok(ext_pen.elp.elpPenStyle == pen[i].style, "expected %x, got %x\n", pen[i].style, ext_pen.elp.elpPenStyle);
318         ok(ext_pen.elp.elpWidth == 1, "expected 1, got %x\n", ext_pen.elp.elpWidth);
319         ok(ext_pen.elp.elpColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, ext_pen.elp.elpColor);
320         ok(ext_pen.elp.elpBrushStyle == BS_SOLID, "expected BS_SOLID, got %x\n", ext_pen.elp.elpBrushStyle);
321
322         DeleteObject(hpen);
323
324 test_geometric_pens:
325         /********************** geometric pens **********************/
326         lb.lbStyle = BS_SOLID;
327         lb.lbColor = pen[i].color;
328         lb.lbHatch = HS_CROSS; /* just in case */
329         SetLastError(0xdeadbeef);
330         hpen = ExtCreatePen(PS_GEOMETRIC | pen[i].style, pen[i].width, &lb, 2, user_style);
331         if (pen[i].style != PS_USERSTYLE)
332         {
333             ok(hpen == 0, "ExtCreatePen should fail\n");
334             SetLastError(0xdeadbeef);
335             hpen = ExtCreatePen(PS_GEOMETRIC | pen[i].style, pen[i].width, &lb, 0, NULL);
336         }
337         if (pen[i].style == PS_ALTERNATE)
338         {
339             /* This style is applicable only for cosmetic pens */
340             ok(hpen == 0, "ExtCreatePen should fail\n");
341             continue;
342         }
343         ok(hpen != 0, "ExtCreatePen error %d\n", GetLastError());
344
345         obj_type = GetObjectType(hpen);
346         /* for some reason XP differentiates PS_NULL here */
347         if (pen[i].style == PS_NULL)
348             ok(obj_type == OBJ_PEN, "wrong object type %u\n", obj_type);
349         else
350             ok(obj_type == OBJ_EXTPEN, "wrong object type %u\n", obj_type);
351
352         /* check what's the real size of the object */
353         size = GetObject(hpen, 0, NULL);
354         switch (pen[i].style)
355         {
356         case PS_NULL:
357             ok(size == sizeof(LOGPEN),
358                "GetObject returned %d, error %d\n", size, GetLastError());
359             break;
360
361         case PS_USERSTYLE:
362             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry) + sizeof(user_style),
363                "GetObject returned %d, error %d\n", size, GetLastError());
364             break;
365
366         default:
367             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry),
368                "GetObject returned %d, error %d\n", size, GetLastError());
369             break;
370         }
371
372         /* ask for truncated data */
373         memset(&lp, 0xb0, sizeof(lp));
374         SetLastError(0xdeadbeef);
375         size = GetObject(hpen, sizeof(lp.lopnStyle), &lp);
376         ok(!size, "GetObject should fail: size %d, error %d\n", size, GetLastError());
377
378         memset(&lp, 0xb0, sizeof(lp));
379         SetLastError(0xdeadbeef);
380         size = GetObject(hpen, sizeof(lp), &lp);
381         /* for some reason XP differentiates PS_NULL here */
382         if (pen[i].style == PS_NULL)
383         {
384             ok(size == sizeof(LOGPEN), "GetObject returned %d, error %d\n", size, GetLastError());
385             ok(lp.lopnStyle == pen[i].ret_style, "expected %u, got %u\n", pen[i].ret_style, lp.lopnStyle);
386             ok(lp.lopnWidth.x == pen[i].ret_width, "expected %u, got %d\n", pen[i].ret_width, lp.lopnWidth.x);
387             ok(lp.lopnWidth.y == 0, "expected 0, got %d\n", lp.lopnWidth.y);
388             ok(lp.lopnColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, lp.lopnColor);
389         }
390         else
391             /* XP doesn't set last error here */
392             ok(!size /*&& GetLastError() == ERROR_INVALID_PARAMETER*/,
393                "GetObject should fail: size %d, error %d\n", size, GetLastError());
394
395         memset(&ext_pen, 0xb0, sizeof(ext_pen));
396         SetLastError(0xdeadbeef);
397         /* buffer is too small for user styles */
398         size = GetObject(hpen, sizeof(elp), &ext_pen.elp);
399         switch (pen[i].style)
400         {
401         case PS_NULL:
402             ok(size == sizeof(EXTLOGPEN),
403                 "GetObject returned %d, error %d\n", size, GetLastError());
404             ok(ext_pen.elp.elpHatch == 0, "expected 0, got %p\n", (void *)ext_pen.elp.elpHatch);
405             ok(ext_pen.elp.elpNumEntries == 0, "expected 0, got %x\n", ext_pen.elp.elpNumEntries);
406
407             /* for PS_NULL it also works this way */
408             SetLastError(0xdeadbeef);
409             size = GetObject(hpen, sizeof(ext_pen), &lp);
410             ok(size == sizeof(LOGPEN),
411                 "GetObject returned %d, error %d\n", size, GetLastError());
412             ok(lp.lopnStyle == pen[i].ret_style, "expected %u, got %u\n", pen[i].ret_style, lp.lopnStyle);
413             ok(lp.lopnWidth.x == pen[i].ret_width, "expected %u, got %d\n", pen[i].ret_width, lp.lopnWidth.x);
414             ok(lp.lopnWidth.y == 0, "expected 0, got %d\n", lp.lopnWidth.y);
415             ok(lp.lopnColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, lp.lopnColor);
416             break;
417
418         case PS_USERSTYLE:
419             ok(!size /*&& GetLastError() == ERROR_INVALID_PARAMETER*/,
420                "GetObject should fail: size %d, error %d\n", size, GetLastError());
421             size = GetObject(hpen, sizeof(ext_pen), &ext_pen.elp);
422             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry) + sizeof(user_style),
423                "GetObject returned %d, error %d\n", size, GetLastError());
424             ok(ext_pen.elp.elpHatch == HS_CROSS, "expected HS_CROSS, got %p\n", (void *)ext_pen.elp.elpHatch);
425             ok(ext_pen.elp.elpNumEntries == 2, "expected 0, got %x\n", ext_pen.elp.elpNumEntries);
426             ok(ext_pen.elp.elpStyleEntry[0] == 0xabc, "expected 0xabc, got %x\n", ext_pen.elp.elpStyleEntry[0]);
427             ok(ext_pen.elp.elpStyleEntry[1] == 0xdef, "expected 0xdef, got %x\n", ext_pen.elp.elpStyleEntry[1]);
428             break;
429
430         default:
431             ok(size == sizeof(EXTLOGPEN) - sizeof(elp.elpStyleEntry),
432                "GetObject returned %d, error %d\n", size, GetLastError());
433             ok(ext_pen.elp.elpHatch == HS_CROSS, "expected HS_CROSS, got %p\n", (void *)ext_pen.elp.elpHatch);
434             ok(ext_pen.elp.elpNumEntries == 0, "expected 0, got %x\n", ext_pen.elp.elpNumEntries);
435             break;
436         }
437
438         /* for some reason XP differentiates PS_NULL here */
439         if (pen[i].style == PS_NULL)
440             ok(ext_pen.elp.elpPenStyle == pen[i].ret_style, "expected %x, got %x\n", pen[i].ret_style, ext_pen.elp.elpPenStyle);
441         else
442         {
443             ok(ext_pen.elp.elpPenStyle == (PS_GEOMETRIC | pen[i].style), "expected %x, got %x\n", PS_GEOMETRIC | pen[i].style, ext_pen.elp.elpPenStyle);
444         }
445
446         if (pen[i].style == PS_NULL)
447             ok(ext_pen.elp.elpWidth == 0, "expected 0, got %x\n", ext_pen.elp.elpWidth);
448         else
449             ok(ext_pen.elp.elpWidth == pen[i].ret_width, "expected %u, got %x\n", pen[i].ret_width, ext_pen.elp.elpWidth);
450         ok(ext_pen.elp.elpColor == pen[i].ret_color, "expected %08x, got %08x\n", pen[i].ret_color, ext_pen.elp.elpColor);
451         ok(ext_pen.elp.elpBrushStyle == BS_SOLID, "expected BS_SOLID, got %x\n", ext_pen.elp.elpBrushStyle);
452
453         DeleteObject(hpen);
454     }
455 }
456
457 static unsigned int atoi2(const char *s)
458 {
459     unsigned int ret = 0;
460     while(*s) ret = (ret << 1) | (*s++ == '1');
461     return ret;
462 }
463
464 #define TEST_LINE(x1, x2, z) \
465     { int buf = 0; \
466       SetBitmapBits(bmp, sizeof(buf), &buf); \
467       MoveToEx(hdc, x1, 0, NULL); \
468       LineTo(hdc, x2, 0); \
469       GetBitmapBits(bmp, sizeof(buf), &buf); \
470       expect(atoi2(z), buf); }
471
472 static void test_ps_alternate(void)
473 {
474     HDC hdc;
475     HBITMAP bmp;
476     HPEN pen;
477     LOGBRUSH lb;
478     INT iRet;
479     HGDIOBJ hRet;
480
481     lb.lbStyle = BS_SOLID;
482     lb.lbColor = RGB(0xff,0xff,0xff);
483
484     SetLastError(0xdeadbeef);
485     pen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, NULL);
486     if(pen == NULL && GetLastError() == 0xdeadbeef) {
487         skip("looks like 9x, skipping PS_ALTERNATE tests\n");
488         return;
489     }
490     ok(pen != NULL, "gle=%d\n", GetLastError());
491     hdc = CreateCompatibleDC(NULL);
492     ok(hdc != NULL, "gle=%d\n", GetLastError());
493     bmp = CreateBitmap(8, 1, 1, 1, NULL);
494     ok(bmp != NULL, "gle=%d\n", GetLastError());
495     hRet = SelectObject(hdc, bmp);
496     ok(hRet != NULL, "gle=%d\n", GetLastError());
497     hRet = SelectObject(hdc, pen);
498     ok(hRet != NULL, "gle=%d\n", GetLastError());
499     iRet = SetBkMode(hdc, TRANSPARENT);
500     ok(iRet, "gle=%d\n", GetLastError());
501
502     TEST_LINE(0, 1, "10000000")
503     TEST_LINE(0, 2, "10000000")
504     TEST_LINE(0, 3, "10100000")
505     TEST_LINE(0, 4, "10100000")
506     TEST_LINE(1, 4, "01010000")
507     TEST_LINE(1, 5, "01010000")
508     TEST_LINE(4, 8, "00001010")
509
510     DeleteObject(pen);
511     DeleteObject(bmp);
512     DeleteDC(hdc);
513 }
514
515 static void test_ps_userstyle(void)
516 {
517     static DWORD style[17] = {0, 2, 0, 4, 5, 0, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 17};
518     static DWORD bad_style[5] = {0, 0, 0, 0, 0};
519     static DWORD bad_style2[5] = {4, 7, 8, 3, -1};
520
521     LOGBRUSH lb;
522     HPEN pen;
523     INT size, i;
524
525     struct
526     {
527         EXTLOGPEN elp;
528         DWORD style_data[15];
529     } ext_pen;
530
531     lb.lbColor = 0x00ff0000;
532     lb.lbStyle = BS_SOLID;
533     lb.lbHatch = 0;
534
535     pen = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, 50, &lb, 3, NULL);
536     ok(pen == 0, "ExtCreatePen should fail\n");
537     if (pen == 0 && GetLastError() == 0xdeadbeef)
538     {
539         win_skip("Looks like 9x, skipping PS_USERSTYLE tests\n");
540         return;
541     }
542     expect(ERROR_INVALID_PARAMETER, GetLastError());
543     DeleteObject(pen);
544     SetLastError(0xdeadbeef);
545
546     pen = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, 50, &lb, 0, style);
547     ok(pen == 0, "ExtCreatePen should fail\n");
548     expect2(0xdeadbeef, ERROR_INVALID_PARAMETER, GetLastError());
549     DeleteObject(pen);
550     SetLastError(0xdeadbeef);
551
552     pen = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, 50, &lb, 17, style);
553     ok(pen == 0, "ExtCreatePen should fail\n");
554     expect(ERROR_INVALID_PARAMETER, GetLastError());
555     DeleteObject(pen);
556     SetLastError(0xdeadbeef);
557
558     pen = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, 50, &lb, -1, style);
559     ok(pen == 0, "ExtCreatePen should fail\n");
560     expect(0xdeadbeef, GetLastError());
561     DeleteObject(pen);
562     SetLastError(0xdeadbeef);
563
564     pen = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, 50, &lb, 5, bad_style);
565     ok(pen == 0, "ExtCreatePen should fail\n");
566     expect(ERROR_INVALID_PARAMETER, GetLastError());
567     DeleteObject(pen);
568     SetLastError(0xdeadbeef);
569
570     pen = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, 50, &lb, 5, bad_style2);
571     ok(pen == 0, "ExtCreatePen should fail\n");
572     expect(ERROR_INVALID_PARAMETER, GetLastError());
573     DeleteObject(pen);
574     SetLastError(0xdeadbeef);
575
576     pen = ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, 50, &lb, 16, style);
577     ok(pen != 0, "ExtCreatePen should not fail\n");
578
579     size = GetObject(pen, sizeof(ext_pen), &ext_pen);
580     expect(FIELD_OFFSET(EXTLOGPEN,elpStyleEntry[16]), size);
581
582     for(i = 0; i < 16; i++)
583         expect(style[i], ext_pen.elp.elpStyleEntry[i]);
584
585     DeleteObject(pen);
586 }
587
588 START_TEST(pen)
589 {
590     test_logpen();
591     test_ps_alternate();
592     test_ps_userstyle();
593 }