Fix expose requests seek graph
[xboard.git] / draw.c
1 /*
2  * draw.c -- drawing routines for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #include "config.h"
53
54 #include <stdio.h>
55 #include <math.h>
56 #include <cairo/cairo.h>
57 #include <cairo/cairo-xlib.h>
58
59 #if STDC_HEADERS
60 # include <stdlib.h>
61 # include <string.h>
62 #else /* not STDC_HEADERS */
63 extern char *getenv();
64 # if HAVE_STRING_H
65 #  include <string.h>
66 # else /* not HAVE_STRING_H */
67 #  include <strings.h>
68 # endif /* not HAVE_STRING_H */
69 #endif /* not STDC_HEADERS */
70
71 #if ENABLE_NLS
72 #include <locale.h>
73 #endif
74
75
76 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
77 #include "common.h"
78
79 #if HAVE_LIBXPM
80 #include "pixmaps/pixmaps.h"
81 #else
82 #include "bitmaps/bitmaps.h"
83 #endif
84
85 #include "frontend.h"
86 #include "backend.h"
87 #include "xevalgraph.h"
88 #include "board.h"
89 #include "menus.h"
90 #include "dialogs.h"
91 #include "gettext.h"
92 #include "draw.h"
93
94
95 #ifdef __EMX__
96 #ifndef HAVE_USLEEP
97 #define HAVE_USLEEP
98 #endif
99 #define usleep(t)   _sleep2(((t)+500)/1000)
100 #endif
101
102 #ifdef ENABLE_NLS
103 # define  _(s) gettext (s)
104 # define N_(s) gettext_noop (s)
105 #else
106 # define  _(s) (s)
107 # define N_(s)  s
108 #endif
109
110 #define SOLID 0
111 #define OUTLINE 1
112 Boolean cairoAnimate;
113 Option *currBoard;
114 cairo_surface_t *csBoardWindow;
115 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4];   // png 256 x 256 images
116 static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn];    // scaled pieces as used
117 static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
118 static cairo_surface_t *pngBoardBitmap[2];
119 int useTexture, textureW[2], textureH[2];
120
121 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
122 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
123
124 #define White(piece) ((int)(piece) < (int)BlackPawn)
125
126 struct {
127   int x1, x2, y1, y2;
128 } gridSegments[BOARD_RANKS + BOARD_FILES + 2];
129
130 static int dual = 0;
131
132 void
133 SwitchWindow ()
134 {
135     dual = !dual;
136     currBoard = (dual ? &mainOptions[W_BOARD] : &dualOptions[3]);
137     csBoardWindow = DRAWABLE(currBoard);
138 }
139
140 #define BoardSize int
141 void
142 InitDrawingSizes (BoardSize boardSize, int flags)
143 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
144     int boardWidth, boardHeight;
145     int i;
146     static int oldWidth, oldHeight;
147     static VariantClass oldVariant;
148     static int oldMono = -1, oldTwoBoards = 0;
149     extern Widget formWidget;
150
151     if(!formWidget) return;
152
153     if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
154     oldTwoBoards = twoBoards;
155
156     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
157     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
158     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
159
160   if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
161
162     oldWidth = boardWidth; oldHeight = boardHeight;
163     CreateGrid();
164
165     /*
166      * Inhibit shell resizing.
167      */
168     ResizeBoardWindow(boardWidth, boardHeight, 0);
169
170     DelayedDrag();
171   }
172
173     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
174     // (only for xpm)
175
176   if(gameInfo.variant != oldVariant) { // and only if variant changed
177
178     for(i=0; i<2; i++) {
179         int p;
180         for(p=0; p<=(int)WhiteKing; p++)
181            pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
182         if(gameInfo.variant == VariantShogi) {
183            pngPieceBitmaps[i][(int)WhiteCannon] = pngPieceBitmaps2[i][(int)WhiteKing+1];
184            pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+2];
185            pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteKing+3];
186            pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhiteKing+4];
187            pngPieceBitmaps[i][(int)WhiteQueen] = pngPieceBitmaps2[i][(int)WhiteLance];
188         }
189 #ifdef GOTHIC
190         if(gameInfo.variant == VariantGothic) {
191            pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteSilver];
192         }
193 #endif
194         if(gameInfo.variant == VariantSChess) {
195            pngPieceBitmaps[i][(int)WhiteAngel]    = pngPieceBitmaps2[i][(int)WhiteFalcon];
196            pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteAlfil];
197         }
198     }
199     oldMono = -10; // kludge to force recreation of animation masks
200     oldVariant = gameInfo.variant;
201   }
202   CreateAnimVars();
203   oldMono = appData.monoMode;
204 }
205
206 static void
207 CreatePNGBoard (char *s, int kind)
208 {
209     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
210     if(strstr(s, ".png")) {
211         cairo_surface_t *img = cairo_image_surface_create_from_png (s);
212         if(img) {
213             useTexture |= kind + 1; pngBoardBitmap[kind] = img;
214             textureW[kind] = cairo_image_surface_get_width (img);
215             textureH[kind] = cairo_image_surface_get_height (img);
216         }
217     }
218 }
219
220 char *pngPieceNames[] = // must be in same order as internal piece encoding
221 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner", 
222   "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King", 
223   "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
224 };
225
226 cairo_surface_t *
227 ConvertPixmap (int color, int piece)
228 {
229   int i, j, stride, f, colcode[10], w, b;
230   char ch[10];
231   cairo_surface_t *res;
232   XpmPieces *p = builtInXpms + 10;
233   char **pixels = p->xpm[piece % BlackPawn][2*color];
234   int *buf;
235   sscanf(pixels[0], "%*d %*d %d", &f);
236   sscanf(appData.whitePieceColor+1, "%x", &w);
237   sscanf(appData.blackPieceColor+1, "%x", &b);
238   res = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, p->size, p->size);
239   stride = cairo_image_surface_get_stride(res);
240   buf = (int *) cairo_image_surface_get_data(res);
241   for(i=0; i<f; i++) {
242     ch[i] = pixels[i+1][0];
243     colcode[i] = 0;
244     if(strstr(pixels[i+1], "black")) colcode[i] = 0xFF000000; // 0xFF000000 + (color ? b : 0);
245     if(strstr(pixels[i+1], "white")) colcode[i] = 0xFFFFFFCC; // 0xFF000000 + w;
246   }
247   for(i=0; i<p->size; i++) {
248     for(j=0; j<p->size; j++) {
249       char c = pixels[i+f+1][j];
250       int k;
251       for(k=0; ch[k] != c && k < f; k++);
252       buf[i*p->size + j] = colcode[k];
253     }
254   }
255   cairo_surface_mark_dirty(res);
256   return res;
257 }
258
259 static void
260 ScaleOnePiece (char *name, int color, int piece)
261 {
262   float w, h;
263   char buf[MSG_SIZ];
264   cairo_surface_t *img, *cs;
265   cairo_t *cr;
266
267   if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
268     if(!*appData.pngDirectory) img = ConvertPixmap(color, piece); else {
269       snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
270       img = cairo_image_surface_create_from_png (buf);
271       if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) img = ConvertPixmap(color, piece);
272     }
273   }
274   pngPieceImages[color][piece] = img;
275   // create new bitmap to hold scaled piece image (and remove any old)
276   if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
277   pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
278   if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
279   // scaled copying of the raw png image
280   cr = cairo_create(cs);
281   w = cairo_image_surface_get_width (img);
282   h = cairo_image_surface_get_height (img);
283   cairo_scale(cr, squareSize/w, squareSize/h);
284   cairo_set_source_surface (cr, img, 0, 0);
285   cairo_paint (cr);
286   cairo_destroy (cr);
287   { // operate on bitmap to color it (king-size hack...)
288     int stride = cairo_image_surface_get_stride(cs)/4;
289     int *buf = (int *) cairo_image_surface_get_data(cs);
290     int i, j, p;
291     sscanf(color ? appData.blackPieceColor+1 : appData.whitePieceColor+1, "%x", &p); // replacement color
292     cairo_surface_flush(cs);
293     for(i=0; i<squareSize; i++) for(j=0; j<squareSize; j++) {
294         int r, a;
295         float f;
296         unsigned int c = buf[i*stride + j];
297         a = c >> 24; r = c >> 16 & 255;     // alpha and red, where red is the 'white' weight, since white is #FFFFCC in the source images
298         f = (color ? a - r : r)/255.;       // fraction of black or white in the mix that has to be replaced
299         buf[i*stride + j] = c & 0xFF000000; // alpha channel is kept at same opacity
300         buf[i*stride + j] += ((int)(f*(p&0xFF0000)) & 0xFF0000) + ((int)(f*(p&0xFF00)) & 0xFF00) + (int)(f*(p&0xFF)); // add desired fraction of new color
301         if(color) buf[i*stride + j] += r | r << 8 | r << 16; // details on black pieces get their weight added in pure white
302     }
303     cairo_surface_mark_dirty(cs);
304   }
305 }
306
307 void
308 CreatePNGPieces ()
309 {
310   int p;
311
312   for(p=0; pngPieceNames[p]; p++) {
313     ScaleOnePiece(pngPieceNames[p], 0, p);
314     ScaleOnePiece(pngPieceNames[p], 1, p);
315   }
316 }
317
318 void
319 CreateAnyPieces ()
320 {   // [HGM] taken out of main
321     CreatePNGPieces();
322     CreatePNGBoard(appData.liteBackTextureFile, 1);
323     CreatePNGBoard(appData.darkBackTextureFile, 0);
324 }
325
326 void
327 InitDrawingParams (int reloadPieces)
328 {
329     int i, p;
330     MakeColors();
331     if(reloadPieces)
332     for(i=0; i<2; i++) for(p=0; p<BlackPawn+4; p++) {
333         if(pngPieceImages[i][p]) cairo_surface_destroy(pngPieceImages[i][p]);
334         pngPieceImages[i][p] = NULL;
335     }
336     CreateAnyPieces();
337 }
338
339 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
340
341 float
342 Color (char *col, int n)
343 {
344   int c;
345   sscanf(col, "#%x", &c);
346   c = c >> 4*n & 255;
347   return c/255.;
348 }
349
350 void
351 SetPen (cairo_t *cr, float w, char *col, int dash)
352 {
353   static const double dotted[] = {4.0, 4.0};
354   static int len  = sizeof(dotted) / sizeof(dotted[0]);
355   cairo_set_line_width (cr, w);
356   cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
357   if(dash) cairo_set_dash (cr, dotted, len, 0.0);
358 }
359
360 void DrawSeekAxis( int x, int y, int xTo, int yTo )
361 {
362     cairo_t *cr;
363
364     /* get a cairo_t */
365     cr = cairo_create (csBoardWindow);
366
367     cairo_move_to (cr, x, y);
368     cairo_line_to(cr, xTo, yTo );
369
370     SetPen(cr, 2, "#000000", 0);
371     cairo_stroke(cr);
372
373     /* free memory */
374     cairo_destroy (cr);
375     GraphExpose(currBoard, x-1, yTo-1, xTo-x+2, y-yTo+2);
376 }
377
378 void DrawSeekBackground( int left, int top, int right, int bottom )
379 {
380     cairo_t *cr = cairo_create (csBoardWindow);
381
382     cairo_rectangle (cr, left, top, right-left, bottom-top);
383
384     cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
385     cairo_fill(cr);
386
387     /* free memory */
388     cairo_destroy (cr);
389     GraphExpose(currBoard, left, top, right-left, bottom-top);
390 }
391
392 void DrawSeekText(char *buf, int x, int y)
393 {
394     cairo_t *cr = cairo_create (csBoardWindow);
395
396     cairo_select_font_face (cr, "Sans",
397                             CAIRO_FONT_SLANT_NORMAL,
398                             CAIRO_FONT_WEIGHT_NORMAL);
399
400     cairo_set_font_size (cr, 12.0);
401
402     cairo_move_to (cr, x, y+4);
403     cairo_set_source_rgba(cr, 0, 0, 0,1.0);
404     cairo_show_text( cr, buf);
405
406     /* free memory */
407     cairo_destroy (cr);
408     GraphExpose(currBoard, x-5, y-10, 60, 15);
409 }
410
411 void DrawSeekDot(int x, int y, int colorNr)
412 {
413     cairo_t *cr = cairo_create (csBoardWindow);
414     int square = colorNr & 0x80;
415     colorNr &= 0x7F;
416
417     if(square)
418         cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*(squareSize/9), 2*(squareSize/9));
419     else
420         cairo_arc(cr, x, y, squareSize/9, 0.0, 2*M_PI);
421
422     SetPen(cr, 2, "#000000", 0);
423     cairo_stroke_preserve(cr);
424     switch (colorNr) {
425       case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
426       case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
427       default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
428     }
429     cairo_fill(cr);
430
431     /* free memory */
432     cairo_destroy (cr);
433     GraphExpose(currBoard, x-squareSize/8, y-squareSize/8, 2*(squareSize/8), 2*(squareSize/8));
434 }
435
436 void
437 InitDrawingHandle (Option *opt)
438 {
439     csBoardWindow = DRAWABLE(opt);
440 }
441
442 void
443 CreateGrid ()
444 {
445     int i, j;
446
447     if (lineGap == 0) return;
448
449     /* [HR] Split this into 2 loops for non-square boards. */
450
451     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
452         gridSegments[i].x1 = 0;
453         gridSegments[i].x2 =
454           lineGap + BOARD_WIDTH * (squareSize + lineGap);
455         gridSegments[i].y1 = gridSegments[i].y2
456           = lineGap / 2 + (i * (squareSize + lineGap));
457     }
458
459     for (j = 0; j < BOARD_WIDTH + 1; j++) {
460         gridSegments[j + i].y1 = 0;
461         gridSegments[j + i].y2 =
462           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
463         gridSegments[j + i].x1 = gridSegments[j + i].x2
464           = lineGap / 2 + (j * (squareSize + lineGap));
465     }
466 }
467
468 void
469 DrawGrid()
470 {
471   /* draws a grid starting around Nx, Ny squares starting at x,y */
472   int i;
473   cairo_t *cr;
474
475   /* get a cairo_t */
476   cr = cairo_create (csBoardWindow);
477
478   cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
479   SetPen(cr, lineGap, "#000000", 0);
480
481   /* lines in X */
482   for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
483     {
484       cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
485       cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
486       cairo_stroke (cr);
487     }
488
489   /* free memory */
490   cairo_destroy (cr);
491
492   return;
493 }
494
495 void
496 DrawBorder (int x, int y, int type)
497 {
498     cairo_t *cr;
499     char *col;
500
501     switch(type) {
502         case 0: col = "#000000"; break;
503         case 1: col = appData.highlightSquareColor; break;
504         case 2: col = appData.premoveHighlightColor; break;
505     }
506     cr = cairo_create(csBoardWindow);
507     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
508     cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
509     SetPen(cr, lineGap, col, 0);
510     cairo_stroke(cr);
511     cairo_destroy(cr);
512     GraphExpose(currBoard, x - lineGap/2, y - lineGap/2, squareSize+2*lineGap, squareSize+2*lineGap);
513 }
514
515 static int
516 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
517 {
518     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
519     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
520     *x0 = 0; *y0 = 0;
521     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
522     if(textureW[kind] < W*squareSize)
523         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
524     else
525         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
526     if(textureH[kind] < H*squareSize)
527         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
528     else
529         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
530     return 1;
531 }
532
533 void
534 DrawLogo (Option *opt, void *logo)
535 {
536     cairo_surface_t *img;
537     cairo_t *cr;
538     int w, h;
539
540     if(!logo || !opt) return;
541     img = cairo_image_surface_create_from_png (logo);
542     w = cairo_image_surface_get_width (img);
543     h = cairo_image_surface_get_height (img);
544     cr = cairo_create(DRAWABLE(opt));
545     cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
546     cairo_set_source_surface (cr, img, 0, 0);
547     cairo_paint (cr);
548     cairo_destroy (cr);
549     cairo_surface_destroy (img);
550     GraphExpose(opt, 0, 0, appData.logoSize, appData.logoSize/2);
551 }
552
553 static void
554 BlankSquare (cairo_surface_t *dest, int x, int y, int color, ChessSquare piece, int fac)
555 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
556     int x0, y0;
557     cairo_t *cr;
558
559     cr = cairo_create (dest);
560
561     if ((useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
562             cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
563             cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
564             cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
565             cairo_fill (cr);
566             cairo_destroy (cr);
567     } else { // evenly colored squares
568         char *col;
569         switch (color) {
570           case 0: col = appData.darkSquareColor; break;
571           case 1: col = appData.lightSquareColor; break;
572           case 2: col = "#000000"; break;
573         }
574         SetPen(cr, 2.0, col, 0);
575         cairo_rectangle (cr, x, y, squareSize, squareSize);
576         cairo_fill (cr);
577         cairo_destroy (cr);
578     }
579 }
580
581 static void
582 pngDrawPiece (cairo_surface_t *dest, ChessSquare piece, int square_color, int x, int y)
583 {
584     int kind, p = piece;
585     cairo_t *cr;
586
587     if ((int)piece < (int) BlackPawn) {
588         kind = 0;
589     } else {
590         kind = 1;
591         piece -= BlackPawn;
592     }
593     if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
594     BlankSquare(dest, x, y, square_color, piece, 1); // erase previous contents with background
595     cr = cairo_create (dest);
596     cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
597     cairo_paint(cr);
598     cairo_destroy (cr);
599 }
600
601 void
602 DoDrawDot (cairo_surface_t *cs, int marker, int x, int y, int r)
603 {
604         cairo_t *cr;
605
606         cr = cairo_create(cs);
607         cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
608         if(appData.monoMode) {
609             SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
610             cairo_stroke_preserve(cr);
611             SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
612         } else {
613             SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
614         }
615         cairo_fill(cr);
616
617         cairo_destroy(cr);
618 }
619
620 void
621 DrawDot (int marker, int x, int y, int r)
622 { // used for atomic captures; no need to draw on backup
623   DoDrawDot(csBoardWindow, marker, x, y, r);
624 }
625
626 void
627 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
628 {   // basic front-end board-draw function: takes care of everything that can be in square:
629     // piece, background, coordinate/count, marker dot
630     cairo_t *cr;
631
632     if (piece == EmptySquare) {
633         BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
634     } else {
635         pngDrawPiece(csBoardWindow, piece, square_color, x, y);
636     }
637
638     if(align) { // square carries inscription (coord or piece count)
639         int xx = x, yy = y;
640         cairo_text_extents_t te;
641
642         cr = cairo_create (csBoardWindow);
643         cairo_select_font_face (cr, "Sans",
644                     CAIRO_FONT_SLANT_NORMAL,
645                     CAIRO_FONT_WEIGHT_BOLD);
646
647         cairo_set_font_size (cr, squareSize/4);
648         // calculate where it goes
649         cairo_text_extents (cr, string, &te);
650
651         if (align == 1) {
652             xx += squareSize - te.width - te.x_bearing - 1;
653             yy += squareSize - te.height - te.y_bearing - 1;
654         } else if (align == 2) {
655             xx += te.x_bearing + 1, yy += -te.y_bearing + 1;
656         } else if (align == 3) {
657             xx += squareSize - te.width -te.x_bearing - 1;
658             yy += -te.y_bearing + 3;
659         } else if (align == 4) {
660             xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
661         }
662
663         cairo_move_to (cr, xx-1, yy);
664         if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
665         else          cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
666         cairo_show_text (cr, string);
667         cairo_destroy (cr);
668     }
669
670     if(marker) { // print fat marker dot, if requested
671         DoDrawDot(csBoardWindow, marker, x + squareSize/4, y+squareSize/4, squareSize/2);
672     }
673 }
674
675 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
676
677 /*      Masks for XPM pieces. Black and white pieces can have
678         different shapes, but in the interest of retaining my
679         sanity pieces must have the same outline on both light
680         and dark squares, and all pieces must use the same
681         background square colors/images.                */
682
683 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
684
685 static void
686 InitAnimState (AnimNr anr)
687 {
688     if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
689     if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
690     c_animBufs[anr+4] = csBoardWindow;
691     c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
692     c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
693 }
694
695 void
696 CreateAnimVars ()
697 {
698   InitAnimState(Game);
699   InitAnimState(Player);
700 }
701
702 static void
703 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
704 {
705   static cairo_t *pieceSource;
706   pieceSource = cairo_create (dest);
707   cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
708   if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
709   else cairo_paint(pieceSource);
710   cairo_destroy (pieceSource);
711 }
712
713 void
714 InsertPiece (AnimNr anr, ChessSquare piece)
715 {
716     CairoOverlayPiece(piece, c_animBufs[anr]);
717 }
718
719 void
720 DrawBlank (AnimNr anr, int x, int y, int startColor)
721 {
722     BlankSquare(c_animBufs[anr+2], x, y, startColor, EmptySquare, 0);
723 }
724
725 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
726                  int srcX, int srcY, int width, int height, int destX, int destY)
727 {
728         cairo_t *cr = cairo_create (c_animBufs[anr+destBuf]);
729         cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
730         cairo_rectangle (cr, destX, destY, width, height);
731         cairo_fill (cr);
732         cairo_destroy (cr);
733         if(c_animBufs[anr+destBuf] == csBoardWindow)
734             GraphExpose(currBoard, destX, destY, squareSize, squareSize);
735 }
736
737 void
738 SetDragPiece (AnimNr anr, ChessSquare piece)
739 {
740 }
741
742 /* [AS] Arrow highlighting support */
743
744 void
745 DoDrawPolygon (cairo_surface_t *cs, Pnt arrow[], int nr)
746 {
747     cairo_t *cr;
748     int i;
749     cr = cairo_create (cs);
750     cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
751     for (i=0;i<nr;i++) {
752         cairo_line_to(cr, arrow[i].x, arrow[i].y);
753     }
754     if(appData.monoMode) { // should we always outline arrow?
755         cairo_line_to(cr, arrow[0].x, arrow[0].y);
756         SetPen(cr, 2, "#000000", 0);
757         cairo_stroke_preserve(cr);
758     }
759     SetPen(cr, 2, appData.highlightSquareColor, 0);
760     cairo_fill(cr);
761
762     /* free memory */
763     cairo_destroy (cr);
764 }
765
766 void
767 DrawPolygon (Pnt arrow[], int nr)
768 {
769     DoDrawPolygon(csBoardWindow, arrow, nr);
770 //    if(!dual) DoDrawPolygon(csBoardBackup, arrow, nr);
771 }
772
773