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