winemac.drv: Implement GetMonitorInfo.
[wine] / dlls / winex11.drv / graphics.c
1 /*
2  * X11 graphics driver graphics functions
3  *
4  * Copyright 1993,1994 Alexandre Julliard
5  * Copyright 1998 Huw Davies
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 /*
23  * FIXME: only some of these functions obey the GM_ADVANCED
24  * graphics mode
25  */
26
27 #include "config.h"
28
29 #include <stdarg.h>
30 #include <math.h>
31 #ifdef HAVE_FLOAT_H
32 # include <float.h>
33 #endif
34 #include <stdlib.h>
35 #ifndef PI
36 #define PI M_PI
37 #endif
38 #include <string.h>
39 #include <limits.h>
40
41 #include "windef.h"
42 #include "winbase.h"
43 #include "winreg.h"
44
45 #include "x11drv.h"
46 #include "wine/debug.h"
47 #include "wine/unicode.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(graphics);
50
51 #define ABS(x)    ((x)<0?(-(x)):(x))
52
53   /* ROP code to GC function conversion */
54 const int X11DRV_XROPfunction[16] =
55 {
56     GXclear,        /* R2_BLACK */
57     GXnor,          /* R2_NOTMERGEPEN */
58     GXandInverted,  /* R2_MASKNOTPEN */
59     GXcopyInverted, /* R2_NOTCOPYPEN */
60     GXandReverse,   /* R2_MASKPENNOT */
61     GXinvert,       /* R2_NOT */
62     GXxor,          /* R2_XORPEN */
63     GXnand,         /* R2_NOTMASKPEN */
64     GXand,          /* R2_MASKPEN */
65     GXequiv,        /* R2_NOTXORPEN */
66     GXnoop,         /* R2_NOP */
67     GXorInverted,   /* R2_MERGENOTPEN */
68     GXcopy,         /* R2_COPYPEN */
69     GXorReverse,    /* R2_MERGEPENNOT */
70     GXor,           /* R2_MERGEPEN */
71     GXset           /* R2_WHITE */
72 };
73
74
75 /* get the rectangle in device coordinates, with optional mirroring */
76 static RECT get_device_rect( HDC hdc, int left, int top, int right, int bottom )
77 {
78     RECT rect;
79
80     rect.left   = left;
81     rect.top    = top;
82     rect.right  = right;
83     rect.bottom = bottom;
84     if (GetLayout( hdc ) & LAYOUT_RTL)
85     {
86         /* shift the rectangle so that the right border is included after mirroring */
87         /* it would be more correct to do this after LPtoDP but that's not what Windows does */
88         rect.left--;
89         rect.right--;
90     }
91     LPtoDP( hdc, (POINT *)&rect, 2 );
92     if (rect.left > rect.right)
93     {
94         int tmp = rect.left;
95         rect.left = rect.right;
96         rect.right = tmp;
97     }
98     if (rect.top > rect.bottom)
99     {
100         int tmp = rect.top;
101         rect.top = rect.bottom;
102         rect.bottom = tmp;
103     }
104     return rect;
105 }
106
107 static void add_pen_device_bounds( X11DRV_PDEVICE *dev, const POINT *points, int count )
108 {
109     RECT bounds, rect;
110     int width = 0;
111
112     if (!dev->bounds) return;
113     reset_bounds( &bounds );
114
115     if (dev->pen.type & PS_GEOMETRIC || dev->pen.width > 1)
116     {
117         /* Windows uses some heuristics to estimate the distance from the point that will be painted */
118         width = dev->pen.width + 2;
119         if (dev->pen.linejoin == PS_JOIN_MITER)
120         {
121             width *= 5;
122             if (dev->pen.endcap == PS_ENDCAP_SQUARE) width = (width * 3 + 1) / 2;
123         }
124         else
125         {
126             if (dev->pen.endcap == PS_ENDCAP_SQUARE) width -= width / 4;
127             else width = (width + 1) / 2;
128         }
129     }
130
131     while (count-- > 0)
132     {
133         rect.left   = points->x - width;
134         rect.top    = points->y - width;
135         rect.right  = points->x + width + 1;
136         rect.bottom = points->y + width + 1;
137         add_bounds_rect( &bounds, &rect );
138         points++;
139     }
140
141     add_device_bounds( dev, &bounds );
142 }
143
144 /***********************************************************************
145  *           X11DRV_GetRegionData
146  *
147  * Calls GetRegionData on the given region and converts the rectangle
148  * array to XRectangle format. The returned buffer must be freed by
149  * caller using HeapFree(GetProcessHeap(),...).
150  * If hdc_lptodp is not 0, the rectangles are converted through LPtoDP.
151  */
152 RGNDATA *X11DRV_GetRegionData( HRGN hrgn, HDC hdc_lptodp )
153 {
154     RGNDATA *data;
155     DWORD size;
156     unsigned int i;
157     RECT *rect, tmp;
158     XRectangle *xrect;
159
160     if (!(size = GetRegionData( hrgn, 0, NULL ))) return NULL;
161     if (sizeof(XRectangle) > sizeof(RECT))
162     {
163         /* add extra size for XRectangle array */
164         int count = (size - sizeof(RGNDATAHEADER)) / sizeof(RECT);
165         size += count * (sizeof(XRectangle) - sizeof(RECT));
166     }
167     if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
168     if (!GetRegionData( hrgn, size, data ))
169     {
170         HeapFree( GetProcessHeap(), 0, data );
171         return NULL;
172     }
173
174     rect = (RECT *)data->Buffer;
175     xrect = (XRectangle *)data->Buffer;
176     if (hdc_lptodp)  /* map to device coordinates */
177     {
178         LPtoDP( hdc_lptodp, (POINT *)rect, data->rdh.nCount * 2 );
179         for (i = 0; i < data->rdh.nCount; i++)
180         {
181             if (rect[i].right < rect[i].left)
182             {
183                 INT tmp = rect[i].right;
184                 rect[i].right = rect[i].left;
185                 rect[i].left = tmp;
186             }
187             if (rect[i].bottom < rect[i].top)
188             {
189                 INT tmp = rect[i].bottom;
190                 rect[i].bottom = rect[i].top;
191                 rect[i].top = tmp;
192             }
193         }
194     }
195
196     if (sizeof(XRectangle) > sizeof(RECT))
197     {
198         int j;
199         /* need to start from the end */
200         xrect += data->rdh.nCount;
201         for (j = data->rdh.nCount-1; j >= 0; j--)
202         {
203             tmp = rect[j];
204             if (tmp.left > SHRT_MAX) continue;
205             if (tmp.top > SHRT_MAX) continue;
206             if (tmp.right < SHRT_MIN) continue;
207             if (tmp.bottom < SHRT_MIN) continue;
208             xrect--;
209             xrect->x      = max( min( tmp.left, SHRT_MAX), SHRT_MIN);
210             xrect->y      = max( min( tmp.top, SHRT_MAX), SHRT_MIN);
211             xrect->width  = max( min( tmp.right, SHRT_MAX ) - xrect->x, 0);
212             xrect->height = max( min( tmp.bottom, SHRT_MAX ) - xrect->y, 0);
213         }
214         if (xrect > (XRectangle *)data->Buffer)
215         {
216             data->rdh.nCount -= xrect - (XRectangle *)data->Buffer;
217             memmove( (XRectangle *)data->Buffer, xrect, data->rdh.nCount * sizeof(*xrect) );
218         }
219     }
220     else
221     {
222         for (i = 0; i < data->rdh.nCount; i++)
223         {
224             tmp = rect[i];
225             if (tmp.left > SHRT_MAX) continue;
226             if (tmp.top > SHRT_MAX) continue;
227             if (tmp.right < SHRT_MIN) continue;
228             if (tmp.bottom < SHRT_MIN) continue;
229             xrect->x      = max( min( tmp.left, SHRT_MAX), SHRT_MIN);
230             xrect->y      = max( min( tmp.top, SHRT_MAX), SHRT_MIN);
231             xrect->width  = max( min( tmp.right, SHRT_MAX ) - xrect->x, 0);
232             xrect->height = max( min( tmp.bottom, SHRT_MAX ) - xrect->y, 0);
233             xrect++;
234         }
235         data->rdh.nCount = xrect - (XRectangle *)data->Buffer;
236     }
237     return data;
238 }
239
240
241 /***********************************************************************
242  *           update_x11_clipping
243  */
244 static void update_x11_clipping( X11DRV_PDEVICE *physDev, HRGN rgn )
245 {
246     RGNDATA *data;
247
248     if (!rgn)
249     {
250         XSetClipMask( gdi_display, physDev->gc, None );
251     }
252     else if ((data = X11DRV_GetRegionData( rgn, 0 )))
253     {
254         XSetClipRectangles( gdi_display, physDev->gc, physDev->dc_rect.left, physDev->dc_rect.top,
255                             (XRectangle *)data->Buffer, data->rdh.nCount, YXBanded );
256         HeapFree( GetProcessHeap(), 0, data );
257     }
258 }
259
260
261 /***********************************************************************
262  *           add_extra_clipping_region
263  *
264  * Temporarily add a region to the current clipping region.
265  * The region must be restored with restore_clipping_region.
266  */
267 BOOL add_extra_clipping_region( X11DRV_PDEVICE *dev, HRGN rgn )
268 {
269     HRGN clip;
270
271     if (!rgn) return FALSE;
272     if (dev->region)
273     {
274         if (!(clip = CreateRectRgn( 0, 0, 0, 0 ))) return FALSE;
275         CombineRgn( clip, dev->region, rgn, RGN_AND );
276         update_x11_clipping( dev, clip );
277         DeleteObject( clip );
278     }
279     else update_x11_clipping( dev, rgn );
280     return TRUE;
281 }
282
283
284 /***********************************************************************
285  *           restore_clipping_region
286  */
287 void restore_clipping_region( X11DRV_PDEVICE *dev )
288 {
289     update_x11_clipping( dev, dev->region );
290 }
291
292
293 /***********************************************************************
294  *           X11DRV_SetDeviceClipping
295  */
296 void X11DRV_SetDeviceClipping( PHYSDEV dev, HRGN rgn )
297 {
298     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
299
300     physDev->region = rgn;
301     update_x11_clipping( physDev, rgn );
302 }
303
304
305 /***********************************************************************
306  *           X11DRV_SetupGCForPatBlt
307  *
308  * Setup the GC for a PatBlt operation using current brush.
309  * If fMapColors is TRUE, X pixels are mapped to Windows colors.
310  * Return FALSE if brush is BS_NULL, TRUE otherwise.
311  */
312 BOOL X11DRV_SetupGCForPatBlt( X11DRV_PDEVICE *physDev, GC gc, BOOL fMapColors )
313 {
314     XGCValues val;
315     unsigned long mask;
316     Pixmap pixmap = 0;
317     POINT pt;
318
319     if (physDev->brush.style == BS_NULL) return FALSE;
320     if (physDev->brush.pixel == -1)
321     {
322         /* Special case used for monochrome pattern brushes.
323          * We need to swap foreground and background because
324          * Windows does it the wrong way...
325          */
326         val.foreground = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
327         val.background = X11DRV_PALETTE_ToPhysical( physDev, GetTextColor(physDev->dev.hdc) );
328     }
329     else
330     {
331         val.foreground = physDev->brush.pixel;
332         val.background = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
333     }
334     if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
335     {
336         val.foreground = X11DRV_PALETTE_XPixelToPalette[val.foreground];
337         val.background = X11DRV_PALETTE_XPixelToPalette[val.background];
338     }
339
340     val.function = X11DRV_XROPfunction[GetROP2(physDev->dev.hdc)-1];
341     /*
342     ** Let's replace GXinvert by GXxor with (black xor white)
343     ** This solves the selection color and leak problems in excel
344     ** FIXME : Let's do that only if we work with X-pixels, not with Win-pixels
345     */
346     if (val.function == GXinvert)
347     {
348         val.foreground = (WhitePixel( gdi_display, DefaultScreen(gdi_display) ) ^
349                           BlackPixel( gdi_display, DefaultScreen(gdi_display) ));
350         val.function = GXxor;
351     }
352     val.fill_style = physDev->brush.fillStyle;
353     switch(val.fill_style)
354     {
355     case FillStippled:
356     case FillOpaqueStippled:
357         if (GetBkMode(physDev->dev.hdc)==OPAQUE) val.fill_style = FillOpaqueStippled;
358         val.stipple = physDev->brush.pixmap;
359         mask = GCStipple;
360         break;
361
362     case FillTiled:
363         if (fMapColors && X11DRV_PALETTE_XPixelToPalette)
364         {
365             register int x, y;
366             XImage *image;
367             pixmap = XCreatePixmap( gdi_display, root_window, 8, 8, physDev->depth );
368             image = XGetImage( gdi_display, physDev->brush.pixmap, 0, 0, 8, 8,
369                                AllPlanes, ZPixmap );
370             for (y = 0; y < 8; y++)
371                 for (x = 0; x < 8; x++)
372                     XPutPixel( image, x, y,
373                                X11DRV_PALETTE_XPixelToPalette[XGetPixel( image, x, y)] );
374             XPutImage( gdi_display, pixmap, gc, image, 0, 0, 0, 0, 8, 8 );
375             XDestroyImage( image );
376             val.tile = pixmap;
377         }
378         else val.tile = physDev->brush.pixmap;
379         mask = GCTile;
380         break;
381
382     default:
383         mask = 0;
384         break;
385     }
386     GetBrushOrgEx( physDev->dev.hdc, &pt );
387     val.ts_x_origin = physDev->dc_rect.left + pt.x;
388     val.ts_y_origin = physDev->dc_rect.top + pt.y;
389     val.fill_rule = (GetPolyFillMode(physDev->dev.hdc) == WINDING) ? WindingRule : EvenOddRule;
390     XChangeGC( gdi_display, gc,
391                GCFunction | GCForeground | GCBackground | GCFillStyle |
392                GCFillRule | GCTileStipXOrigin | GCTileStipYOrigin | mask,
393                &val );
394     if (pixmap) XFreePixmap( gdi_display, pixmap );
395     return TRUE;
396 }
397
398
399 /***********************************************************************
400  *           X11DRV_SetupGCForBrush
401  *
402  * Setup physDev->gc for drawing operations using current brush.
403  * Return FALSE if brush is BS_NULL, TRUE otherwise.
404  */
405 BOOL X11DRV_SetupGCForBrush( X11DRV_PDEVICE *physDev )
406 {
407     return X11DRV_SetupGCForPatBlt( physDev, physDev->gc, FALSE );
408 }
409
410
411 /***********************************************************************
412  *           X11DRV_SetupGCForPen
413  *
414  * Setup physDev->gc for drawing operations using current pen.
415  * Return FALSE if pen is PS_NULL, TRUE otherwise.
416  */
417 static BOOL X11DRV_SetupGCForPen( X11DRV_PDEVICE *physDev )
418 {
419     XGCValues val;
420     UINT rop2 = GetROP2(physDev->dev.hdc);
421
422     if (physDev->pen.style == PS_NULL) return FALSE;
423
424     switch (rop2)
425     {
426     case R2_BLACK :
427         val.foreground = BlackPixel( gdi_display, DefaultScreen(gdi_display) );
428         val.function = GXcopy;
429         break;
430     case R2_WHITE :
431         val.foreground = WhitePixel( gdi_display, DefaultScreen(gdi_display) );
432         val.function = GXcopy;
433         break;
434     case R2_XORPEN :
435         val.foreground = physDev->pen.pixel;
436         /* It is very unlikely someone wants to XOR with 0 */
437         /* This fixes the rubber-drawings in paintbrush */
438         if (val.foreground == 0)
439             val.foreground = (WhitePixel( gdi_display, DefaultScreen(gdi_display) ) ^
440                               BlackPixel( gdi_display, DefaultScreen(gdi_display) ));
441         val.function = GXxor;
442         break;
443     default :
444         val.foreground = physDev->pen.pixel;
445         val.function   = X11DRV_XROPfunction[rop2-1];
446     }
447     val.background = X11DRV_PALETTE_ToPhysical( physDev, GetBkColor(physDev->dev.hdc) );
448     val.fill_style = FillSolid;
449     val.line_width = physDev->pen.width;
450     if (val.line_width <= 1) {
451         val.cap_style = CapNotLast;
452     } else {
453         switch (physDev->pen.endcap)
454         {
455         case PS_ENDCAP_SQUARE:
456             val.cap_style = CapProjecting;
457             break;
458         case PS_ENDCAP_FLAT:
459             val.cap_style = CapButt;
460             break;
461         case PS_ENDCAP_ROUND:
462         default:
463             val.cap_style = CapRound;
464         }
465     }
466     switch (physDev->pen.linejoin)
467     {
468     case PS_JOIN_BEVEL:
469         val.join_style = JoinBevel;
470         break;
471     case PS_JOIN_MITER:
472         val.join_style = JoinMiter;
473         break;
474     case PS_JOIN_ROUND:
475     default:
476         val.join_style = JoinRound;
477     }
478
479     if (physDev->pen.dash_len)
480         val.line_style = ((GetBkMode(physDev->dev.hdc) == OPAQUE) && (!physDev->pen.ext))
481                          ? LineDoubleDash : LineOnOffDash;
482     else
483         val.line_style = LineSolid;
484
485     if (physDev->pen.dash_len)
486         XSetDashes( gdi_display, physDev->gc, 0, physDev->pen.dashes, physDev->pen.dash_len );
487     XChangeGC( gdi_display, physDev->gc,
488                GCFunction | GCForeground | GCBackground | GCLineWidth |
489                GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle, &val );
490     return TRUE;
491 }
492
493
494 /***********************************************************************
495  *           X11DRV_XWStoDS
496  *
497  * Performs a world-to-viewport transformation on the specified width.
498  */
499 INT X11DRV_XWStoDS( HDC hdc, INT width )
500 {
501     POINT pt[2];
502
503     pt[0].x = 0;
504     pt[0].y = 0;
505     pt[1].x = width;
506     pt[1].y = 0;
507     LPtoDP( hdc, pt, 2 );
508     return pt[1].x - pt[0].x;
509 }
510
511 /***********************************************************************
512  *           X11DRV_YWStoDS
513  *
514  * Performs a world-to-viewport transformation on the specified height.
515  */
516 INT X11DRV_YWStoDS( HDC hdc, INT height )
517 {
518     POINT pt[2];
519
520     pt[0].x = 0;
521     pt[0].y = 0;
522     pt[1].x = 0;
523     pt[1].y = height;
524     LPtoDP( hdc, pt, 2 );
525     return pt[1].y - pt[0].y;
526 }
527
528 /***********************************************************************
529  *           X11DRV_LineTo
530  */
531 BOOL X11DRV_LineTo( PHYSDEV dev, INT x, INT y )
532 {
533     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
534     POINT pt[2];
535
536     GetCurrentPositionEx( dev->hdc, &pt[0] );
537     pt[1].x = x;
538     pt[1].y = y;
539     LPtoDP( dev->hdc, pt, 2 );
540     add_pen_device_bounds( physDev, pt, 2 );
541
542     if (X11DRV_SetupGCForPen( physDev ))
543         XDrawLine(gdi_display, physDev->drawable, physDev->gc,
544                   physDev->dc_rect.left + pt[0].x, physDev->dc_rect.top + pt[0].y,
545                   physDev->dc_rect.left + pt[1].x, physDev->dc_rect.top + pt[1].y );
546     return TRUE;
547 }
548
549
550
551 /***********************************************************************
552  *           X11DRV_DrawArc
553  *
554  * Helper functions for Arc(), Chord() and Pie().
555  * 'lines' is the number of lines to draw: 0 for Arc, 1 for Chord, 2 for Pie.
556  *
557  */
558 static BOOL X11DRV_DrawArc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
559                             INT xstart, INT ystart, INT xend, INT yend, INT lines )
560 {
561     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
562     INT xcenter, ycenter, istart_angle, idiff_angle;
563     INT width, oldwidth;
564     double start_angle, end_angle;
565     XPoint points[4];
566     POINT start, end;
567     RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
568
569     start.x = xstart;
570     start.y = ystart;
571     end.x = xend;
572     end.y = yend;
573     LPtoDP(dev->hdc, &start, 1);
574     LPtoDP(dev->hdc, &end, 1);
575
576     if ((rc.left == rc.right) || (rc.top == rc.bottom)
577             ||(lines && ((rc.right-rc.left==1)||(rc.bottom-rc.top==1)))) return TRUE;
578
579     if (GetArcDirection( dev->hdc ) == AD_CLOCKWISE)
580       { POINT tmp = start; start = end; end = tmp; }
581
582     oldwidth = width = physDev->pen.width;
583     if (!width) width = 1;
584     if(physDev->pen.style == PS_NULL) width = 0;
585
586     if (physDev->pen.style == PS_INSIDEFRAME)
587     {
588         if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
589         if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
590         rc.left   += width / 2;
591         rc.right  -= (width - 1) / 2;
592         rc.top    += width / 2;
593         rc.bottom -= (width - 1) / 2;
594     }
595     if(width == 0) width = 1; /* more accurate */
596     physDev->pen.width = width;
597
598     xcenter = (rc.right + rc.left) / 2;
599     ycenter = (rc.bottom + rc.top) / 2;
600     start_angle = atan2( (double)(ycenter-start.y)*(rc.right-rc.left),
601                          (double)(start.x-xcenter)*(rc.bottom-rc.top) );
602     end_angle   = atan2( (double)(ycenter-end.y)*(rc.right-rc.left),
603                          (double)(end.x-xcenter)*(rc.bottom-rc.top) );
604     if ((start.x==end.x)&&(start.y==end.y))
605       { /* A lazy program delivers xstart=xend=ystart=yend=0) */
606         start_angle = 0;
607         end_angle = 2* PI;
608       }
609     else /* notorious cases */
610       if ((start_angle == PI)&&( end_angle <0))
611         start_angle = - PI;
612     else
613       if ((end_angle == PI)&&( start_angle <0))
614         end_angle = - PI;
615     istart_angle = (INT)(start_angle * 180 * 64 / PI + 0.5);
616     idiff_angle  = (INT)((end_angle - start_angle) * 180 * 64 / PI + 0.5);
617     if (idiff_angle <= 0) idiff_angle += 360 * 64;
618
619       /* Fill arc with brush if Chord() or Pie() */
620
621     if ((lines > 0) && X11DRV_SetupGCForBrush( physDev )) {
622         XSetArcMode( gdi_display, physDev->gc, (lines==1) ? ArcChord : ArcPieSlice);
623         XFillArc( gdi_display, physDev->drawable, physDev->gc,
624                   physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
625                   rc.right-rc.left-1, rc.bottom-rc.top-1, istart_angle, idiff_angle );
626     }
627
628       /* Draw arc and lines */
629
630     if (X11DRV_SetupGCForPen( physDev ))
631     {
632         XDrawArc( gdi_display, physDev->drawable, physDev->gc,
633                   physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
634                   rc.right-rc.left-1, rc.bottom-rc.top-1, istart_angle, idiff_angle );
635         if (lines) {
636             /* use the truncated values */
637             start_angle=(double)istart_angle*PI/64./180.;
638             end_angle=(double)(istart_angle+idiff_angle)*PI/64./180.;
639             /* calculate the endpoints and round correctly */
640             points[0].x = (int) floor(physDev->dc_rect.left + (rc.right+rc.left)/2.0 +
641                     cos(start_angle) * (rc.right-rc.left-width*2+2) / 2. + 0.5);
642             points[0].y = (int) floor(physDev->dc_rect.top + (rc.top+rc.bottom)/2.0 -
643                     sin(start_angle) * (rc.bottom-rc.top-width*2+2) / 2. + 0.5);
644             points[1].x = (int) floor(physDev->dc_rect.left + (rc.right+rc.left)/2.0 +
645                     cos(end_angle) * (rc.right-rc.left-width*2+2) / 2. + 0.5);
646             points[1].y = (int) floor(physDev->dc_rect.top + (rc.top+rc.bottom)/2.0 -
647                     sin(end_angle) * (rc.bottom-rc.top-width*2+2) / 2. + 0.5);
648
649             /* OK, this stuff is optimized for Xfree86
650              * which is probably the server most used by
651              * wine users. Other X servers will not
652              * display correctly. (eXceed for instance)
653              * so if you feel you must make changes, make sure that
654              * you either use Xfree86 or separate your changes
655              * from these (compile switch or whatever)
656              */
657             if (lines == 2) {
658                 INT dx1,dy1;
659                 points[3] = points[1];
660                 points[1].x = physDev->dc_rect.left + xcenter;
661                 points[1].y = physDev->dc_rect.top + ycenter;
662                 points[2] = points[1];
663                 dx1=points[1].x-points[0].x;
664                 dy1=points[1].y-points[0].y;
665                 if(((rc.top-rc.bottom) | -2) == -2)
666                     if(dy1>0) points[1].y--;
667                 if(dx1<0) {
668                     if (((-dx1)*64)<=ABS(dy1)*37) points[0].x--;
669                     if(((-dx1*9))<(dy1*16)) points[0].y--;
670                     if( dy1<0 && ((dx1*9)) < (dy1*16)) points[0].y--;
671                 } else {
672                     if(dy1 < 0)  points[0].y--;
673                     if(((rc.right-rc.left) | -2) == -2) points[1].x--;
674                 }
675                 dx1=points[3].x-points[2].x;
676                 dy1=points[3].y-points[2].y;
677                 if(((rc.top-rc.bottom) | -2 ) == -2)
678                     if(dy1 < 0) points[2].y--;
679                 if( dx1<0){
680                     if( dy1>0) points[3].y--;
681                     if(((rc.right-rc.left) | -2) == -2 ) points[2].x--;
682                 }else {
683                     points[3].y--;
684                     if( dx1 * 64 < dy1 * -37 ) points[3].x--;
685                 }
686                 lines++;
687             }
688             XDrawLines( gdi_display, physDev->drawable, physDev->gc,
689                         points, lines+1, CoordModeOrigin );
690         }
691     }
692
693     physDev->pen.width = oldwidth;
694     add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
695     return TRUE;
696 }
697
698
699 /***********************************************************************
700  *           X11DRV_Arc
701  */
702 BOOL X11DRV_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
703                  INT xstart, INT ystart, INT xend, INT yend )
704 {
705     return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 0 );
706 }
707
708
709 /***********************************************************************
710  *           X11DRV_Pie
711  */
712 BOOL X11DRV_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
713                  INT xstart, INT ystart, INT xend, INT yend )
714 {
715     return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 2 );
716 }
717
718 /***********************************************************************
719  *           X11DRV_Chord
720  */
721 BOOL X11DRV_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
722                    INT xstart, INT ystart, INT xend, INT yend )
723 {
724     return X11DRV_DrawArc( dev, left, top, right, bottom, xstart, ystart, xend, yend, 1 );
725 }
726
727
728 /***********************************************************************
729  *           X11DRV_Ellipse
730  */
731 BOOL X11DRV_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
732 {
733     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
734     INT width, oldwidth;
735     RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
736
737     if ((rc.left == rc.right) || (rc.top == rc.bottom)) return TRUE;
738
739     oldwidth = width = physDev->pen.width;
740     if (!width) width = 1;
741     if(physDev->pen.style == PS_NULL) width = 0;
742
743     if (physDev->pen.style == PS_INSIDEFRAME)
744     {
745         if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
746         if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
747         rc.left   += width / 2;
748         rc.right  -= (width - 1) / 2;
749         rc.top    += width / 2;
750         rc.bottom -= (width - 1) / 2;
751     }
752     if(width == 0) width = 1; /* more accurate */
753     physDev->pen.width = width;
754
755     if (X11DRV_SetupGCForBrush( physDev ))
756         XFillArc( gdi_display, physDev->drawable, physDev->gc,
757                   physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
758                   rc.right-rc.left-1, rc.bottom-rc.top-1, 0, 360*64 );
759
760     if (X11DRV_SetupGCForPen( physDev ))
761         XDrawArc( gdi_display, physDev->drawable, physDev->gc,
762                   physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
763                   rc.right-rc.left-1, rc.bottom-rc.top-1, 0, 360*64 );
764
765     physDev->pen.width = oldwidth;
766     add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
767     return TRUE;
768 }
769
770
771 /***********************************************************************
772  *           X11DRV_Rectangle
773  */
774 BOOL X11DRV_Rectangle(PHYSDEV dev, INT left, INT top, INT right, INT bottom)
775 {
776     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
777     INT width, oldwidth, oldjoinstyle;
778     RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
779
780     TRACE("(%d %d %d %d)\n", left, top, right, bottom);
781
782     if ((rc.left == rc.right) || (rc.top == rc.bottom)) return TRUE;
783
784     oldwidth = width = physDev->pen.width;
785     if (!width) width = 1;
786     if(physDev->pen.style == PS_NULL) width = 0;
787
788     if (physDev->pen.style == PS_INSIDEFRAME)
789     {
790         if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
791         if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
792         rc.left   += width / 2;
793         rc.right  -= (width - 1) / 2;
794         rc.top    += width / 2;
795         rc.bottom -= (width - 1) / 2;
796     }
797     if(width == 1) width = 0;
798     physDev->pen.width = width;
799     oldjoinstyle = physDev->pen.linejoin;
800     if(physDev->pen.type != PS_GEOMETRIC)
801         physDev->pen.linejoin = PS_JOIN_MITER;
802
803     rc.right--;
804     rc.bottom--;
805     if ((rc.right >= rc.left + width) && (rc.bottom >= rc.top + width))
806     {
807         if (X11DRV_SetupGCForBrush( physDev ))
808             XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
809                             physDev->dc_rect.left + rc.left + (width + 1) / 2,
810                             physDev->dc_rect.top + rc.top + (width + 1) / 2,
811                             rc.right-rc.left-width, rc.bottom-rc.top-width);
812     }
813     if (X11DRV_SetupGCForPen( physDev ))
814         XDrawRectangle( gdi_display, physDev->drawable, physDev->gc,
815                         physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
816                         rc.right-rc.left, rc.bottom-rc.top );
817
818     physDev->pen.width = oldwidth;
819     physDev->pen.linejoin = oldjoinstyle;
820     add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
821     return TRUE;
822 }
823
824 /***********************************************************************
825  *           X11DRV_RoundRect
826  */
827 BOOL X11DRV_RoundRect( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
828                        INT ell_width, INT ell_height )
829 {
830     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
831     INT width, oldwidth, oldendcap;
832     POINT pts[2];
833     RECT rc = get_device_rect( dev->hdc, left, top, right, bottom );
834
835     TRACE("(%d %d %d %d  %d %d\n",
836         left, top, right, bottom, ell_width, ell_height);
837
838     if ((rc.left == rc.right) || (rc.top == rc.bottom))
839         return TRUE;
840
841     /* Make sure ell_width and ell_height are >= 1 otherwise XDrawArc gets
842        called with width/height < 0 */
843     pts[0].x = pts[0].y = 0;
844     pts[1].x = ell_width;
845     pts[1].y = ell_height;
846     LPtoDP(dev->hdc, pts, 2);
847     ell_width  = max(abs( pts[1].x - pts[0].x ), 1);
848     ell_height = max(abs( pts[1].y - pts[0].y ), 1);
849
850     oldwidth = width = physDev->pen.width;
851     oldendcap = physDev->pen.endcap;
852     if (!width) width = 1;
853     if(physDev->pen.style == PS_NULL) width = 0;
854
855     if (physDev->pen.style == PS_INSIDEFRAME)
856     {
857         if (2*width > (rc.right-rc.left)) width=(rc.right-rc.left + 1)/2;
858         if (2*width > (rc.bottom-rc.top)) width=(rc.bottom-rc.top + 1)/2;
859         rc.left   += width / 2;
860         rc.right  -= (width - 1) / 2;
861         rc.top    += width / 2;
862         rc.bottom -= (width - 1) / 2;
863     }
864     if(width == 0) width = 1;
865     physDev->pen.width = width;
866     physDev->pen.endcap = PS_ENDCAP_SQUARE;
867
868     if (X11DRV_SetupGCForBrush( physDev ))
869     {
870         if (ell_width > (rc.right-rc.left) )
871             if (ell_height > (rc.bottom-rc.top) )
872                 XFillArc( gdi_display, physDev->drawable, physDev->gc,
873                           physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
874                           rc.right - rc.left - 1, rc.bottom - rc.top - 1,
875                           0, 360 * 64 );
876             else{
877                 XFillArc( gdi_display, physDev->drawable, physDev->gc,
878                           physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
879                           rc.right - rc.left - 1, ell_height, 0, 180 * 64 );
880                 XFillArc( gdi_display, physDev->drawable, physDev->gc,
881                           physDev->dc_rect.left + rc.left,
882                           physDev->dc_rect.top + rc.bottom - ell_height - 1,
883                           rc.right - rc.left - 1, ell_height, 180 * 64,
884                           180 * 64 );
885             }
886         else if (ell_height > (rc.bottom-rc.top) ){
887             XFillArc( gdi_display, physDev->drawable, physDev->gc,
888                       physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
889                       ell_width, rc.bottom - rc.top - 1, 90 * 64, 180 * 64 );
890             XFillArc( gdi_display, physDev->drawable, physDev->gc,
891                       physDev->dc_rect.left + rc.right - ell_width - 1, physDev->dc_rect.top + rc.top,
892                       ell_width, rc.bottom - rc.top - 1, 270 * 64, 180 * 64 );
893         }else{
894             XFillArc( gdi_display, physDev->drawable, physDev->gc,
895                       physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
896                       ell_width, ell_height, 90 * 64, 90 * 64 );
897             XFillArc( gdi_display, physDev->drawable, physDev->gc,
898                       physDev->dc_rect.left + rc.left,
899                       physDev->dc_rect.top + rc.bottom - ell_height - 1,
900                       ell_width, ell_height, 180 * 64, 90 * 64 );
901             XFillArc( gdi_display, physDev->drawable, physDev->gc,
902                       physDev->dc_rect.left + rc.right - ell_width - 1,
903                       physDev->dc_rect.top + rc.bottom - ell_height - 1,
904                       ell_width, ell_height, 270 * 64, 90 * 64 );
905             XFillArc( gdi_display, physDev->drawable, physDev->gc,
906                       physDev->dc_rect.left + rc.right - ell_width - 1,
907                       physDev->dc_rect.top + rc.top,
908                       ell_width, ell_height, 0, 90 * 64 );
909         }
910         if (ell_width < rc.right - rc.left)
911         {
912             XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
913                             physDev->dc_rect.left + rc.left + (ell_width + 1) / 2,
914                             physDev->dc_rect.top + rc.top + 1,
915                             rc.right - rc.left - ell_width - 1,
916                             (ell_height + 1) / 2 - 1);
917             XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
918                             physDev->dc_rect.left + rc.left + (ell_width + 1) / 2,
919                             physDev->dc_rect.top + rc.bottom - (ell_height) / 2 - 1,
920                             rc.right - rc.left - ell_width - 1,
921                             (ell_height) / 2 );
922         }
923         if  (ell_height < rc.bottom - rc.top)
924         {
925             XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
926                             physDev->dc_rect.left + rc.left + 1,
927                             physDev->dc_rect.top + rc.top + (ell_height + 1) / 2,
928                             rc.right - rc.left - 2,
929                             rc.bottom - rc.top - ell_height - 1);
930         }
931     }
932     /* FIXME: this could be done with on X call
933      * more efficient and probably more correct
934      * on any X server: XDrawArcs will draw
935      * straight horizontal and vertical lines
936      * if width or height are zero.
937      *
938      * BTW this stuff is optimized for an Xfree86 server
939      * read the comments inside the X11DRV_DrawArc function
940      */
941     if (X11DRV_SetupGCForPen( physDev ))
942     {
943         if (ell_width > (rc.right-rc.left) )
944             if (ell_height > (rc.bottom-rc.top) )
945                 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
946                           physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
947                           rc.right - rc.left - 1, rc.bottom - rc.top - 1, 0 , 360 * 64 );
948             else{
949                 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
950                           physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
951                           rc.right - rc.left - 1, ell_height - 1, 0 , 180 * 64 );
952                 XDrawArc( gdi_display, physDev->drawable, physDev->gc,
953                           physDev->dc_rect.left + rc.left,
954                           physDev->dc_rect.top + rc.bottom - ell_height,
955                           rc.right - rc.left - 1, ell_height - 1, 180 * 64 , 180 * 64 );
956             }
957         else if (ell_height > (rc.bottom-rc.top) ){
958             XDrawArc( gdi_display, physDev->drawable, physDev->gc,
959                       physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
960                       ell_width - 1 , rc.bottom - rc.top - 1, 90 * 64 , 180 * 64 );
961             XDrawArc( gdi_display, physDev->drawable, physDev->gc,
962                       physDev->dc_rect.left + rc.right - ell_width,
963                       physDev->dc_rect.top + rc.top,
964                       ell_width - 1 , rc.bottom - rc.top - 1, 270 * 64 , 180 * 64 );
965         }else{
966             XDrawArc( gdi_display, physDev->drawable, physDev->gc,
967                       physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.top,
968                       ell_width - 1, ell_height - 1, 90 * 64, 90 * 64 );
969             XDrawArc( gdi_display, physDev->drawable, physDev->gc,
970                       physDev->dc_rect.left + rc.left, physDev->dc_rect.top + rc.bottom - ell_height,
971                       ell_width - 1, ell_height - 1, 180 * 64, 90 * 64 );
972             XDrawArc( gdi_display, physDev->drawable, physDev->gc,
973                       physDev->dc_rect.left + rc.right - ell_width,
974                       physDev->dc_rect.top + rc.bottom - ell_height,
975                       ell_width - 1, ell_height - 1, 270 * 64, 90 * 64 );
976             XDrawArc( gdi_display, physDev->drawable, physDev->gc,
977                       physDev->dc_rect.left + rc.right - ell_width, physDev->dc_rect.top + rc.top,
978                       ell_width - 1, ell_height - 1, 0, 90 * 64 );
979         }
980         if (ell_width < rc.right - rc.left)
981         {
982             XDrawLine( gdi_display, physDev->drawable, physDev->gc,
983                        physDev->dc_rect.left + rc.left + ell_width / 2,
984                        physDev->dc_rect.top + rc.top,
985                        physDev->dc_rect.left + rc.right - (ell_width+1) / 2,
986                        physDev->dc_rect.top + rc.top);
987             XDrawLine( gdi_display, physDev->drawable, physDev->gc,
988                        physDev->dc_rect.left + rc.left + ell_width / 2 ,
989                        physDev->dc_rect.top + rc.bottom - 1,
990                        physDev->dc_rect.left + rc.right - (ell_width+1)/ 2,
991                        physDev->dc_rect.top + rc.bottom - 1);
992         }
993         if (ell_height < rc.bottom - rc.top)
994         {
995             XDrawLine( gdi_display, physDev->drawable, physDev->gc,
996                        physDev->dc_rect.left + rc.right - 1,
997                        physDev->dc_rect.top + rc.top + ell_height / 2,
998                        physDev->dc_rect.left + rc.right - 1,
999                        physDev->dc_rect.top + rc.bottom - (ell_height+1) / 2);
1000             XDrawLine( gdi_display, physDev->drawable, physDev->gc,
1001                        physDev->dc_rect.left + rc.left,
1002                        physDev->dc_rect.top + rc.top + ell_height / 2,
1003                        physDev->dc_rect.left + rc.left,
1004                        physDev->dc_rect.top + rc.bottom - (ell_height+1) / 2);
1005         }
1006     }
1007
1008     physDev->pen.width = oldwidth;
1009     physDev->pen.endcap = oldendcap;
1010     add_pen_device_bounds( physDev, (POINT *)&rc, 2 );
1011     return TRUE;
1012 }
1013
1014
1015 /***********************************************************************
1016  *           X11DRV_SetPixel
1017  */
1018 COLORREF X11DRV_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
1019 {
1020     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1021     unsigned long pixel;
1022     POINT pt;
1023     RECT rect;
1024
1025     pt.x = x;
1026     pt.y = y;
1027     LPtoDP( dev->hdc, &pt, 1 );
1028     pixel = X11DRV_PALETTE_ToPhysical( physDev, color );
1029
1030     XSetForeground( gdi_display, physDev->gc, pixel );
1031     XSetFunction( gdi_display, physDev->gc, GXcopy );
1032     XDrawPoint( gdi_display, physDev->drawable, physDev->gc,
1033                 physDev->dc_rect.left + pt.x, physDev->dc_rect.top + pt.y );
1034
1035     SetRect( &rect, pt.x, pt.y, pt.x + 1, pt.y + 1 );
1036     add_device_bounds( physDev, &rect );
1037     return X11DRV_PALETTE_ToLogical(physDev, pixel);
1038 }
1039
1040
1041 /***********************************************************************
1042  *           X11DRV_PaintRgn
1043  */
1044 BOOL X11DRV_PaintRgn( PHYSDEV dev, HRGN hrgn )
1045 {
1046     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1047     RECT rc;
1048
1049     if (X11DRV_SetupGCForBrush( physDev ))
1050     {
1051         unsigned int i;
1052         XRectangle *rect;
1053         RGNDATA *data = X11DRV_GetRegionData( hrgn, dev->hdc );
1054
1055         if (!data) return FALSE;
1056         rect = (XRectangle *)data->Buffer;
1057         for (i = 0; i < data->rdh.nCount; i++)
1058         {
1059             rect[i].x += physDev->dc_rect.left;
1060             rect[i].y += physDev->dc_rect.top;
1061         }
1062
1063         XFillRectangles( gdi_display, physDev->drawable, physDev->gc, rect, data->rdh.nCount );
1064         HeapFree( GetProcessHeap(), 0, data );
1065     }
1066     if (GetRgnBox( hrgn, &rc ))
1067     {
1068         LPtoDP( dev->hdc, (POINT *)&rc, 2 );
1069         add_device_bounds( physDev, &rc );
1070     }
1071     return TRUE;
1072 }
1073
1074 /**********************************************************************
1075  *          X11DRV_Polygon
1076  */
1077 BOOL X11DRV_Polygon( PHYSDEV dev, const POINT* pt, INT count )
1078 {
1079     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1080     int i;
1081     POINT *points;
1082     XPoint *xpoints;
1083
1084     points = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pt) );
1085     if (!points) return FALSE;
1086     memcpy( points, pt, count * sizeof(*pt) );
1087     LPtoDP( dev->hdc, points, count );
1088     add_pen_device_bounds( physDev, points, count );
1089
1090     if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * (count+1) )))
1091     {
1092         HeapFree( GetProcessHeap(), 0, points );
1093         return FALSE;
1094     }
1095     for (i = 0; i < count; i++)
1096     {
1097         xpoints[i].x = physDev->dc_rect.left + points[i].x;
1098         xpoints[i].y = physDev->dc_rect.top + points[i].y;
1099     }
1100     xpoints[count] = xpoints[0];
1101
1102     if (X11DRV_SetupGCForBrush( physDev ))
1103         XFillPolygon( gdi_display, physDev->drawable, physDev->gc,
1104                       xpoints, count+1, Complex, CoordModeOrigin);
1105
1106     if (X11DRV_SetupGCForPen ( physDev ))
1107         XDrawLines( gdi_display, physDev->drawable, physDev->gc,
1108                     xpoints, count+1, CoordModeOrigin );
1109
1110     HeapFree( GetProcessHeap(), 0, xpoints );
1111     HeapFree( GetProcessHeap(), 0, points );
1112     return TRUE;
1113 }
1114
1115
1116 /**********************************************************************
1117  *          X11DRV_PolyPolygon
1118  */
1119 BOOL X11DRV_PolyPolygon( PHYSDEV dev, const POINT* pt, const INT* counts, UINT polygons )
1120 {
1121     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1122     DWORD total = 0, max = 0, pos, i;
1123     POINT *points;
1124     BOOL ret = FALSE;
1125
1126     for (i = 0; i < polygons; i++)
1127     {
1128         if (counts[i] < 2) return FALSE;
1129         if (counts[i] > max) max = counts[i];
1130         total += counts[i];
1131     }
1132
1133     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1134     if (!points) return FALSE;
1135     memcpy( points, pt, total * sizeof(*pt) );
1136     LPtoDP( dev->hdc, points, total );
1137     add_pen_device_bounds( physDev, points, total );
1138
1139     if (X11DRV_SetupGCForBrush( physDev ))
1140     {
1141         XRectangle *rect;
1142         HRGN hrgn = CreatePolyPolygonRgn( points, counts, polygons, GetPolyFillMode( dev->hdc ) );
1143         RGNDATA *data = X11DRV_GetRegionData( hrgn, 0 );
1144
1145         DeleteObject( hrgn );
1146         if (!data) goto done;
1147         rect = (XRectangle *)data->Buffer;
1148         for (i = 0; i < data->rdh.nCount; i++)
1149         {
1150             rect[i].x += physDev->dc_rect.left;
1151             rect[i].y += physDev->dc_rect.top;
1152         }
1153
1154         XFillRectangles( gdi_display, physDev->drawable, physDev->gc, rect, data->rdh.nCount );
1155         HeapFree( GetProcessHeap(), 0, data );
1156     }
1157
1158     if (X11DRV_SetupGCForPen ( physDev ))
1159     {
1160         XPoint *xpoints;
1161         int j;
1162
1163         if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * (max + 1) ))) goto done;
1164         for (i = pos = 0; i < polygons; pos += counts[i++])
1165         {
1166             for (j = 0; j < counts[i]; j++)
1167             {
1168                 xpoints[j].x = physDev->dc_rect.left + points[pos + j].x;
1169                 xpoints[j].y = physDev->dc_rect.top + points[pos + j].y;
1170             }
1171             xpoints[j] = xpoints[0];
1172             XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j + 1, CoordModeOrigin );
1173         }
1174         HeapFree( GetProcessHeap(), 0, xpoints );
1175     }
1176     ret = TRUE;
1177
1178 done:
1179     HeapFree( GetProcessHeap(), 0, points );
1180     return ret;
1181 }
1182
1183
1184 /**********************************************************************
1185  *          X11DRV_PolyPolyline
1186  */
1187 BOOL X11DRV_PolyPolyline( PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polylines )
1188 {
1189     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1190     DWORD total = 0, max = 0, pos, i, j;
1191     POINT *points;
1192
1193     for (i = 0; i < polylines; i++)
1194     {
1195         if (counts[i] < 2) return FALSE;
1196         if (counts[i] > max) max = counts[i];
1197         total += counts[i];
1198     }
1199
1200     points = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*pt) );
1201     if (!points) return FALSE;
1202     memcpy( points, pt, total * sizeof(*pt) );
1203     LPtoDP( dev->hdc, points, total );
1204     add_pen_device_bounds( physDev, points, total );
1205
1206     if (X11DRV_SetupGCForPen ( physDev ))
1207     {
1208         XPoint *xpoints;
1209
1210         if (!(xpoints = HeapAlloc( GetProcessHeap(), 0, sizeof(XPoint) * max )))
1211         {
1212             HeapFree( GetProcessHeap(), 0, points );
1213             return FALSE;
1214         }
1215         for (i = pos = 0; i < polylines; pos += counts[i++])
1216         {
1217             for (j = 0; j < counts[i]; j++)
1218             {
1219                 xpoints[j].x = physDev->dc_rect.left + points[pos + j].x;
1220                 xpoints[j].y = physDev->dc_rect.top + points[pos + j].y;
1221             }
1222             XDrawLines( gdi_display, physDev->drawable, physDev->gc, xpoints, j, CoordModeOrigin );
1223         }
1224         HeapFree( GetProcessHeap(), 0, xpoints );
1225     }
1226     HeapFree( GetProcessHeap(), 0, points );
1227     return TRUE;
1228 }
1229
1230
1231 /**********************************************************************
1232  *          X11DRV_InternalFloodFill
1233  *
1234  * Internal helper function for flood fill.
1235  * (xorg,yorg) is the origin of the X image relative to the drawable.
1236  * (x,y) is relative to the origin of the X image.
1237  */
1238 static void X11DRV_InternalFloodFill(XImage *image, X11DRV_PDEVICE *physDev,
1239                                      int x, int y,
1240                                      int xOrg, int yOrg,
1241                                      unsigned long pixel, WORD fillType, RECT *bounds )
1242 {
1243     int left, right;
1244
1245 #define TO_FLOOD(x,y)  ((fillType == FLOODFILLBORDER) ? \
1246                         (XGetPixel(image,x,y) != pixel) : \
1247                         (XGetPixel(image,x,y) == pixel))
1248
1249     if (!TO_FLOOD(x,y)) return;
1250
1251       /* Find left and right boundaries */
1252
1253     left = right = x;
1254     while ((left > 0) && TO_FLOOD( left-1, y )) left--;
1255     while ((right < image->width) && TO_FLOOD( right, y )) right++;
1256     bounds->left   = min( bounds->left, left );
1257     bounds->top    = min( bounds->top, y );
1258     bounds->right  = max( bounds->right, right );
1259     bounds->bottom = max( bounds->bottom, y + 1 );
1260     XFillRectangle( gdi_display, physDev->drawable, physDev->gc,
1261                     xOrg + left, yOrg + y, right-left, 1 );
1262
1263       /* Set the pixels of this line so we don't fill it again */
1264
1265     for (x = left; x < right; x++)
1266     {
1267         if (fillType == FLOODFILLBORDER) XPutPixel( image, x, y, pixel );
1268         else XPutPixel( image, x, y, ~pixel );
1269     }
1270
1271       /* Fill the line above */
1272
1273     if (--y >= 0)
1274     {
1275         x = left;
1276         while (x < right)
1277         {
1278             while ((x < right) && !TO_FLOOD(x,y)) x++;
1279             if (x >= right) break;
1280             while ((x < right) && TO_FLOOD(x,y)) x++;
1281             X11DRV_InternalFloodFill(image, physDev, x-1, y,
1282                                      xOrg, yOrg, pixel, fillType, bounds );
1283         }
1284     }
1285
1286       /* Fill the line below */
1287
1288     if ((y += 2) < image->height)
1289     {
1290         x = left;
1291         while (x < right)
1292         {
1293             while ((x < right) && !TO_FLOOD(x,y)) x++;
1294             if (x >= right) break;
1295             while ((x < right) && TO_FLOOD(x,y)) x++;
1296             X11DRV_InternalFloodFill(image, physDev, x-1, y,
1297                                      xOrg, yOrg, pixel, fillType, bounds );
1298         }
1299     }
1300 #undef TO_FLOOD
1301 }
1302
1303
1304 static int ExtFloodFillXGetImageErrorHandler( Display *dpy, XErrorEvent *event, void *arg )
1305 {
1306     return (event->request_code == X_GetImage && event->error_code == BadMatch);
1307 }
1308
1309 /**********************************************************************
1310  *          X11DRV_ExtFloodFill
1311  */
1312 BOOL X11DRV_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT fillType )
1313 {
1314     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1315     XImage *image;
1316     RECT rect, bounds;
1317     POINT pt;
1318
1319     TRACE("X11DRV_ExtFloodFill %d,%d %06x %d\n", x, y, color, fillType );
1320
1321     pt.x = x;
1322     pt.y = y;
1323     LPtoDP( dev->hdc, &pt, 1 );
1324
1325     if (!physDev->region)
1326     {
1327         rect.left   = 0;
1328         rect.top    = 0;
1329         rect.right  = physDev->dc_rect.right - physDev->dc_rect.left;
1330         rect.bottom = physDev->dc_rect.bottom - physDev->dc_rect.top;
1331     }
1332     else
1333     {
1334         if (!PtInRegion( physDev->region, pt.x, pt.y )) return FALSE;
1335         GetRgnBox( physDev->region, &rect );
1336         rect.left   = max( rect.left, 0 );
1337         rect.top    = max( rect.top, 0 );
1338         rect.right  = min( rect.right, physDev->dc_rect.right - physDev->dc_rect.left );
1339         rect.bottom = min( rect.bottom, physDev->dc_rect.bottom - physDev->dc_rect.top );
1340     }
1341     if (pt.x < rect.left || pt.x >= rect.right || pt.y < rect.top || pt.y >= rect.bottom) return FALSE;
1342
1343     X11DRV_expect_error( gdi_display, ExtFloodFillXGetImageErrorHandler, NULL );
1344     image = XGetImage( gdi_display, physDev->drawable,
1345                        physDev->dc_rect.left + rect.left, physDev->dc_rect.top + rect.top,
1346                        rect.right - rect.left, rect.bottom - rect.top,
1347                        AllPlanes, ZPixmap );
1348     if(X11DRV_check_error()) image = NULL;
1349     if (!image) return FALSE;
1350
1351     if (X11DRV_SetupGCForBrush( physDev ))
1352     {
1353         unsigned long pixel = X11DRV_PALETTE_ToPhysical( physDev, color );
1354
1355         reset_bounds( &bounds );
1356
1357         X11DRV_InternalFloodFill(image, physDev,
1358                                  pt.x - rect.left,
1359                                  pt.y - rect.top,
1360                                  physDev->dc_rect.left + rect.left,
1361                                  physDev->dc_rect.top + rect.top,
1362                                  pixel, fillType, &bounds );
1363
1364         OffsetRect( &bounds, rect.left, rect.top );
1365         add_device_bounds( physDev, &bounds );
1366     }
1367
1368     XDestroyImage( image );
1369     return TRUE;
1370 }
1371
1372 /**********************************************************************
1373  *          X11DRV_GradientFill
1374  */
1375 BOOL X11DRV_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
1376                           void *grad_array, ULONG ngrad, ULONG mode )
1377 {
1378     X11DRV_PDEVICE *physdev = get_x11drv_dev( dev );
1379     const GRADIENT_RECT *rect = grad_array;
1380     TRIVERTEX v[2];
1381     POINT pt[2];
1382     RECT rc, bounds;
1383     unsigned int i;
1384     XGCValues val;
1385
1386     /* <= 16-bpp use dithering */
1387     if (physdev->depth <= 16) goto fallback;
1388
1389     switch (mode)
1390     {
1391     case GRADIENT_FILL_RECT_H:
1392         val.function   = GXcopy;
1393         val.fill_style = FillSolid;
1394         val.line_width = 1;
1395         val.cap_style  = CapNotLast;
1396         val.line_style = LineSolid;
1397         XChangeGC( gdi_display, physdev->gc,
1398                    GCFunction | GCLineWidth | GCLineStyle | GCCapStyle | GCFillStyle, &val );
1399         reset_bounds( &bounds );
1400
1401         for (i = 0; i < ngrad; i++, rect++)
1402         {
1403             int x, dx;
1404
1405             v[0] = vert_array[rect->UpperLeft];
1406             v[1] = vert_array[rect->LowerRight];
1407             pt[0].x = v[0].x;
1408             pt[0].y = v[0].y;
1409             pt[1].x = v[1].x;
1410             pt[1].y = v[1].y;
1411             LPtoDP( dev->hdc, pt, 2 );
1412             dx = pt[1].x - pt[0].x;
1413             if (!dx) continue;
1414             if (dx < 0)  /* swap the colors */
1415             {
1416                 v[0] = vert_array[rect->LowerRight];
1417                 v[1] = vert_array[rect->UpperLeft];
1418                 dx = -dx;
1419             }
1420             rc.left   = min( pt[0].x, pt[1].x );
1421             rc.top    = min( pt[0].y, pt[1].y );
1422             rc.right  = max( pt[0].x, pt[1].x );
1423             rc.bottom = max( pt[0].y, pt[1].y );
1424             add_bounds_rect( &bounds, &rc );
1425             for (x = 0; x < dx; x++)
1426             {
1427                 int color = X11DRV_PALETTE_ToPhysical( physdev,
1428                                  RGB( (v[0].Red   * (dx - x) + v[1].Red   * x) / dx / 256,
1429                                       (v[0].Green * (dx - x) + v[1].Green * x) / dx / 256,
1430                                       (v[0].Blue  * (dx - x) + v[1].Blue  * x) / dx / 256) );
1431
1432                 XSetForeground( gdi_display, physdev->gc, color );
1433                 XDrawLine( gdi_display, physdev->drawable, physdev->gc,
1434                            physdev->dc_rect.left + rc.left + x, physdev->dc_rect.top + rc.top,
1435                            physdev->dc_rect.left + rc.left + x, physdev->dc_rect.top + rc.bottom );
1436             }
1437         }
1438         add_device_bounds( physdev, &bounds );
1439         return TRUE;
1440
1441     case GRADIENT_FILL_RECT_V:
1442         val.function   = GXcopy;
1443         val.fill_style = FillSolid;
1444         val.line_width = 1;
1445         val.cap_style  = CapNotLast;
1446         val.line_style = LineSolid;
1447         XChangeGC( gdi_display, physdev->gc,
1448                    GCFunction | GCLineWidth | GCLineStyle | GCCapStyle | GCFillStyle, &val );
1449         reset_bounds( &bounds );
1450
1451         for (i = 0; i < ngrad; i++, rect++)
1452         {
1453             int y, dy;
1454
1455             v[0] = vert_array[rect->UpperLeft];
1456             v[1] = vert_array[rect->LowerRight];
1457             pt[0].x = v[0].x;
1458             pt[0].y = v[0].y;
1459             pt[1].x = v[1].x;
1460             pt[1].y = v[1].y;
1461             LPtoDP( dev->hdc, pt, 2 );
1462             dy = pt[1].y - pt[0].y;
1463             if (!dy) continue;
1464             if (dy < 0)  /* swap the colors */
1465             {
1466                 v[0] = vert_array[rect->LowerRight];
1467                 v[1] = vert_array[rect->UpperLeft];
1468                 dy = -dy;
1469             }
1470             rc.left   = min( pt[0].x, pt[1].x );
1471             rc.top    = min( pt[0].y, pt[1].y );
1472             rc.right  = max( pt[0].x, pt[1].x );
1473             rc.bottom = max( pt[0].y, pt[1].y );
1474             add_bounds_rect( &bounds, &rc );
1475             for (y = 0; y < dy; y++)
1476             {
1477                 int color = X11DRV_PALETTE_ToPhysical( physdev,
1478                                  RGB( (v[0].Red   * (dy - y) + v[1].Red   * y) / dy / 256,
1479                                       (v[0].Green * (dy - y) + v[1].Green * y) / dy / 256,
1480                                       (v[0].Blue  * (dy - y) + v[1].Blue  * y) / dy / 256) );
1481
1482                 XSetForeground( gdi_display, physdev->gc, color );
1483                 XDrawLine( gdi_display, physdev->drawable, physdev->gc,
1484                            physdev->dc_rect.left + rc.left, physdev->dc_rect.top + rc.top + y,
1485                            physdev->dc_rect.left + rc.right, physdev->dc_rect.top + rc.top + y );
1486             }
1487         }
1488         add_device_bounds( physdev, &bounds );
1489         return TRUE;
1490     }
1491
1492 fallback:
1493     dev = GET_NEXT_PHYSDEV( dev, pGradientFill );
1494     return dev->funcs->pGradientFill( dev, vert_array, nvert, grad_array, ngrad, mode );
1495 }
1496
1497 static unsigned char *get_icm_profile( unsigned long *size )
1498 {
1499     Atom type;
1500     int format;
1501     unsigned long count, remaining;
1502     unsigned char *profile, *ret = NULL;
1503
1504     XGetWindowProperty( gdi_display, DefaultRootWindow(gdi_display),
1505                         x11drv_atom(_ICC_PROFILE), 0, ~0UL, False, AnyPropertyType,
1506                         &type, &format, &count, &remaining, &profile );
1507     *size = get_property_size( format, count );
1508     if (format && count)
1509     {
1510         if ((ret = HeapAlloc( GetProcessHeap(), 0, *size ))) memcpy( ret, profile, *size );
1511         XFree( profile );
1512     }
1513     return ret;
1514 }
1515
1516 typedef struct
1517 {
1518     unsigned int unknown[6];
1519     unsigned int state[5];
1520     unsigned int count[2];
1521     unsigned char buffer[64];
1522 } sha_ctx;
1523
1524 extern void WINAPI A_SHAInit( sha_ctx * );
1525 extern void WINAPI A_SHAUpdate( sha_ctx *, const unsigned char *, unsigned int );
1526 extern void WINAPI A_SHAFinal( sha_ctx *, unsigned char * );
1527
1528 static const WCHAR mntr_key[] =
1529     {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
1530      'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t',
1531      'V','e','r','s','i','o','n','\\','I','C','M','\\','m','n','t','r',0};
1532
1533 static const WCHAR color_path[] =
1534     {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\','c','o','l','o','r','\\',0};
1535
1536 /***********************************************************************
1537  *              GetICMProfile (X11DRV.@)
1538  */
1539 BOOL X11DRV_GetICMProfile( PHYSDEV dev, LPDWORD size, LPWSTR filename )
1540 {
1541     static const WCHAR srgb[] =
1542         {'s','R','G','B',' ','C','o','l','o','r',' ','S','p','a','c','e',' ',
1543          'P','r','o','f','i','l','e','.','i','c','m',0};
1544     HKEY hkey;
1545     DWORD required, len;
1546     WCHAR profile[MAX_PATH], fullname[2*MAX_PATH + sizeof(color_path)/sizeof(WCHAR)];
1547     unsigned char *buffer;
1548     unsigned long buflen;
1549
1550     if (!size) return FALSE;
1551
1552     GetSystemDirectoryW( fullname, MAX_PATH );
1553     strcatW( fullname, color_path );
1554
1555     len = sizeof(profile)/sizeof(WCHAR);
1556     if (!RegCreateKeyExW( HKEY_LOCAL_MACHINE, mntr_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL ) &&
1557         !RegEnumValueW( hkey, 0, profile, &len, NULL, NULL, NULL, NULL )) /* FIXME handle multiple values */
1558     {
1559         strcatW( fullname, profile );
1560         RegCloseKey( hkey );
1561     }
1562     else if ((buffer = get_icm_profile( &buflen )))
1563     {
1564         static const WCHAR fmt[] = {'%','0','2','x',0};
1565         static const WCHAR icm[] = {'.','i','c','m',0};
1566
1567         unsigned char sha1sum[20];
1568         unsigned int i;
1569         sha_ctx ctx;
1570         HANDLE file;
1571
1572         A_SHAInit( &ctx );
1573         A_SHAUpdate( &ctx, buffer, buflen );
1574         A_SHAFinal( &ctx, sha1sum );
1575
1576         for (i = 0; i < sizeof(sha1sum); i++) sprintfW( &profile[i * 2], fmt, sha1sum[i] );
1577         memcpy( &profile[i * 2], icm, sizeof(icm) );
1578
1579         strcatW( fullname, profile );
1580         file = CreateFileW( fullname, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0 );
1581         if (file != INVALID_HANDLE_VALUE)
1582         {
1583             DWORD written;
1584
1585             if (!WriteFile( file, buffer, buflen, &written, NULL ) || written != buflen)
1586                 ERR( "Unable to write color profile\n" );
1587             CloseHandle( file );
1588         }
1589         HeapFree( GetProcessHeap(), 0, buffer );
1590     }
1591     else strcatW( fullname, srgb );
1592
1593     required = strlenW( fullname ) + 1;
1594     if (*size < required)
1595     {
1596         *size = required;
1597         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1598         return FALSE;
1599     }
1600     if (filename)
1601     {
1602         strcpyW( filename, fullname );
1603         if (GetFileAttributesW( filename ) == INVALID_FILE_ATTRIBUTES)
1604             WARN( "color profile not found\n" );
1605     }
1606     *size = required;
1607     return TRUE;
1608 }
1609
1610 /***********************************************************************
1611  *              EnumICMProfiles (X11DRV.@)
1612  */
1613 INT X11DRV_EnumICMProfiles( PHYSDEV dev, ICMENUMPROCW proc, LPARAM lparam )
1614 {
1615     X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
1616     HKEY hkey;
1617     DWORD len_sysdir, len_path, len, index = 0;
1618     WCHAR sysdir[MAX_PATH], *profile;
1619     LONG res;
1620     INT ret;
1621
1622     TRACE("%p, %p, %ld\n", physDev, proc, lparam);
1623
1624     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, mntr_key, 0, KEY_ALL_ACCESS, &hkey ))
1625         return -1;
1626
1627     len_sysdir = GetSystemDirectoryW( sysdir, MAX_PATH );
1628     len_path = len_sysdir + sizeof(color_path) / sizeof(color_path[0]) - 1;
1629     len = 64;
1630     for (;;)
1631     {
1632         if (!(profile = HeapAlloc( GetProcessHeap(), 0, (len_path + len) * sizeof(WCHAR) )))
1633         {
1634             RegCloseKey( hkey );
1635             return -1;
1636         }
1637         res = RegEnumValueW( hkey, index, profile + len_path, &len, NULL, NULL, NULL, NULL );
1638         while (res == ERROR_MORE_DATA)
1639         {
1640             len *= 2;
1641             HeapFree( GetProcessHeap(), 0, profile );
1642             if (!(profile = HeapAlloc( GetProcessHeap(), 0, (len_path + len) * sizeof(WCHAR) )))
1643             {
1644                 RegCloseKey( hkey );
1645                 return -1;
1646             }
1647             res = RegEnumValueW( hkey, index, profile + len_path, &len, NULL, NULL, NULL, NULL );
1648         }
1649         if (res != ERROR_SUCCESS)
1650         {
1651             HeapFree( GetProcessHeap(), 0, profile );
1652             break;
1653         }
1654         memcpy( profile, sysdir, len_sysdir * sizeof(WCHAR) );
1655         memcpy( profile + len_sysdir, color_path, sizeof(color_path) - sizeof(WCHAR) );
1656         ret = proc( profile, lparam );
1657         HeapFree( GetProcessHeap(), 0, profile );
1658         if (!ret) break;
1659         index++;
1660     }
1661     RegCloseKey( hkey );
1662     return -1;
1663 }