CwChessboard.cc
Go to the documentation of this file.
1 // cwchessboard -- A GTK+ chessboard widget
2 //
3 // Copyright (C) 2006, 2008 Carlo Wood
4 //
5 // Carlo Wood, Run on IRC <carlo@alinoe.com>
6 // RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt
7 // Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 
23 /*! \file CwChessboard.cc
24 
25  \brief This file contains the implementation of the GTK+ widget %CwChessboard.
26 
27  You can compile this file with C or C++ compiler (just renaming
28  it to .c or .cc should do the trick with most build systems).
29  If you compile it as C source, then you need to generate a
30  file CwChessboard-CONST.h from this file with the following
31  commands: <tt>./gen.sh CwChessboard.c; g++ -o gen gen.cc; ./gen > %CwChessboard-CONST.h</tt>
32  where the contents of the script 'gen.sh' is:
33  \code
34  echo "#include <iostream>" > gen.cc
35  echo "#include <cmath>" >> gen.cc
36  echo >> gen.cc
37  echo "#define CWCHESSBOARD_CONST_(name, expr) expr; \\"
38  echo " std::cout << \"#define CWCHESSBOARD_CONST_\" << #name << \" \" << name << std::endl;" >> gen.cc
39  echo "int main()" >> gen.cc
40  echo "{" >> gen.cc
41  mawk 'BEGIN { inl=0; } \
42  /static .*= CWCHESSBOARD_CONST_\(.*;/ { sub(/^[ \t]+/, ""); printf(" %s\n", $0); inl=0; } \
43  // { if (inl) { sub(/^[ \t]+/, ""); printf(" %s\n", $0); inl=0; } } \
44  /static .*= CWCHESSBOARD_CONST_\([^;]*$/ { sub(/^[ \t]+/, ""); printf(" %s\n", $0); inl=1; }' \
45  $1 >> gen.cc
46  echo "}" >> gen.cc
47  \endcode
48 
49 */
50 
51 #include "sys.h"
52 #if defined(__cplusplus) && defined(CWDEBUG)
53 #include "debug.h"
54 #else
55 #undef Dout
56 #undef DoutFatal
57 #define Dout(channel, output)
58 #define DoutFatal(channel, output) do { } while(0)
59 #ifndef _GNU_SOURCE
60 #define _GNU_SOURCE
61 #endif
62 #endif
63 
64 #include <glib-object.h>
65 #include <math.h>
66 #include <stdlib.h>
67 #include <string.h> // memset
68 
69 #ifdef __cplusplus
70 #define CONST(name, expr) expr
71 #else
72 #include "CwChessboard-CONST.h"
73 #define CONST(name, expr) CWCHESSBOARD_CONST_##name
74 #define CWCHESSBOARD_DEFINE_INLINE 1
75 #endif
76 
77 #include "CwChessboard.h"
78 #include "CwChessboardCodes.h"
79 
80 // Fine tuning.
81 #define CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET 0
82 #define CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER 1
83 #define CW_CHESSBOARD_EXPOSE_ALWAYS_CLEAR_BACKGROUND 1 // Needed to erase things like menu's.
84 #define CW_CHESSBOARD_EXPOSE_DEBUG 0
85 
86 #define CW_CHESSBOARD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), CW_TYPE_CHESSBOARD, CwChessboardPrivate))
87 
88 G_DEFINE_TYPE(CwChessboard, cw_chessboard, GTK_TYPE_DRAWING_AREA);
89 
90 static void cw_chessboard_destroy(GtkObject* object);
91 static void cw_chessboard_finalize(GObject* object);
92 static void cw_chessboard_realize(GtkWidget* widget);
93 static void cw_chessboard_unrealize(GtkWidget* widget);
94 static void cw_chessboard_size_request(GtkWidget* widget, GtkRequisition* requisition);
95 static void cw_chessboard_size_allocate(GtkWidget* widget, GtkAllocation* allocation);
96 static gboolean cw_chessboard_expose(GtkWidget* chessboard, GdkEventExpose* event);
97 static gboolean cw_chessboard_motion_notify(GtkWidget* chessboard, GdkEventMotion* event);
98 static gboolean cw_chessboard_default_motion_notify(GtkWidget* chessboard, GdkEventMotion* event);
99 
100 static void recreate_hud_layers(CwChessboard* chessboard);
101 static void redraw_hud_layer(CwChessboard* chessboard, guint hud);
102 
103 /*
104  * Array index type for chessboard square.
105 * /
106 typedef gint BoardIndex;
107 
108 // The number of squares being drawn on the screen (for debugging purposes).
109 // If set to less than 8, only the bottom-left corner of the board is show.
110 gint const squares = 8;
111 
112 // The absolute minimum size of a side of a square in pixels.
113 gint const min_sside = 12;
114 
115 // The number of Head-Up Displays.
116 #ifdef __cplusplus
117 guint const number_of_hud_layers = 2;
118 #else
119 #define number_of_hud_layers 2
120 #endif
121 
122 /*
123  * Convert a (column, row) pair (each running from 0 till 7) to a BoardIndex value.
124 * /
125 inline static BoardIndex convert_colrow2index(gint col, gint row)
126 {
127  return col | (row << 3);
128 }
129 
130 /*
131  * Convert a BoardIndex value to the column of the corresponding square.
132  * A value of 0 corresponds to 'a', 1 corresponds to 'b' and so on, till 7 corresponding to column 'h'.
133 * /
134 inline static gint convert_index2column(BoardIndex index)
135 {
136  // The last 3 bits contain the col.
137  return index & 0x7;
138 }
139 
140 /*
141  * Convert a BoardIndex value to the row of the corresponding square.
142 * /
143 inline static gint convert_index2row(BoardIndex index)
144 {
145  // Bits 3 till 6 contain the row. Higher bits are zero.
146  return index >> 3;
147 }
148 
149 /*
150  * Convert a BoardIndex value to the top-left widget coordinates of the corresponding square.
151 * /
152 inline static void convert_index2xy(CwChessboard* chessboard, BoardIndex index, gint* x, gint* y)
153 {
154  cw_chessboard_colrow2xy(chessboard, convert_index2column(index), convert_index2row(index), x, y);
155 }
156 
157 /*
158  * Convert the widget coordinates (\a x, \a y) to the BoardIndex of the corresponding square.
159 * /
160 inline static BoardIndex convert_xy2index(CwChessboard* chessboard, gdouble x, gdouble y)
161 {
162  gint col = cw_chessboard_x2col(chessboard, x);
163  if ((col & ~0x7))
164  return -1;
165  gint row = cw_chessboard_y2row(chessboard, y);
166  if ((row & ~0x7))
167  return -1;
168  return convert_colrow2index(col, row);
169 }
170 
171 /*
172  * An RGB color as used by cairo.
173 * /
174 struct _CairoColor {
175  double red;
176  double green;
177  double blue;
178 };
179 typedef struct _CairoColor CairoColor;
180 
181 static int const pawn = 0;
182 static int const rook = 1;
183 static int const knight = 2;
184 static int const bishop = 3;
185 static int const queen = 4;
186 static int const king = 5;
187 
188 // A piece that is not bound to a square.
190  gint pixmap_x; // Current x position of the top-left of the piece, in pixmap coordinates.
191  gint pixmap_y; // Current y position of the top-left of the piece, in pixmap coordinates.
192  CwChessboardCode code; // Which piece and which color.
193  gboolean moved; // Temporary set between a move and a redraw.
194  gboolean pointer_device; // Set if this is the pointer device.
195 };
196 typedef struct _FloatingPiece FloatingPiece;
197 
198 // Pixmap cache of the size of one square.
199 struct _SquareCache {
200  cairo_surface_t* surface; // Pointer to the cairo surface of the pixmap.
201 };
202 typedef struct _SquareCache SquareCache;
203 
204 // An arrow drawn on the board.
205 struct _Arrow {
206  gint begin_col;
207  gint begin_row;
208  gint end_col;
209  gint end_row;
210  CairoColor color;
211  guint64 has_content[number_of_hud_layers];
212 };
213 typedef struct _Arrow Arrow;
214 
215 /*
216  * The private members of class CwChessboard.
217 * /
219 {
220  // Colors.
221  CairoColor dark_square_color; // The color of the dark squares.
222  guint32 dark_square_color_pixel;
223  CairoColor light_square_color; // The color of the light squares.
224  guint32 light_square_color_pixel;
225  CairoColor board_border_color; // The color of the border.
226  guint32 board_border_color_pixel;
227  CairoColor white_piece_fill_color; // The fill color of the white pieces.
228  guint32 white_piece_fill_color_pixel;
229  CairoColor white_piece_line_color; // The line color of the white pieces.
230  guint32 white_piece_line_color_pixel;
231  CairoColor black_piece_fill_color; // The fill color of the black pieces.
232  guint32 black_piece_fill_color_pixel;
233  CairoColor black_piece_line_color; // The line color of the black pieces.
234  guint32 black_piece_line_color_pixel;
235  GdkColor widget_background_color; // The background color of the widget.
236  CairoColor color_palet[31]; // 31 colors for markers and backgrounds.
237  guint32 allocated_colors_mask; // The colors of color_palet that are used.
238  CairoColor cursor_color; // The color of the cursor.
239  guint32 cursor_color_pixel;
240 
241  // Other configuration.
242  gboolean draw_border; // Do we need to draw a border?
243  gboolean flip_board; // White or black at the bottom?
244  gboolean draw_turn_indicators; // Do we need to draw turn indicators?
245  gboolean active_turn_indicator; // Is whites turn indicator active (or blacks?).
246  gboolean has_hud_layer[number_of_hud_layers]; // Do we need to call the user provided HUD draw function?
247  gdouble marker_thickness; // Thickness of the markers as fraction of sside.
248  gboolean marker_below; // Whether the markers are drawn below HUD 0.
249  gdouble cursor_thickness; // Thickness of the cursor as faction of sside.
250  gboolean show_cursor; // True if the cursor should be visible.
251 
252  // Sizes and offsets.
253  gint sside; // The size of one side of a square.
254  gint border_width; // The border width.
255  gint top_left_pixmap_x; // The x offset of the top-left corner of the pixmap.
256  gint top_left_pixmap_y; // The y offset of the top-left corner of the pixmap.
257  gint bottom_right_pixmap_x; // The x offset of the bottom-right corner of the pixmap.
258  gint bottom_right_pixmap_y; // The y offset of the bottom-right corner of the pixmap.
259  gint pixmap_top_left_a8_x; // The x offset of the top-left of square a8, in pixmap coordinates.
260  gint pixmap_top_left_a8_y; // The y offset of the top-left of square a8, in pixmap coordinates.
261  GdkRegion* chessboard_region; // The rectangular region where the chessboard resides.
262  gint marker_thickness_px; // Thickness of the markers.
263  gint cursor_thickness_px; // Thickness of the cursor.
264  gint cursor_col; // Current cursor column.
265  gint cursor_row; // Current cursor row.
266 
267  // Buffers and caches.
268  GdkPixmap* pixmap; // X server side drawing buffer.
269  cairo_t* cr; // Corresponding Cairo context.
270  SquareCache piece_pixmap[12]; // 12 images using piece_buffer.
271  cairo_surface_t* hud_layer_surface[number_of_hud_layers]; // The HUD layers.
272  gint64 hud_has_content[number_of_hud_layers]; // Which HUD squares are not entirely transparent?
273  gint64 hud_need_redraw[number_of_hud_layers]; // Which HUD squares need a redraw?
274  SquareCache hatching_pixmap; // Cache used for the hatch lines.
275 
276  CwChessboardCode board_codes[64]; // The chessboard, contains pieces and background colors.
277  gint64 need_redraw_invalidated; // Which squares are invalidated?
278  gint64 need_redraw; // Which squares need a redraw?
279 
280  gsize number_of_floating_pieces; // Current number of floating pieces.
281  FloatingPiece floating_piece[32]; // Current floating pieces.
282  gint floating_piece_handle; // The handle of the floating piece under the pointer device, or -1.
283  gboolean redraw_background; // Set when the window was recently resized (reset in the expose function).
284 
285  GPtrArray* arrows; // Array with pointers to Arrow objects.
286 };
287 
288 static CwChessboardCode const color_mask = 0x0001;
289 static CwChessboardCode const piece_mask = 0x000e;
290 static CwChessboardCode const piece_color_mask = 0x000f;
291 static CwChessboardCode const bghandle_mask = 0x01f0; // BackGround of square.
292 static CwChessboardCode const mahandle_mask = 0x3e00; // MArker.
293 
294 static inline gboolean is_empty_square(CwChessboardCode code)
295 {
296  return (code & piece_mask) == 0;
297 }
298 
299 // This assumes it is a piece (not an empty square).
300 static inline int convert_code2piece(CwChessboardCode code)
301 {
302  return ((code & piece_mask) >> 1) - 1;
303 }
304 
305 // This assumes it is a piece (not an empty square).
306 static inline int convert_code2piece_pixmap_index(CwChessboardCode code)
307 {
308  return (code & piece_color_mask) - 2;
309 }
310 
311 // This assumes it is a piece (not an empty square).
312 static inline gboolean is_white_piece(CwChessboardCode code)
313 {
314  return (code & color_mask) != 0;
315 }
316 
317 static inline CwChessboardCode convert_piece2code(int piece, gboolean white)
318 {
319  return (unsigned char)((piece << 1) | (white ? 3 : 2));
320 }
321 
322 static inline gint convert_code2bghandle(CwChessboardCode code)
323 {
324  return (code & bghandle_mask) >> 4;
325 }
326 
327 // This should be OR-ed with the other data.
328 static inline CwChessboardCode convert_bghandle2code(gint bghandle)
329 {
330  return bghandle << 4;
331 }
332 
333 static inline gint convert_code2mahandle(CwChessboardCode code)
334 {
335  return (code & mahandle_mask) >> 9;
336 }
337 
338 // This should be OR-ed with the other data.
339 static inline CwChessboardCode convert_mahandle2code(gint mahandle)
340 {
341  return mahandle << 9;
342 }
343 
344 static inline gboolean is_inside_board(gint col, gint row)
345 {
346  return !((col | row) & ~0x7);
347 }
348 
349 // One-time initialization of the "class" of CwChessboard.
350 static void cw_chessboard_class_init(CwChessboardClass* chessboard_class)
351 {
352  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(chessboard_class);
353  GtkObjectClass* object_class = GTK_OBJECT_CLASS(widget_class);
354  GObjectClass* gobject_class = G_OBJECT_CLASS(object_class);
355 
356  g_type_class_add_private(object_class, sizeof(CwChessboardPrivate));
357 
358  gobject_class->finalize = cw_chessboard_finalize;
359 
360  object_class->destroy = cw_chessboard_destroy;
361 
362  widget_class->expose_event = cw_chessboard_expose;
363  widget_class->motion_notify_event = cw_chessboard_default_motion_notify;
364 
365  widget_class->realize = cw_chessboard_realize;
366  widget_class->size_request = cw_chessboard_size_request;
367  widget_class->size_allocate = cw_chessboard_size_allocate;
368  widget_class->unrealize = cw_chessboard_unrealize;
369 
373  chessboard_class->draw_piece[pawn] = cw_chessboard_draw_pawn;
374  chessboard_class->draw_piece[rook] = cw_chessboard_draw_rook;
375  chessboard_class->draw_piece[knight] = cw_chessboard_draw_knight;
376  chessboard_class->draw_piece[bishop] = cw_chessboard_draw_bishop;
377  chessboard_class->draw_piece[queen] = cw_chessboard_draw_queen;
378  chessboard_class->draw_piece[king] = cw_chessboard_draw_king;
381  chessboard_class->cursor_left_chessboard = NULL;
382  chessboard_class->cursor_entered_square = NULL;
383 }
384 
385 static void cw_chessboard_destroy(GtkObject* object)
386 {
387  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_destroy(" << object << ")");
388  GTK_OBJECT_CLASS(cw_chessboard_parent_class)->destroy(object);
389 }
390 
391 // Initialization of CwChessboard instances.
392 static void cw_chessboard_init(CwChessboard* chessboard)
393 {
394  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_init(" << chessboard << ")");
395 
396  CwChessboardPrivate* priv = CW_CHESSBOARD_GET_PRIVATE(chessboard);
397  GdkColormap* colormap = gtk_widget_get_colormap(GTK_WIDGET(chessboard));
398 
399  // Initialize local pointer to private struct for faster access.
400  chessboard->priv = priv;
401 
402  priv->draw_border = TRUE;
403  priv->flip_board = FALSE;
404  priv->draw_turn_indicators = TRUE;
405  priv->active_turn_indicator = TRUE;
406  memset(priv->piece_pixmap, 0, sizeof(priv->piece_pixmap));
407  memset(priv->has_hud_layer, 0, sizeof(priv->has_hud_layer));
408  priv->hatching_pixmap.surface = NULL;
409  priv->sside = -1;
410  priv->marker_thickness = 0.08;
411  priv->marker_below = FALSE;
412  priv->cursor_thickness = 0.04;
413  priv->show_cursor = FALSE;
414  priv->cursor_col = -1;
415  priv->cursor_row = -1;
416  * (gint*)(&chessboard->sside) = 0;
417  memset(priv->board_codes, 0, sizeof(priv->board_codes));
418  priv->number_of_floating_pieces = 0;
419  priv->floating_piece_handle = -1;
420  memset(priv->floating_piece, 0, sizeof(priv->floating_piece));
421  priv->allocated_colors_mask = 0;
422  priv->pixmap = NULL;
423  priv->top_left_pixmap_x = 0;
424  priv->top_left_pixmap_y = 0;
425  priv->bottom_right_pixmap_x = 0;
426  priv->bottom_right_pixmap_y = 0;
427  priv->cr = NULL;
428  priv->chessboard_region = NULL;
429  memset(priv->hud_layer_surface, 0, sizeof(priv->hud_layer_surface));
430  priv->arrows = g_ptr_array_new();
431 
432  priv->board_codes[0] = white_rook;
433  priv->board_codes[1] = white_knight;
434  priv->board_codes[2] = white_bishop;
435  priv->board_codes[3] = white_queen;
436  priv->board_codes[4] = white_king;
437  priv->board_codes[5] = white_bishop;
438  priv->board_codes[6] = white_knight;
439  priv->board_codes[7] = white_rook;
440  priv->board_codes[8] = white_pawn;
441  priv->board_codes[9] = white_pawn;
442  priv->board_codes[10] = white_pawn;
443  priv->board_codes[11] = white_pawn;
444  priv->board_codes[12] = white_pawn;
445  priv->board_codes[13] = white_pawn;
446  priv->board_codes[14] = white_pawn;
447  priv->board_codes[15] = white_pawn;
448  priv->board_codes[63] = black_rook;
449  priv->board_codes[62] = black_knight;
450  priv->board_codes[61] = black_bishop;
451  priv->board_codes[60] = black_king;
452  priv->board_codes[59] = black_queen;
453  priv->board_codes[58] = black_bishop;
454  priv->board_codes[57] = black_knight;
455  priv->board_codes[56] = black_rook;
456  priv->board_codes[55] = black_pawn;
457  priv->board_codes[54] = black_pawn;
458  priv->board_codes[53] = black_pawn;
459  priv->board_codes[52] = black_pawn;
460  priv->board_codes[51] = black_pawn;
461  priv->board_codes[50] = black_pawn;
462  priv->board_codes[49] = black_pawn;
463  priv->board_codes[48] = black_pawn;
464 
465  // The default values for the dark square color (a1).
466  gushort const dark_square_red = 45875;
467  gushort const dark_square_green = 58981;
468  gushort const dark_square_blue = 45875;
469 
470  // The default values for the light square color (h1).
471  gushort const light_square_red = 65535;
472  gushort const light_square_green = 65535;
473  gushort const light_square_blue = 58981;
474 
475  // Make sure that these colors are different with the current color map.
476  // If the they are not different then, shift the light squares to white
477  // and the dark squares to black until they ARE different.
478 
479  GdkColor dark_square_color;
480  GdkColor light_square_color;
481 
482  // If you get a compile error here, add -std=c99 to CFLAGS.
483  for (double x = 0.0; x <= 1.0; x += 0.01)
484  {
485  dark_square_color.red = (gushort)((1.0 - x) * dark_square_red);
486  dark_square_color.green = (gushort)((1.0 - x) * dark_square_green);
487  dark_square_color.blue = (gushort)((1.0 - x) * dark_square_blue);
488  if (!gdk_colormap_alloc_color(colormap,& dark_square_color, FALSE, TRUE))
489  DoutFatal(dc::fatal, "gdk_colormap_alloc_color failed to allocate dark_square_color (" <<
490  dark_square_color.red << ", " <<
491  dark_square_color.green << ", " <<
492  dark_square_color.blue << ")");
493 
494  light_square_color.red = light_square_red + (gushort)(x * (65535 - light_square_red));
495  light_square_color.green = light_square_green + (gushort)(x * (65535 - light_square_green));
496  light_square_color.blue = light_square_blue + (gushort)(x * (65535 - light_square_blue));
497  if (!gdk_colormap_alloc_color(colormap,& light_square_color, FALSE, TRUE))
498  DoutFatal(dc::fatal, "gdk_colormap_alloc_color failed to allocate light_square_color (" <<
499  light_square_color.red << ", " <<
500  light_square_color.green << ", " <<
501  light_square_color.blue << ")");
502 
503  if (dark_square_color.red != light_square_color.red ||
504  dark_square_color.green != light_square_color.green ||
505  dark_square_color.blue != light_square_color.blue)
506  break;
507  }
508 
509  // Store the actual colors.
510  cw_chessboard_set_dark_square_color(chessboard,& dark_square_color);
511  cw_chessboard_set_light_square_color(chessboard,& light_square_color);
512 
513  // Set the default widget's background color equal to the color of the light squares.
514  priv->widget_background_color = light_square_color;
515 
516  // Set default values for the colors used when drawing the pieces.
517  // White
518  priv->white_piece_fill_color.red = 1.0;
519  priv->white_piece_fill_color.green = 1.0;
520  priv->white_piece_fill_color.blue = 1.0;
521  // Black
522  priv->white_piece_line_color.red = 0.0;
523  priv->white_piece_line_color.green = 0.0;
524  priv->white_piece_line_color.blue = 0.0;
525  // Black
526  priv->black_piece_fill_color.red = 0.0;
527  priv->black_piece_fill_color.green = 0.0;
528  priv->black_piece_fill_color.blue = 0.0;
529  // White
530  priv->black_piece_line_color.red = 1.0;
531  priv->black_piece_line_color.green = 1.0;
532  priv->black_piece_line_color.blue = 1.0;
533  // Cursor
534  priv->cursor_color.red = 1.0;
535  priv->cursor_color.green = 0.0;
536  priv->cursor_color.blue = 0.0;
537 
538  // We do the buffering ourselves.
539  gtk_widget_set_double_buffered(GTK_WIDGET(chessboard), FALSE);
540 
541  gtk_widget_add_events(GTK_WIDGET(chessboard),
542  GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
543  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
544 
545  // Catch motion notify events for cursor handling.
546  g_signal_connect(G_OBJECT(chessboard), "motion-notify-event", G_CALLBACK(cw_chessboard_motion_notify), NULL);
547 }
548 
549 static void cw_chessboard_finalize(GObject* object)
550 {
551  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_finalize(" << object << ")");
552  CwChessboard* chessboard = CW_CHESSBOARD(object);
553  g_ptr_array_free(chessboard->priv->arrows, TRUE);
554  G_OBJECT_CLASS(cw_chessboard_parent_class)->finalize(object);
555 }
556 
557 static void invalidate_border(CwChessboard* chessboard)
558 {
559  if (GTK_WIDGET_REALIZED(chessboard))
560  {
561  CwChessboardPrivate* priv = chessboard->priv;
562  GdkWindow* window = GTK_WIDGET(chessboard)->window;
563  GdkRectangle pixmap_rect;
564  pixmap_rect.x = priv->top_left_pixmap_x;
565  pixmap_rect.y = priv->top_left_pixmap_y;
566  pixmap_rect.width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
567  pixmap_rect.height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
568  GdkRegion* border_region = gdk_region_rectangle(&pixmap_rect);
569  gdk_region_subtract(border_region, priv->chessboard_region);
570  gdk_window_invalidate_region(window, border_region, FALSE);
571  gdk_region_destroy(border_region);
572  }
573 }
574 
575 static void invalidate_turn_indicators(CwChessboard* chessboard)
576 {
577  if (GTK_WIDGET_REALIZED(chessboard))
578  {
579  CwChessboardPrivate* priv = chessboard->priv;
580 
581  gint const border_width = priv->border_width;
582  gint const border_shadow_width = 2;
583  gint const edge_width = border_width - border_shadow_width - 1;
584  gint const side = squares * priv->sside;
585 
586  double const factor = 0.085786; // (1/sqrt(2) − 0.5)/(1 + sqrt(2)).
587  int const dx = (int)ceil((edge_width + 1) * factor);
588 
589  GdkRectangle top_indicator_rect, bottom_indicator_rect;
590  top_indicator_rect.x = priv->top_left_pixmap_x + priv->pixmap_top_left_a8_x + side + 1 - dx;
591  top_indicator_rect.y = priv->top_left_pixmap_y + priv->pixmap_top_left_a8_y - 1 - edge_width;
592  top_indicator_rect.width = edge_width + dx;
593  top_indicator_rect.height = edge_width;
594  GdkRegion* indicator_region = gdk_region_rectangle(&top_indicator_rect);
595  bottom_indicator_rect.x = top_indicator_rect.x;
596  bottom_indicator_rect.y = top_indicator_rect.y + edge_width + side + 2;
597  bottom_indicator_rect.width = edge_width + dx;
598  bottom_indicator_rect.height = edge_width;
599  gdk_region_union_with_rect(indicator_region,& bottom_indicator_rect);
600  top_indicator_rect.x += dx;
601  top_indicator_rect.y += edge_width;
602  top_indicator_rect.width = edge_width;
603  top_indicator_rect.height = dx;
604  gdk_region_union_with_rect(indicator_region,& top_indicator_rect);
605  bottom_indicator_rect.x += dx;
606  bottom_indicator_rect.y -= dx;
607  bottom_indicator_rect.width = edge_width;
608  bottom_indicator_rect.height = dx;
609  gdk_region_union_with_rect(indicator_region,& bottom_indicator_rect);
610  gdk_window_invalidate_region(GTK_WIDGET(chessboard)->window, indicator_region, FALSE);
611  gdk_region_destroy(indicator_region);
612  }
613 }
614 
615 #if 0 // Not currently used anywhere.
616 static void invalidate_background(CwChessboard* chessboard)
617 {
618  if (GTK_WIDGET_REALIZED(chessboard))
619  {
620  CwChessboardPrivate* priv = chessboard->priv;
621  GdkWindow* window = GTK_WIDGET(chessboard)->window;
622  GdkRegion* background_region = gdk_drawable_get_clip_region(window);
623  gdk_region_subtract(background_region, priv->chessboard_region);
624  gdk_window_invalidate_region(window, background_region, FALSE);
625  gdk_region_destroy(background_region);
626  priv->redraw_background = TRUE;
627  }
628 }
629 #endif
630 
631 static void invalidate_square(CwChessboard* chessboard, gint col, gint row)
632 {
633  CwChessboardPrivate* priv = chessboard->priv;
634  guint64 redraw_mask = 1;
635  redraw_mask <<= convert_colrow2index(col, row);
636  // No need to call gdk_window_invalidate_rect again when need_redraw_invalidated is already set.
637  if (!(priv->need_redraw_invalidated & redraw_mask))
638  {
639  if (GTK_WIDGET_REALIZED(chessboard))
640  {
641  GdkRectangle rect;
642  rect.width = priv->sside;
643  rect.height = priv->sside;
644  cw_chessboard_colrow2xy(chessboard, col, row,& rect.x,& rect.y);
645  gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
646  priv->need_redraw_invalidated |= redraw_mask;
647  }
648  else
649  priv->need_redraw |= redraw_mask;
650  }
651 }
652 
653 static void invalidate_board(CwChessboard* chessboard)
654 {
655  CwChessboardPrivate* priv = chessboard->priv;
656  if (GTK_WIDGET_REALIZED(chessboard))
657  {
658  gdk_window_invalidate_region(GTK_WIDGET(chessboard)->window, priv->chessboard_region, FALSE);
659  priv->need_redraw_invalidated = (guint64)-1;
660  }
661  else
662  priv->need_redraw = (guint64)-1;
663 }
664 
665 static void invalidate_markers(CwChessboard* chessboard)
666 {
667  CwChessboardPrivate* priv = chessboard->priv;
668  for (gint row = 0; row < squares; ++row)
669  for (gint col = 0; col < squares; ++col)
670  if (convert_code2mahandle(priv->board_codes[convert_colrow2index(col, row)]) != 0)
671  invalidate_square(chessboard, col, row);
672 }
673 
674 static void invalidate_cursor(CwChessboard* chessboard)
675 {
676  CwChessboardPrivate* priv = chessboard->priv;
677  if (priv->show_cursor && is_inside_board(priv->cursor_col, priv->cursor_row))
678  invalidate_square(chessboard, priv->cursor_col, priv->cursor_row);
679 }
680 
681 static void update_cursor_position(CwChessboard* chessboard, gdouble x, gdouble y, gboolean forced)
682 {
683  CwChessboardPrivate* priv = chessboard->priv;
684  gint col = cw_chessboard_x2col(chessboard, x);
685  gint row = cw_chessboard_y2row(chessboard, y);
686  gboolean cursor_moved = (col != priv->cursor_col || row != priv->cursor_row);
687  gboolean was_inside_board = is_inside_board(priv->cursor_col, priv->cursor_row);
688  gboolean inside_board = is_inside_board(col, row);
689  if (cursor_moved || forced)
690  {
691  if (inside_board)
692  {
693  if (CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_entered_square)
694  {
695  if (was_inside_board)
696  CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_entered_square(chessboard, priv->cursor_col, priv->cursor_row, col, row);
697  else
698  CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_entered_square(chessboard, -1, -1, col, row);
699  }
700  if (priv->show_cursor)
701  invalidate_square(chessboard, col, row);
702  }
703  else if (was_inside_board && CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_left_chessboard)
704  CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_left_chessboard(chessboard, priv->cursor_col, priv->cursor_row);
705  if (priv->show_cursor && was_inside_board)
706  invalidate_square(chessboard, priv->cursor_col, priv->cursor_row);
707  priv->cursor_col = col;
708  priv->cursor_row = row;
709  }
710  if (priv->show_cursor && !priv->need_redraw_invalidated)
711  {
712  // Make sure we'll get more motion events.
713  Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
714  gdk_window_get_pointer(GTK_WIDGET(chessboard)->window, NULL, NULL, NULL);
715  }
716 }
717 
718 static void redraw_border(CwChessboard* chessboard)
719 {
720  CwChessboardPrivate* priv = chessboard->priv;
721  cairo_t* cr = priv->cr;
722 
723  gint const pixmap_width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
724  gint const pixmap_height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
725  gint const side = squares * priv->sside;
726 
727  // Fill the area around the board with the background color.
728  if (priv->pixmap_top_left_a8_x != 0)
729  cairo_rectangle(cr, 0, 0, priv->pixmap_top_left_a8_x, pixmap_height);
730  if (pixmap_width - priv->pixmap_top_left_a8_x - side != 0)
731  cairo_rectangle(cr, priv->pixmap_top_left_a8_x + side, 0,
732  pixmap_width - priv->pixmap_top_left_a8_x - side, pixmap_height);
733  if (priv->pixmap_top_left_a8_y != 0)
734  cairo_rectangle(cr, 0, 0, pixmap_width, priv->pixmap_top_left_a8_y);
735  if (pixmap_height - priv->pixmap_top_left_a8_y - side != 0)
736  cairo_rectangle(cr, 0, priv->pixmap_top_left_a8_y + side,
737  pixmap_width, pixmap_height - priv->pixmap_top_left_a8_y - side);
738  cairo_set_source_rgb(cr, priv->widget_background_color.red / 65535.0,
739  priv->widget_background_color.green / 65535.0, priv->widget_background_color.blue / 65535.0);
740  cairo_fill(cr);
741 
742  // Draw outline of the board if any.
743  if (priv->border_width)
744  CW_CHESSBOARD_GET_CLASS(chessboard)->draw_border(chessboard);
745 
746  // We will need to draw it on the screen too.
747  invalidate_border(chessboard);
748 }
749 
751 {
752  CwChessboardPrivate* priv = chessboard->priv;
753  cairo_t* cr = priv->cr;
754 
755  gint const border_width = priv->border_width;
756  gint const border_shadow_width = 2;
757  gint const edge_width = border_width - border_shadow_width - 1;
758  gint const side = squares * priv->sside;
759 
760  cairo_save(cr);
761  // We draw relative to the top-left of the border.
762  cairo_translate(cr, priv->pixmap_top_left_a8_x - border_width, priv->pixmap_top_left_a8_y - border_width);
763  // Draw a black line around the board.
764  cairo_set_source_rgb(cr, 0, 0, 0);
765  cairo_set_line_width(cr, 1.0);
766  cairo_set_source_rgb(cr, priv->board_border_color.red * 0.5, priv->board_border_color.green * 0.5, priv->board_border_color.blue * 0.5);
767  cairo_move_to(cr, side + border_width + 0.5, border_width - 0.5);
768  cairo_line_to(cr, border_width - 0.5, border_width - 0.5);
769  cairo_line_to(cr, border_width - 0.5, side + border_width + 0.5);
770  cairo_stroke(cr);
771  cairo_set_source_rgb(cr, (1.0 + priv->board_border_color.red) * 0.5, (1.0 + priv->board_border_color.green) * 0.5, (1.0 + priv->board_border_color.blue) * 0.5);
772  cairo_move_to(cr, border_width - 0.5, side + border_width + 0.5);
773  cairo_line_to(cr, side + border_width + 0.5, side + border_width + 0.5);
774  cairo_line_to(cr, side + border_width + 0.5, border_width - 0.5);
775  cairo_stroke(cr);
776  // Draw an edge around that that will contain the chessboard coordinates.
777  cairo_set_source_rgb(cr, priv->board_border_color.red, priv->board_border_color.green, priv->board_border_color.blue);
778  cairo_set_line_width(cr, edge_width);
779  cairo_rectangle(cr, border_shadow_width + edge_width * 0.5, border_shadow_width + edge_width * 0.5, side + edge_width + 2, side + edge_width + 2);
780  cairo_stroke(cr);
781  // Draw the shadow around that.
782  cairo_set_source_rgb(cr, (1.0 + priv->board_border_color.red) * 0.5, (1.0 + priv->board_border_color.green) * 0.5, (1.0 + priv->board_border_color.blue) * 0.5);
783  cairo_move_to(cr, 0, 0);
784  cairo_line_to(cr, 0, side + 2 * border_width);
785  cairo_rel_line_to(cr, border_shadow_width, -border_shadow_width);
786  cairo_rel_line_to(cr, 0, -(side + 2 + 2 * edge_width));
787  cairo_rel_line_to(cr, side + 2 + 2 * edge_width, 0);
788  cairo_rel_line_to(cr, border_shadow_width, -border_shadow_width);
789  cairo_close_path(cr);
790  cairo_fill(cr);
791  cairo_set_source_rgb(cr, priv->board_border_color.red * 0.5, priv->board_border_color.green * 0.5, priv->board_border_color.blue * 0.5);
792  cairo_move_to(cr, side + 2 * border_width, side + 2 * border_width);
793  cairo_line_to(cr, side + 2 * border_width, 0);
794  cairo_rel_line_to(cr, -border_shadow_width, border_shadow_width);
795  cairo_rel_line_to(cr, 0, side + 2 + 2 * edge_width);
796  cairo_rel_line_to(cr, -(side + 2 + 2 * edge_width), 0);
797  cairo_rel_line_to(cr, -border_shadow_width, border_shadow_width);
798  cairo_close_path(cr);
799  cairo_fill(cr);
800  // Draw the coordinates.
801  cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
802  PangoLayout* layout = pango_cairo_create_layout(cr);
803  PangoFontDescription* desc = pango_font_description_from_string("Sans Bold 14");
804  gint font_pixels = (edge_width > 14) ? MAX(14, edge_width * 0.66) : edge_width;
805  pango_font_description_set_absolute_size(desc, MAX(font_pixels, 7) * PANGO_SCALE);
806  pango_layout_set_font_description(layout, desc);
807  pango_font_description_free(desc);
808  char c[2] = { 0, 0 };
809  for (int col = 0; col < 8; ++col)
810  {
811  c[0] = 'A' + col;
812  pango_layout_set_text(layout, c, -1);
813  int width, height;
814  pango_layout_get_size(layout,& width,& height);
815  cairo_move_to(cr, border_width + ((priv->flip_board ? 7 - col : col) + 0.5) * priv->sside - ((double)width / PANGO_SCALE) / 2,
816  side + 1.5 * border_width - ((double)height / PANGO_SCALE) / 2);
817  pango_cairo_show_layout(cr, layout);
818  }
819  for (int row = 0; row < 8; ++row)
820  {
821  c[0] = '1' + row;
822  pango_layout_set_text(layout, c, -1);
823  int width, height;
824  pango_layout_get_size(layout,& width,& height);
825  cairo_move_to(cr, border_width / 2 - ((double)width / PANGO_SCALE) / 2,
826  border_width + ((priv->flip_board ? row : 7 - row) + 0.5) * priv->sside - ((double)height / PANGO_SCALE) / 2);
827  pango_cairo_show_layout(cr, layout);
828  }
829  g_object_unref(layout);
830 
831  cairo_restore(cr);
832 
833  // Draw the turn indicator, if requested.
834  if (priv->draw_turn_indicators)
835  CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, priv->active_turn_indicator, TRUE);
836 }
837 
838 void cw_chessboard_default_draw_turn_indicator(CwChessboard* chessboard, gboolean white, gboolean on)
839 {
840  CwChessboardPrivate* priv = chessboard->priv;
841  cairo_t* cr = priv->cr;
842 
843  gint const border_width = priv->border_width;
844  gint const border_shadow_width = 2;
845  gint const edge_width = border_width - border_shadow_width - 1;
846  gint const side = squares * priv->sside;
847 
848  cairo_save(cr);
849  // We draw relative to the top-left of a square of edge_width X edge_width.
850  cairo_translate(cr, priv->pixmap_top_left_a8_x + side + 1,
851  priv->pixmap_top_left_a8_y - border_width + border_shadow_width + ((white != priv->flip_board) ? side + edge_width + 2 : 0));
852 
853  double const factor = 0.085786; // (1/sqrt(2) − 0.5)/(1 + sqrt(2)).
854  if (on)
855  {
856  if (white)
857  cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
858  else
859  cairo_set_source_rgb(cr, 0, 0, 0);
860  cairo_arc(cr, edge_width * 0.5 - MAX((edge_width + 1) * factor - 1, 0),
861  edge_width * 0.5 - (edge_width + 1) * ((white != priv->flip_board) ? factor : -factor), edge_width * 0.5, 0, 2 * M_PI);
862  cairo_fill(cr);
863  }
864  else
865  {
866  gboolean top = (white == priv->flip_board);
867  double dir = top ? 1.0 : -1.0;
868  cairo_set_source_rgb(cr, priv->board_border_color.red, priv->board_border_color.green, priv->board_border_color.blue);
869  cairo_move_to(cr, 0, top ? edge_width : 0);
870  cairo_rel_line_to(cr, 0, dir * ((edge_width + 1) * factor + 1));
871  cairo_rel_line_to(cr, edge_width, 0);
872  cairo_line_to(cr, edge_width, top ? 0 : edge_width);
873  cairo_rel_line_to(cr, -(edge_width + (edge_width + 1) * factor + 1), 0);
874  cairo_rel_line_to(cr, 0, dir * edge_width);
875  cairo_close_path(cr);
876  cairo_fill(cr);
877  }
878 
879  cairo_restore(cr);
880 }
881 
882 // This function should only be called from cw_chessboard_expose.
883 // Call invalidate_square to redraw a square from elsewhere.
884 static void redraw_square(CwChessboard* chessboard, gint index)
885 {
886  CwChessboardPrivate* priv = chessboard->priv;
887  cairo_t* cr = priv->cr;
888 
889  CwChessboardCode code = priv->board_codes[index];
890  CwChessboardColorHandle bghandle = convert_code2bghandle(code);
891  CwChessboardColorHandle mahandle = convert_code2mahandle(code);
892  gint col = convert_index2column(index);
893  gint row = convert_index2row(index);
894  gint sx = priv->pixmap_top_left_a8_x + (priv->flip_board ? 7 - col : col) * priv->sside;
895  gint sy = priv->pixmap_top_left_a8_y + (squares - 1 - (priv->flip_board ? 7 - row : row)) * priv->sside;
896 
897  Dout(dc::cwchessboardwidget, "Calling redraw_square(" << chessboard << ", " << index << ")" <<
898  " with Board Code: " << (int)code);
899 
900  // Draw background color.
901  cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
902  if (bghandle != 0)
903  cairo_set_source_rgb(cr,
904  priv->color_palet[bghandle - 1].red,
905  priv->color_palet[bghandle - 1].green,
906  priv->color_palet[bghandle - 1].blue);
907  else if (((col + row) & 1))
908  cairo_set_source_rgb(cr,
909  priv->light_square_color.red,
910  priv->light_square_color.green,
911  priv->light_square_color.blue);
912  else
913  cairo_set_source_rgb(cr,
914  priv->dark_square_color.red,
915  priv->dark_square_color.green,
916  priv->dark_square_color.blue);
917  if (!mahandle || !priv->marker_below)
918  cairo_fill(cr);
919  else
920  {
921  cairo_fill_preserve(cr);
922  // Draw marker.
923  cairo_rectangle(cr, sx + priv->marker_thickness_px, sy + priv->marker_thickness_px,
924  priv->sside - 2 * priv->marker_thickness_px, priv->sside - 2 * priv->marker_thickness_px);
925  cairo_fill_rule_t prev_fill_rule = cairo_get_fill_rule(cr);
926  cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
927  cairo_set_source_rgb(cr,
928  priv->color_palet[mahandle - 1].red,
929  priv->color_palet[mahandle - 1].green,
930  priv->color_palet[mahandle - 1].blue);
931  cairo_fill(cr);
932  cairo_set_fill_rule(cr, prev_fill_rule);
933  }
934 
935  guint64 bit = 1;
936  bit <<= index;
937 
938  // Draw bottom HUD layer, if any.
939  if ((priv->hud_has_content[0] & bit))
940  {
941  cairo_set_source_surface(cr, priv->hud_layer_surface[0], priv->pixmap_top_left_a8_x, priv->pixmap_top_left_a8_y);
942  cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
943  cairo_fill(cr);
944  }
945 
946  // Draw marker, if any and still needed.
947  if (mahandle && !priv->marker_below)
948  {
949  cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
950  cairo_rectangle(cr, sx + priv->marker_thickness_px, sy + priv->marker_thickness_px,
951  priv->sside - 2 * priv->marker_thickness_px, priv->sside - 2 * priv->marker_thickness_px);
952  cairo_fill_rule_t prev_fill_rule = cairo_get_fill_rule(cr);
953  cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
954  cairo_set_source_rgb(cr,
955  priv->color_palet[mahandle - 1].red,
956  priv->color_palet[mahandle - 1].green,
957  priv->color_palet[mahandle - 1].blue);
958  cairo_fill(cr);
959  cairo_set_fill_rule(cr, prev_fill_rule);
960  }
961 
962  if (priv->show_cursor && priv->cursor_col == col && priv->cursor_row == row)
963  {
964  cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
965  cairo_rectangle(cr, sx + priv->cursor_thickness_px, sy + priv->cursor_thickness_px,
966  priv->sside - 2 * priv->cursor_thickness_px, priv->sside - 2 * priv->cursor_thickness_px);
967  cairo_fill_rule_t prev_fill_rule = cairo_get_fill_rule(cr);
968  cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
969  cairo_set_source_rgb(cr,
970  priv->cursor_color.red,
971  priv->cursor_color.green,
972  priv->cursor_color.blue);
973  cairo_fill(cr);
974  cairo_set_fill_rule(cr, prev_fill_rule);
975  }
976 
977  // Draw piece, if any.
978  if (!is_empty_square(code))
979  {
980  cairo_set_source_surface(cr, priv->piece_pixmap[convert_code2piece_pixmap_index(code)].surface, sx, sy);
981  cairo_paint(cr);
982  }
983 
984  // Draw top HUD layer, if any.
985  if ((priv->hud_has_content[1] & bit))
986  {
987  cairo_set_source_surface(cr, priv->hud_layer_surface[1], priv->pixmap_top_left_a8_x, priv->pixmap_top_left_a8_y);
988  cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
989  cairo_fill(cr);
990  }
991 }
992 
993 static void redraw_pieces(CwChessboard* chessboard)
994 {
995  CwChessboardPrivate* priv = chessboard->priv;
996  for (int i = 0; i < 12; ++i)
997  {
998  if (priv->piece_pixmap[i].surface)
999  cairo_surface_destroy(priv->piece_pixmap[i].surface);
1000  priv->piece_pixmap[i].surface =
1001  cairo_surface_create_similar(cairo_get_target(priv->cr),
1002  CAIRO_CONTENT_COLOR_ALPHA, priv->sside, priv->sside);
1003  Dout(dc::cwchessboardwidget|continued_cf, "(Re)drawing piece cache " << i << "... ");
1004  cairo_t* cr = cairo_create(priv->piece_pixmap[i].surface);
1005  unsigned char code = (unsigned char)(i + 2);
1006  cairo_rectangle(cr, 0, 0, priv->sside, priv->sside);
1007  cairo_clip(cr);
1008  CW_CHESSBOARD_GET_CLASS(chessboard)->
1009  draw_piece[convert_code2piece(code)] (chessboard,
1010  cr,
1011  0.5 * priv->sside,
1012  0.5 * priv->sside,
1013  priv->sside,
1014  is_white_piece(code));
1015  cairo_destroy(cr);
1016  Dout(dc::finish, "done");
1017  }
1018  invalidate_board(chessboard);
1019 }
1020 
1021 // This function is called after realize, or after a resize.
1022 // Allocate the pixmap and redraw everything (including the border).
1023 static void redraw_pixmap(GtkWidget* widget)
1024 {
1025  CwChessboard* chessboard = CW_CHESSBOARD(widget);
1026  CwChessboardPrivate* priv = chessboard->priv;
1027 
1028  // Calculate the size of the squares.
1029  gint const max_total_side = MIN(widget->allocation.width, widget->allocation.height);
1030 
1031  // First, assume we have no border.
1032  gint sside = MAX(min_sside, max_total_side / squares);
1033  priv->border_width = 0;
1034 
1035  if (priv->draw_border) // Do we have a border?
1036  {
1037  // The total side of the board is: squares * sside + 2 * border_width;
1038  // However, we don't know the border_width as it is a function of sside!
1039  // The only way to calculate this is trying recursively.
1040  ++sside;
1041  do
1042  {
1043  --sside;
1044  // Call the function that calculates the border width.
1045  priv->border_width = CW_CHESSBOARD_GET_CLASS(chessboard)->calc_board_border_width(chessboard, sside);
1046  }
1047  while (sside > min_sside && squares * sside + 2 * priv->border_width > max_total_side);
1048  }
1049  // Now sside and border_width have been established.
1050 
1051  // The total size of one side of the board, excluding border.
1052  gint const side = squares * sside;
1053  // The total size of one side of the board, including border.
1054  gint const total_side = side + 2 * priv->border_width;
1055 
1056  // Calculate the size of the pixmap. Include areas outside the border if small enough.
1057  gint pixmap_width = total_side;
1058  gint pixmap_height = total_side;
1059  if (widget->allocation.width < widget->allocation.height)
1060  pixmap_width = widget->allocation.width;
1061  else
1062  pixmap_height = widget->allocation.height;
1063 
1064  // Remember old size of pixmap.
1065  gint old_pixmap_width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
1066  gint old_pixmap_height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
1067  // Calculate the offsets of the pixmap corners.
1068  priv->top_left_pixmap_x = ((widget->allocation.width - pixmap_width) & ~1) / 2;
1069  priv->top_left_pixmap_y = ((widget->allocation.height - pixmap_height) & ~1) / 2;
1070  priv->bottom_right_pixmap_x = priv->top_left_pixmap_x + pixmap_width;
1071  priv->bottom_right_pixmap_y = priv->top_left_pixmap_y + pixmap_height;
1072  g_assert(priv->top_left_pixmap_x == 0 || priv->top_left_pixmap_y == 0);
1073 
1074  // Calculate the offset of the top-left of square a1.
1075  * (gint*)(&chessboard->top_left_a1_x) = ((widget->allocation.width - side) & ~1) / 2;
1076  * (gint*)(&chessboard->top_left_a1_y) = ((widget->allocation.height - side) & ~1) / 2 + side - sside;
1077 
1078  // Calculate the offset of the top-left of square a8, relative to the top-left of the pixmap.
1079  priv->pixmap_top_left_a8_x = chessboard->top_left_a1_x - priv->top_left_pixmap_x;
1080  priv->pixmap_top_left_a8_y = chessboard->top_left_a1_y + sside - side - priv->top_left_pixmap_y;
1081 
1082  // Set the rectangular region where the chessboard resides.
1083  GdkRectangle rect;
1084  rect.x = priv->top_left_pixmap_x + priv->pixmap_top_left_a8_x;
1085  rect.y = priv->top_left_pixmap_y + priv->pixmap_top_left_a8_y;
1086  rect.width = squares * sside;
1087  rect.height = squares * sside;
1088  if (priv->chessboard_region)
1089  gdk_region_destroy(priv->chessboard_region);
1090  priv->chessboard_region = gdk_region_rectangle(&rect);
1091 
1092  Dout(dc::cwchessboardwidget, "widget size is (" << widget->allocation.width << ", " << widget->allocation.height << ")");
1093  Dout(dc::cwchessboardwidget, "border width is " << priv->border_width <<
1094  "; " << squares << 'x' << squares << " squares with side " << sside);
1095  Dout(dc::cwchessboardwidget, "pixmap at (" << priv->top_left_pixmap_x << ", " << priv->top_left_pixmap_y << ") with size (" <<
1096  pixmap_width << ", " << pixmap_height << ")");
1097  Dout(dc::cwchessboardwidget, "a8 offset within pixmap is (" <<
1098  priv->pixmap_top_left_a8_x << ", " << priv->pixmap_top_left_a8_y << ")");
1099  Dout(dc::cwchessboardwidget, " a1 at (" << chessboard->top_left_a1_x << ", " << chessboard->top_left_a1_y << ")");
1100 
1101  // Invalidate everything.
1102  gdk_window_invalidate_rect(widget->window,& widget->allocation, FALSE);
1103  priv->need_redraw_invalidated = (guint64)-1;
1104 
1105  if (old_pixmap_width == pixmap_width && old_pixmap_height == pixmap_height)
1106  return;
1107 
1108  if (priv->cr)
1109  cairo_destroy(priv->cr);
1110  if (priv->pixmap)
1111  g_object_unref(priv->pixmap);
1112 
1113  // Allocate a pixmap for the board including the border.
1114  priv->pixmap = gdk_pixmap_new(widget->window, pixmap_width, pixmap_height, -1);
1115 
1116  // (Re)create cairo context for the new pixmap.
1117  priv->cr = gdk_cairo_create(priv->pixmap);
1118 
1119  // If the resize even changed the side of the squares then we have to redraw
1120  // the cached pieces and the HUD layer cache. Initially priv->sside is set to
1121  // -1 and the expression will always evalute to true.
1122  if (priv->sside != sside)
1123  {
1124  // Store the new value of sside.
1125  * (gint*)(&chessboard->sside) = priv->sside = sside;
1126 
1127  // Destroy hatching cache for redraw later.
1128  if (priv->hatching_pixmap.surface)
1129  cairo_surface_destroy(priv->hatching_pixmap.surface);
1130  priv->hatching_pixmap.surface = NULL;
1131 
1132  // Create new cairo surfaces for the piece cache and (re)draw the pieces.
1133  redraw_pieces(chessboard);
1134 
1135  // Calculate the marker thickness.
1136  priv->marker_thickness_px = MAX(1, MIN((gint)round(priv->marker_thickness * sside), sside / 2));
1137  Dout(dc::cwchessboardwidget, "Marker thickness set to " << priv->marker_thickness_px);
1138 
1139  // Calculate the cursor thickness.
1140  priv->cursor_thickness_px = MAX(1, MIN((gint)round(priv->cursor_thickness * sside), sside / 2));
1141  Dout(dc::cwchessboardwidget, "Cursor thickness set to " << priv->cursor_thickness_px);
1142 
1143  // (Re)create alpha layer.
1144  Dout(dc::cwchessboardwidget|continued_cf, "(Re)creating HUD layers... ");
1145  recreate_hud_layers(chessboard);
1146  Dout(dc::finish, "done");
1147  }
1148 
1149  redraw_border(chessboard);
1150 }
1151 
1152 static void cw_chessboard_size_request(GtkWidget* widget, GtkRequisition* requisition)
1153 {
1154  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_size_request(" << ")");
1155 
1156  CwChessboard* chessboard = CW_CHESSBOARD(widget);
1157  CwChessboardPrivate* priv = chessboard->priv;
1158 
1159  // Return the minimum size we really need.
1160  gint min_border_width = 0;
1161  if (priv->draw_border)
1162  min_border_width = 2 * CW_CHESSBOARD_GET_CLASS(chessboard)->calc_board_border_width(chessboard, min_sside);
1163  requisition->width = requisition->height = squares * min_sside + min_border_width;
1164 }
1165 
1166 static void cw_chessboard_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
1167 {
1168  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_size_allocate(" << widget << ", " << allocation << ")");
1169  widget->allocation =* allocation;
1170  if (GTK_WIDGET_REALIZED(widget))
1171  {
1172  gdk_window_move_resize(widget->window,
1173  allocation->x, allocation->y,
1174  allocation->width, allocation->height);
1175  redraw_pixmap(widget);
1176  }
1177  CwChessboard* chessboard = CW_CHESSBOARD(widget);
1178  chessboard->priv->redraw_background = TRUE;
1179 }
1180 
1181 static void cw_chessboard_realize(GtkWidget* widget)
1182 {
1183  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_realize(" << widget << ")");
1184  CwChessboard* chessboard = CW_CHESSBOARD(widget);
1185  GTK_WIDGET_CLASS(cw_chessboard_parent_class)->realize(widget);
1186  redraw_pixmap(widget);
1187  //gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
1188  gdk_window_set_background(widget->window,& chessboard->priv->widget_background_color);
1189 }
1190 
1191 static void cw_chessboard_unrealize(GtkWidget* widget)
1192 {
1193  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_unrealize(" << widget << ")");
1194  CwChessboard* chessboard = CW_CHESSBOARD(widget);
1195  CwChessboardPrivate* priv = chessboard->priv;
1196  for (int i = 0; i < 12; ++i)
1197  {
1198  if (priv->piece_pixmap[i].surface)
1199  cairo_surface_destroy(priv->piece_pixmap[i].surface);
1200  priv->piece_pixmap[i].surface = NULL;
1201  }
1202  if (priv->hatching_pixmap.surface)
1203  cairo_surface_destroy(priv->hatching_pixmap.surface);
1204  priv->hatching_pixmap.surface = NULL;
1205  gdk_region_destroy(priv->chessboard_region);
1206  priv->chessboard_region = NULL;
1207  for (guint hud = 0; hud < G_N_ELEMENTS(priv->hud_layer_surface); ++hud)
1208  cairo_surface_destroy(priv->hud_layer_surface[hud]);
1209  memset(priv->hud_layer_surface, 0, sizeof(priv->hud_layer_surface));
1210  cairo_destroy(priv->cr);
1211  priv->cr = NULL;
1212  g_object_unref(priv->pixmap);
1213  priv->pixmap = NULL;
1214  priv->sside = -1;
1215  priv->top_left_pixmap_x = 0;
1216  priv->top_left_pixmap_y = 0;
1217  priv->bottom_right_pixmap_x = 0;
1218  priv->bottom_right_pixmap_y = 0;
1219  GTK_WIDGET_CLASS(cw_chessboard_parent_class)->unrealize(widget);
1220 }
1221 
1222 // This function is called whenever (a certain region of) the chessboard needs
1223 // to be redrawn, for whatever reason.
1224 static gboolean cw_chessboard_expose(GtkWidget* widget, GdkEventExpose* event)
1225 {
1226  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_expose(" << widget << ", " << event << ")");
1227 
1228  CwChessboard* chessboard = CW_CHESSBOARD(widget);
1229  CwChessboardPrivate* priv = chessboard->priv;
1230  GdkRegion* region = event->region;
1231 
1232 #if CW_CHESSBOARD_EXPOSE_DEBUG
1233  gdk_window_clear(widget->window);
1234 #endif
1235 
1236  // Last minute update of the HUD layers.
1237  for (guint hud = 0; hud < G_N_ELEMENTS(priv->hud_need_redraw); ++hud)
1238  if (priv->hud_need_redraw[hud])
1239  redraw_hud_layer(chessboard, hud);
1240 
1241  // Last minute update of pixmap.
1242  guint64 redraw_mask = 1;
1243  for (gint i = 0; i < 64; ++i, redraw_mask <<= 1)
1244  if (((priv->need_redraw | priv->need_redraw_invalidated) & redraw_mask))
1245  redraw_square(chessboard, i); // This uses the HUD layer.
1246  priv->need_redraw_invalidated = 0;
1247  priv->need_redraw = 0;
1248 
1249 #if CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
1250  // Draw any floating pieces to the pixbuf.
1251  if (priv->number_of_floating_pieces)
1252  {
1253  cairo_save(priv->cr);
1254  cairo_rectangle(priv->cr, priv->pixmap_top_left_a8_x, priv->pixmap_top_left_a8_y,
1255  squares * priv->sside, squares * priv->sside);
1256  cairo_clip(priv->cr);
1257  for (gsize i = 0; i < priv->number_of_floating_pieces; ++i)
1258  if (priv->floating_piece[i].moved)
1259  {
1260  cairo_set_source_surface(priv->cr,
1261  priv->piece_pixmap[convert_code2piece_pixmap_index(priv->floating_piece[i].code)].surface,
1262  priv->floating_piece[i].pixmap_x, priv->floating_piece[i].pixmap_y);
1263  cairo_paint(priv->cr);
1264  }
1265  cairo_restore(priv->cr);
1266  }
1267 #endif
1268 
1269  // Either top_left_pixmap_x or top_left_pixmap_y equals 0 (see redraw_pixmap).
1270  // The type of configuration depends on 'vertical':
1271  //
1272  // vertical == true vertical == false.
1273  // .-----------, .--+----------+--,
1274  // | | | | | |
1275  // +-----------+ | | | |
1276  // | | | | pixmap | |
1277  // | pixmap | | | | |
1278  // | | | | | |
1279  // | | | | | |
1280  // +-----------+ `--+----------+--'
1281  // | |
1282  // `-----------'
1283  // The less than zero happens when the window is so small that sside would have to be less than min_sside.
1284  gboolean vertical = (priv->top_left_pixmap_x == 0);
1285 
1286  // This default is false, which is the case when the board is only updated.
1287  gboolean region_extends_outside_pixmap = FALSE;
1288  // However, if we resized -- or another window moved over the widget -- it could be true.
1289  GdkRectangle clipbox;
1290  gdk_region_get_clipbox(region,& clipbox);
1291  if (G_UNLIKELY(clipbox.y < priv->top_left_pixmap_y))
1292  region_extends_outside_pixmap = vertical;
1293  if (G_UNLIKELY(clipbox.y + clipbox.height > priv->bottom_right_pixmap_y))
1294  region_extends_outside_pixmap = vertical;
1295  if (G_UNLIKELY(clipbox.x < priv->top_left_pixmap_x))
1296  region_extends_outside_pixmap = !vertical;
1297  if (G_UNLIKELY(clipbox.x + clipbox.width > priv->bottom_right_pixmap_x))
1298  region_extends_outside_pixmap = !vertical;
1299  if (G_UNLIKELY(region_extends_outside_pixmap))
1300  {
1301  GdkRectangle pixmap_rect;
1302  pixmap_rect.x = priv->top_left_pixmap_x;
1303  pixmap_rect.y = priv->top_left_pixmap_y;
1304  pixmap_rect.width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
1305  pixmap_rect.height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
1306  GdkRegion* pixmap_region = gdk_region_rectangle(&pixmap_rect);
1307  if (CW_CHESSBOARD_EXPOSE_ALWAYS_CLEAR_BACKGROUND || priv->redraw_background)
1308  {
1309  // If the widget was resized, there might be trash outside the pixmap. Erase that too.
1310  GdkRegion* outside_pixmap_region = gdk_region_copy(region);
1311  gdk_region_subtract(outside_pixmap_region, pixmap_region);
1312  GdkRectangle* outside_areas;
1313  gint n_outside_areas;
1314  gdk_region_get_rectangles(outside_pixmap_region,& outside_areas,& n_outside_areas);
1315  GdkRectangle const* outside_rect = outside_areas;
1316  for (int i = 0; i < n_outside_areas; ++i, ++outside_rect)
1317  gdk_window_clear_area(widget->window, outside_rect->x, outside_rect->y, outside_rect->width, outside_rect->height);
1318 #if CW_CHESSBOARD_EXPOSE_DEBUG
1319  // Draw a green rectangle around updated areas.
1320  GdkGC* debug_gc = gdk_gc_new(priv->pixmap);
1321  GdkColor debug_green = { 0, 0, 65535, 0 };
1322  gdk_colormap_alloc_color(gtk_widget_get_colormap(widget),& debug_green, FALSE, TRUE);
1323  gdk_gc_set_foreground(debug_gc,& debug_green);
1324  outside_rect = outside_areas;
1325  for (int i = 0; i < n_outside_areas; ++i, ++outside_rect)
1326  gdk_draw_rectangle(widget->window, debug_gc, FALSE,
1327  outside_rect->x, outside_rect->y, outside_rect->width - 1, outside_rect->height - 1);
1328  g_object_unref(debug_gc);
1329 #endif
1330  gdk_region_destroy(outside_pixmap_region);
1331  priv->redraw_background = FALSE;
1332  }
1333  gdk_region_intersect(region, pixmap_region);
1334  gdk_region_destroy(pixmap_region);
1335  }
1336 
1337  cairo_t* dest = gdk_cairo_create(widget->window);
1338 #if !CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER && !CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET
1339  if (priv->number_of_floating_pieces)
1340  cairo_save(dest);
1341 #endif
1342  cairo_surface_t* source = cairo_get_target(priv->cr);
1343 #if CW_CHESSBOARD_EXPOSE_DEBUG
1344  cairo_set_source_surface(dest, source, priv->top_left_pixmap_x, priv->top_left_pixmap_y);
1345  cairo_paint_with_alpha(dest, 0.35);
1346 #endif
1347  cairo_set_source_surface(dest, source, priv->top_left_pixmap_x, priv->top_left_pixmap_y);
1348  gdk_cairo_region(dest, region);
1349 #if CW_CHESSBOARD_EXPOSE_DEBUG
1350  cairo_clip_preserve(dest);
1351 #else
1352  cairo_clip(dest);
1353 #endif
1354  cairo_paint(dest);
1355 #if CW_CHESSBOARD_EXPOSE_DEBUG
1356  cairo_set_line_width(dest, 2);
1357  cairo_set_source_rgb(dest, 1, 0, 0);
1358  cairo_stroke(dest);
1359 #endif
1360 
1361 #if !CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
1362  // Draw any floating pieces to the screen.
1363  if (priv->number_of_floating_pieces)
1364  {
1365 #if !CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET
1366  cairo_restore(dest); // Remove clip region: we draw floating pieces outside the region!
1367 #endif
1368  for (gsize i = 0; i < priv->number_of_floating_pieces; ++i)
1369  if (priv->floating_piece[i].moved)
1370  {
1371  cairo_set_source_surface(dest,
1372  priv->piece_pixmap[convert_code2piece_pixmap_index(priv->floating_piece[i].code)].surface,
1373  priv->floating_piece[i].pixmap_x + priv->top_left_pixmap_x,
1374  priv->floating_piece[i].pixmap_y + priv->top_left_pixmap_y);
1375  cairo_paint(dest);
1376  }
1377  }
1378 #endif
1379 
1380  cairo_destroy(dest);
1381 
1382  if (priv->show_cursor || priv->floating_piece_handle != -1)
1383  {
1384  // Call this function so that we'll get the next pointer motion hint.
1385  Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
1386  gdk_window_get_pointer(GTK_WIDGET(chessboard)->window, NULL, NULL, NULL);
1387  }
1388 #if 0
1389  static int benchmark = 0;
1390  if (benchmark & 1)
1391  cw_chessboard_enable_hud_layer(chessboard, 0);
1392  else
1393  cw_chessboard_disable_hud_layer(chessboard, 0);
1394  if (++benchmark == 100)
1395  exit(0);
1396 #endif
1397 
1398  return TRUE;
1399 }
1400 
1402 {
1403  // Make line width run from 1.0 (at sside == 12) to 4.0 (at sside == 87)).
1404  // Round to nearest even integer. Then add sside / 3. Return at least 8.
1405  return (gint)MAX(8.0, round(1.0 + (sside - 12) / 25.0) + sside / 3.0);
1406 }
1407 
1408 gboolean cw_chessboard_default_draw_hud_square(G_GNUC_UNUSED CwChessboard* chessboard,
1409  cairo_t* cr, gint col, gint row, gint sside, guint hud)
1410 {
1411  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_default_draw_hud_square(" << chessboard << ", " <<
1412  cr << ", " << col << ", " << row << ", " << sside << ", " << hud << ")");
1413 
1414  g_return_val_if_fail(hud < number_of_hud_layers, FALSE);
1415  g_return_val_if_fail(is_inside_board(col, row), FALSE);
1416 
1417  int const number_of_lines = 21;
1418  double const line_width = 0.25;
1419 
1420  // No HUD layer for the white squares.
1421  if (hud == 1 || ((col + row) & 1) == 1)
1422  return FALSE;
1423 
1424  CwChessboardPrivate* priv = chessboard->priv;
1425  if (!priv->hatching_pixmap.surface)
1426  {
1427  priv->hatching_pixmap.surface = cairo_surface_create_similar(cairo_get_target(priv->cr),
1428  CAIRO_CONTENT_COLOR_ALPHA, sside, sside);
1429  cairo_t* cr = cairo_create(priv->hatching_pixmap.surface);
1430  cairo_set_line_width(cr, line_width);
1431  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1432  cairo_set_source_rgb(cr, 0, 0, 0);
1433  cairo_scale(cr, (double)sside / number_of_lines, (double)sside / number_of_lines);
1434  for (int h = 0; h < number_of_lines; ++h)
1435  {
1436  double s = h + line_width;
1437  cairo_move_to(cr, s - 0.5 * line_width, 0.5 * line_width);
1438  cairo_line_to(cr, 0.5 * line_width, s - 0.5 * line_width);
1439  cairo_move_to(cr, s + 0.5 * line_width, number_of_lines - 0.5 * line_width);
1440  cairo_line_to(cr, number_of_lines - 0.5 * line_width, s + 0.5 * line_width);
1441  }
1442  cairo_stroke(cr);
1443  cairo_destroy(cr);
1444  }
1445 
1446  cairo_set_source_surface(cr, priv->hatching_pixmap.surface, 0, 0);
1447  cairo_paint(cr);
1448 
1449  return TRUE;
1450 }
1451 
1452 void cw_chessboard_default_draw_hud_layer(CwChessboard* chessboard, cairo_t* cr, gint sside, guint hud)
1453 {
1454  g_return_if_fail(hud < number_of_hud_layers);
1455 
1456  CwChessboardPrivate* priv = chessboard->priv;
1457  guint64 bit = 1;
1458  for (gint row = 0; row < 8; ++row)
1459  for (gint col = 0; col < 8; ++col, bit <<= 1)
1460  {
1461  if ((priv->hud_need_redraw[hud] & bit))
1462  {
1463  cairo_save(cr);
1464  // FIXME: also rotate the square 180 degrees when flipped.
1465  cairo_translate(cr, (priv->flip_board ? 7 - col : col) * sside, (priv->flip_board ? row : 7 - row) * sside);
1466  cairo_rectangle(cr, 0, 0, sside, sside);
1467  cairo_clip(cr);
1468  if (CW_CHESSBOARD_GET_CLASS(chessboard)->draw_hud_square(chessboard, cr, col, row, sside, hud))
1469  {
1470  priv->hud_has_content[hud] |= bit;
1471  invalidate_square(chessboard, col, row);
1472  }
1473  cairo_restore(cr);
1474  }
1475  }
1476 }
1477 
1478 static GdkRegion* convert_mask2region(guint64 mask, gint x, gint y, gint sside, gboolean flip_board)
1479 {
1480  GdkRegion* region = gdk_region_new();
1481  guint64 row_mask = (guint64)0xff << 56;
1482  for (gint row = 7; row >= 0; --row, row_mask >>= 8)
1483  {
1484  if ((mask & row_mask))
1485  {
1486  guint64 col_mask = (guint64)1 << 8 * row;
1487  gint col_start = 0;
1488  gint col_end;
1489  do
1490  {
1491  while (col_start != 8 && !(mask & col_mask))
1492  {
1493  ++col_start;
1494  col_mask <<= 1;
1495  }
1496  if (col_start == 8)
1497  break; // No new start found;
1498  col_end = col_start;
1499  while (col_end != 8 && (mask & col_mask))
1500  {
1501  ++col_end;
1502  col_mask <<= 1;
1503  }
1504  GdkRectangle rect;
1505  rect.x = x + (flip_board ? 8 - col_end : col_start) * sside;
1506  rect.y = y + (flip_board ? row : 7 - row) * sside;
1507  rect.width = (col_end - col_start) * sside;
1508  rect.height = sside;
1509  gdk_region_union_with_rect(region,& rect);
1510  col_start = col_end;
1511  }
1512  while(col_end !=8);
1513  }
1514  }
1515  return region;
1516 }
1517 
1518 static void redraw_hud_layer(CwChessboard* chessboard, guint hud)
1519 {
1520  Dout(dc::cwchessboardwidget, "Calling redraw_hud_layer(" << chessboard << ", " << hud << ")");
1521 
1522  CwChessboardPrivate* priv = chessboard->priv;
1523  gint sside = priv->sside;
1524 
1525  cairo_t* cr = cairo_create(priv->hud_layer_surface[hud]);
1526  cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
1527  guint64 need_clear = priv->hud_has_content[hud] & priv->hud_need_redraw[hud];
1528  for (gint row = 0; row < 8; ++row)
1529  if ((need_clear & ((guint64)0xff << (8 * row))))
1530  {
1531  guint64 bit = (guint64)1 << (8 * row);
1532  for (gint col = 0; col < 8; ++col, bit <<= 1)
1533  if ((need_clear & bit))
1534  {
1535  cairo_rectangle(cr, (priv->flip_board ? 7 - col : col) * sside, (priv->flip_board ? row : 7 - row) * sside, sside, sside);
1536  invalidate_square(chessboard, col, row);
1537  }
1538  }
1539  cairo_fill(cr);
1540  cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1541  priv->hud_has_content[hud]& = ~priv->hud_need_redraw[hud];
1542 
1543  if (priv->has_hud_layer[hud])
1544  {
1545  if (CW_CHESSBOARD_GET_CLASS(chessboard)->draw_hud_layer != cw_chessboard_default_draw_hud_layer)
1546  {
1547  priv->hud_has_content[hud] = (guint64)-1; // Assume that a user function puts content in all squares.
1548  invalidate_board(chessboard);
1549  }
1550  CW_CHESSBOARD_GET_CLASS(chessboard)->draw_hud_layer(chessboard, cr, sside, hud);
1551  }
1552 
1553  GdkRegion* need_redraw_region = convert_mask2region(priv->hud_need_redraw[hud], 0, 0, priv->sside, priv->flip_board);
1554  gdk_cairo_region(cr, need_redraw_region);
1555  gdk_region_destroy(need_redraw_region);
1556  cairo_clip(cr);
1557 
1558  for (guint i = 0; i < priv->arrows->len; ++i)
1559  {
1560  Arrow* arrow = (Arrow*)priv->arrows->pdata[i];
1561  if ((priv->hud_need_redraw[hud] & arrow->has_content[hud]))
1562  {
1563  guint64 other_content = 0;
1564  for (guint h = 0; h < number_of_hud_layers; ++h)
1565  if (h != hud)
1566  other_content |= arrow->has_content[h];
1567  gboolean has_colliding_content = (priv->hud_need_redraw[hud] & other_content) != 0;
1568  if (has_colliding_content)
1569  {
1570  cairo_save(cr);
1571  GdkRegion* clip_region = convert_mask2region(arrow->has_content[hud], 0, 0, priv->sside, priv->flip_board);
1572  gdk_cairo_region(cr, clip_region);
1573  gdk_region_destroy(clip_region);
1574  cairo_clip(cr);
1575  }
1576  gdouble length = sqrt((arrow->end_col - arrow->begin_col) * (arrow->end_col - arrow->begin_col) +
1577  (arrow->end_row - arrow->begin_row) * (arrow->end_row - arrow->begin_row));
1578  gdouble begin_x = (0.5 + (priv->flip_board ? 7 - arrow->begin_col : arrow->begin_col)) * priv->sside;
1579  gdouble begin_y = (0.5 + (priv->flip_board ? arrow->begin_row : 7 - arrow->begin_row)) * priv->sside;
1580  // Unit vector in direction of arrow.
1581  gdouble vx = priv->sside * (arrow->end_col - arrow->begin_col) / length;
1582  gdouble vy = priv->sside * (arrow->begin_row - arrow->end_row) / length;
1583  if (priv->flip_board)
1584  {
1585  vx = -vx;
1586  vy = -vy;
1587  }
1588  // Unit vector, rotated 90 degrees counter-clockwise.
1589  gdouble tx = -vy;
1590  gdouble ty = vx;
1591  // Draw arrow.
1592  cairo_move_to(cr, begin_x + 0.125 * tx, begin_y + 0.125 * ty);
1593  cairo_rel_line_to(cr, (length - 0.25) * vx, (length - 0.25) * vy);
1594  cairo_rel_line_to(cr, 0.125 * tx, 0.125 * ty);
1595  cairo_line_to(cr, begin_x + length * vx, begin_y + length * vy);
1596  cairo_line_to(cr, begin_x + (length - 0.25) * vx - 0.25 * tx,
1597  begin_y + (length - 0.25) * vy - 0.25 * ty);
1598  cairo_rel_line_to(cr, 0.125 * tx, 0.125 * ty);
1599  cairo_rel_line_to(cr, (0.25 - length) * vx, (0.25 - length) * vy);
1600  cairo_close_path(cr);
1601  cairo_set_source_rgba(cr, arrow->color.red, arrow->color.green, arrow->color.blue, 0.5);
1602  cairo_fill(cr);
1603  if (has_colliding_content)
1604  cairo_restore(cr);
1605  priv->hud_has_content[hud] |= arrow->has_content[hud];
1606  }
1607  }
1608 
1609  cairo_destroy(cr);
1610 
1611  priv->hud_need_redraw[hud] = 0;
1612 }
1613 
1614 static void recreate_hud_layers(CwChessboard* chessboard)
1615 {
1616  Dout(dc::cwchessboardwidget, "Calling recreate_hud_layers(" << chessboard << ")");
1617 
1618  CwChessboardPrivate* priv = chessboard->priv;
1619 
1620  for (guint hud = 0; hud < number_of_hud_layers; ++hud)
1621  {
1622  if (priv->hud_layer_surface[hud])
1623  cairo_surface_destroy(priv->hud_layer_surface[hud]);
1624 
1625  // (Re)create the HUD layer.
1626  priv->hud_layer_surface[hud] = cairo_surface_create_similar(cairo_get_target(priv->cr),
1627  CAIRO_CONTENT_COLOR_ALPHA, squares * priv->sside, squares * priv->sside);
1628 
1629  priv->hud_has_content[hud] = 0;
1630  priv->hud_need_redraw[hud] = (guint64)-1;
1631  }
1632  // Because we need to expose all squares.
1633  invalidate_board(chessboard);
1634 }
1635 
1636 // CwChessboard API
1637 
1638 // Create and return a new chessboard widget.
1639 GtkWidget* cw_chessboard_new(void)
1640 {
1641  return (GtkWidget*)g_object_new(CW_TYPE_CHESSBOARD, NULL);
1642 }
1643 
1645 cw_chessboard_allocate_color_handle_rgb(CwChessboard* chessboard, gdouble red, gdouble green, gdouble blue)
1646 {
1647  CwChessboardPrivate* priv = chessboard->priv;
1648  guint32 bit = 1;
1649  guint color_index = 0;
1650  while ((priv->allocated_colors_mask & bit))
1651  {
1652  bit <<= 1;
1653  ++color_index;
1654  }
1655  g_assert(color_index < G_N_ELEMENTS(priv->color_palet));
1656  priv->allocated_colors_mask |= bit;
1657  priv->color_palet[color_index].red = red;
1658  priv->color_palet[color_index].green = green;
1659  priv->color_palet[color_index].blue = blue;
1660  return color_index + 1;
1661 }
1662 
1664 {
1665  CwChessboardPrivate* priv = chessboard->priv;
1666  g_assert(handle > 0);
1667  guint color_index = handle - 1;
1668  g_assert(color_index < G_N_ELEMENTS(priv->color_palet));
1669  guint32 bit = 1 << color_index;
1670  g_assert((priv->allocated_colors_mask & bit) != 0);
1671  priv->allocated_colors_mask& = ~bit;
1672 }
1673 
1675  gint col, gint row, CwChessboardColorHandle mahandle)
1676 {
1677  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_set_marker_color(" <<
1678  chessboard << ", " << col << ", " << row << ", " << (int)mahandle << ")");
1679  CwChessboardPrivate* priv = chessboard->priv;
1680  BoardIndex index = convert_colrow2index(col, row);
1681  CwChessboardCode old_code = priv->board_codes[index];
1682  priv->board_codes[index] = convert_mahandle2code(mahandle) | (old_code & ~mahandle_mask);
1683  invalidate_square(chessboard, col, row);
1684 }
1685 
1687 {
1688  return convert_code2mahandle(chessboard->priv->board_codes[convert_colrow2index(col, row)]);
1689 }
1690 
1691 void cw_chessboard_set_marker_thickness(CwChessboard* chessboard, gdouble thickness)
1692 {
1693  CwChessboardPrivate* priv = chessboard->priv;
1694  priv->marker_thickness = MIN(MAX(0, thickness), 0.5);
1695  priv->marker_thickness_px = MAX(1, MIN((gint)round(priv->marker_thickness * priv->sside), priv->sside / 2));
1696  invalidate_markers(chessboard);
1697 }
1698 
1700 {
1701  return chessboard->priv->marker_thickness;
1702 }
1703 
1704 void cw_chessboard_set_marker_level(CwChessboard* chessboard, gboolean below)
1705 {
1706  chessboard->priv->marker_below = below;
1707 }
1708 
1709 void cw_chessboard_set_background_color(CwChessboard* chessboard, gint col, gint row, CwChessboardColorHandle bghandle)
1710 {
1711  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_set_background_color(" <<
1712  chessboard << ", " << col << ", " << row << ", " << (int)bghandle << ")");
1713  CwChessboardPrivate* priv = chessboard->priv;
1714  BoardIndex index = convert_colrow2index(col, row);
1715  CwChessboardCode old_code = priv->board_codes[index];
1716  priv->board_codes[index] = convert_bghandle2code(bghandle) | (old_code & ~bghandle_mask);
1717  invalidate_square(chessboard, col, row);
1718 }
1719 
1721 {
1722  return convert_code2bghandle(chessboard->priv->board_codes[convert_colrow2index(col, row)]);
1723 }
1724 
1726 {
1727  // Read through the whole array, only calling cw_chessboard_set_background_color when the value changed.
1728  CwChessboardCode* board_codes = chessboard->priv->board_codes;
1729  for (int i = 0; i < 64; ++i)
1730  if (convert_code2bghandle(board_codes[i]) != handles[i])
1731  cw_chessboard_set_background_color(chessboard, convert_index2column(i), convert_index2row(i), handles[i]);
1732 }
1733 
1735 {
1736  CwChessboardCode* board_codes = chessboard->priv->board_codes;
1737  for (int i = 0; i < 64; ++i)
1738  handles[i] = convert_code2bghandle(board_codes[i]);
1739 }
1740 
1741 void cw_chessboard_set_square(CwChessboard* chessboard, gint col, gint row, CwChessboardCode code)
1742 {
1743  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_set_square(" <<
1744  chessboard << ", " << col << ", " << row << ", " << (int)code << ")");
1745  gint index = convert_colrow2index(col, row);
1746  CwChessboardCode* board_codes = chessboard->priv->board_codes;
1747  CwChessboardCode old_code = board_codes[index];
1748  if (old_code != code)
1749  {
1750  board_codes[index] = (old_code & ~piece_color_mask) | (code & piece_color_mask);
1751  invalidate_square(chessboard, col, row);
1752  }
1753 }
1754 
1756 {
1757  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_get_square(" << chessboard << ", " << col << ", " << row << ")");
1758  gint index = convert_colrow2index(col, row);
1759  CwChessboardCode* board_codes = chessboard->priv->board_codes;
1760  return board_codes[index] & piece_color_mask;
1761 }
1762 
1763 void cw_chessboard_set_draw_border(CwChessboard* chessboard, gboolean draw)
1764 {
1765  if (chessboard->priv->draw_border != draw)
1766  {
1767  chessboard->priv->draw_border = draw;
1768  if (GTK_WIDGET_REALIZED(chessboard))
1769  redraw_pixmap(GTK_WIDGET(chessboard));
1770  }
1771 }
1772 
1774 {
1775  return chessboard->priv->draw_border;
1776 }
1777 
1778 void cw_chessboard_set_flip_board(CwChessboard* chessboard, gboolean flip)
1779 {
1780  if (chessboard->priv->flip_board != flip)
1781  {
1782  * (gboolean*)(&chessboard->flip_board) = chessboard->priv->flip_board = flip;
1783  if (GTK_WIDGET_REALIZED(chessboard))
1784  {
1785  redraw_border(chessboard);
1786  for (guint hud = 0; hud < G_N_ELEMENTS(chessboard->priv->hud_need_redraw); ++hud)
1787  {
1788  chessboard->priv->hud_need_redraw[hud] = (guint64)-1;
1789  chessboard->priv->hud_has_content[hud] = (guint64)-1;
1790  }
1791  invalidate_board(chessboard);
1792  }
1793  }
1794 }
1795 
1797 {
1798  return chessboard->priv->flip_board;
1799 }
1800 
1802 {
1803  CwChessboardPrivate* priv = chessboard->priv;
1804  if (priv->draw_turn_indicators != draw)
1805  {
1806  priv->draw_turn_indicators = draw;
1807  if (GTK_WIDGET_REALIZED(chessboard) && priv->border_width)
1808  {
1809  CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, priv->active_turn_indicator, priv->draw_turn_indicators);
1810  invalidate_turn_indicators(chessboard);
1811  }
1812  }
1813 }
1814 
1816 {
1817  return chessboard->priv->draw_turn_indicators;
1818 }
1819 
1821 {
1822  CwChessboardPrivate* priv = chessboard->priv;
1823  if (priv->active_turn_indicator != white)
1824  {
1825  priv->active_turn_indicator = white;
1826  if (GTK_WIDGET_REALIZED(chessboard) && priv->border_width && priv->draw_turn_indicators)
1827  {
1828  CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, TRUE, white);
1829  CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, FALSE, !white);
1830  invalidate_turn_indicators(chessboard);
1831  }
1832  }
1833 }
1834 
1836 {
1837  return chessboard->priv->active_turn_indicator;
1838 }
1839 
1840 void cw_chessboard_set_dark_square_color(CwChessboard* chessboard, GdkColor const* color)
1841 {
1842  chessboard->priv->dark_square_color_pixel = color->pixel;
1843  chessboard->priv->dark_square_color.red = color->red / 65535.0;
1844  chessboard->priv->dark_square_color.green = color->green / 65535.0;
1845  chessboard->priv->dark_square_color.blue = color->blue / 65535.0;
1846  invalidate_board(chessboard);
1847 }
1848 
1849 void cw_chessboard_get_dark_square_color(CwChessboard* chessboard, GdkColor* color)
1850 {
1851  color->pixel = chessboard->priv->dark_square_color_pixel;
1852  color->red = (guint16)round(65535.0 * chessboard->priv->dark_square_color.red);
1853  color->green = (guint16)round(65535.0 * chessboard->priv->dark_square_color.green);
1854  color->blue = (guint16)round(65535.0 * chessboard->priv->dark_square_color.blue);
1855 }
1856 
1857 void cw_chessboard_set_light_square_color(CwChessboard* chessboard, GdkColor const* color)
1858 {
1859  chessboard->priv->light_square_color_pixel = color->pixel;
1860  chessboard->priv->light_square_color.red = color->red / 65535.0;
1861  chessboard->priv->light_square_color.green = color->green / 65535.0;
1862  chessboard->priv->light_square_color.blue = color->blue / 65535.0;
1863  invalidate_board(chessboard);
1864 }
1865 
1866 void cw_chessboard_get_light_square_color(CwChessboard* chessboard, GdkColor* color)
1867 {
1868  color->pixel = chessboard->priv->light_square_color_pixel;
1869  color->red = (guint16)round(65535.0 * chessboard->priv->light_square_color.red);
1870  color->green = (guint16)round(65535.0 * chessboard->priv->light_square_color.green);
1871  color->blue = (guint16)round(65535.0 * chessboard->priv->light_square_color.blue);
1872 }
1873 
1874 void cw_chessboard_set_border_color(CwChessboard* chessboard, GdkColor const* color)
1875 {
1876  chessboard->priv->board_border_color_pixel = color->pixel;
1877  chessboard->priv->board_border_color.red = color->red / 65535.0;
1878  chessboard->priv->board_border_color.green = color->green / 65535.0;
1879  chessboard->priv->board_border_color.blue = color->blue / 65535.0;
1880  if (GTK_WIDGET_REALIZED(chessboard))
1881  redraw_border(chessboard);
1882 }
1883 
1884 void cw_chessboard_get_border_color(CwChessboard* chessboard, GdkColor* color)
1885 {
1886  color->pixel = chessboard->priv->board_border_color_pixel;
1887  color->red = (guint16)round(65535.0 * chessboard->priv->board_border_color.red);
1888  color->green = (guint16)round(65535.0 * chessboard->priv->board_border_color.green);
1889  color->blue = (guint16)round(65535.0 * chessboard->priv->board_border_color.blue);
1890 }
1891 
1892 void cw_chessboard_set_white_fill_color(CwChessboard* chessboard, GdkColor const* color)
1893 {
1894  chessboard->priv->white_piece_fill_color_pixel = color->pixel;
1895  chessboard->priv->white_piece_fill_color.red = color->red / 65535.0;
1896  chessboard->priv->white_piece_fill_color.green = color->green / 65535.0;
1897  chessboard->priv->white_piece_fill_color.blue = color->blue / 65535.0;
1898  if (GTK_WIDGET_REALIZED(chessboard))
1899  redraw_pieces(chessboard);
1900 }
1901 
1902 void cw_chessboard_get_white_fill_color(CwChessboard* chessboard, GdkColor* color)
1903 {
1904  color->pixel = chessboard->priv->white_piece_fill_color_pixel;
1905  color->red = (guint16)round(65535.0 * chessboard->priv->white_piece_fill_color.red);
1906  color->green = (guint16)round(65535.0 * chessboard->priv->white_piece_fill_color.green);
1907  color->blue = (guint16)round(65535.0 * chessboard->priv->white_piece_fill_color.blue);
1908 }
1909 
1910 void cw_chessboard_set_white_line_color(CwChessboard* chessboard, GdkColor const* color)
1911 {
1912  chessboard->priv->white_piece_line_color_pixel = color->pixel;
1913  chessboard->priv->white_piece_line_color.red = color->red / 65535.0;
1914  chessboard->priv->white_piece_line_color.green = color->green / 65535.0;
1915  chessboard->priv->white_piece_line_color.blue = color->blue / 65535.0;
1916  if (GTK_WIDGET_REALIZED(chessboard))
1917  redraw_pieces(chessboard);
1918 }
1919 
1920 void cw_chessboard_get_white_line_color(CwChessboard* chessboard, GdkColor* color)
1921 {
1922  color->pixel = chessboard->priv->white_piece_line_color_pixel;
1923  color->red = (guint16)round(65535.0 * chessboard->priv->white_piece_line_color.red);
1924  color->green = (guint16)round(65535.0 * chessboard->priv->white_piece_line_color.green);
1925  color->blue = (guint16)round(65535.0 * chessboard->priv->white_piece_line_color.blue);
1926 }
1927 
1928 void cw_chessboard_set_black_fill_color(CwChessboard* chessboard, GdkColor const* color)
1929 {
1930  chessboard->priv->black_piece_fill_color_pixel = color->pixel;
1931  chessboard->priv->black_piece_fill_color.red = color->red / 65535.0;
1932  chessboard->priv->black_piece_fill_color.green = color->green / 65535.0;
1933  chessboard->priv->black_piece_fill_color.blue = color->blue / 65535.0;
1934  if (GTK_WIDGET_REALIZED(chessboard))
1935  redraw_pieces(chessboard);
1936 }
1937 
1938 void cw_chessboard_get_black_fill_color(CwChessboard* chessboard, GdkColor* color)
1939 {
1940  color->pixel = chessboard->priv->black_piece_fill_color_pixel;
1941  color->red = (guint16)round(65535.0 * chessboard->priv->black_piece_fill_color.red);
1942  color->green = (guint16)round(65535.0 * chessboard->priv->black_piece_fill_color.green);
1943  color->blue = (guint16)round(65535.0 * chessboard->priv->black_piece_fill_color.blue);
1944 }
1945 
1946 void cw_chessboard_set_black_line_color(CwChessboard* chessboard, GdkColor const* color)
1947 {
1948  chessboard->priv->black_piece_line_color_pixel = color->pixel;
1949  chessboard->priv->black_piece_line_color.red = color->red / 65535.0;
1950  chessboard->priv->black_piece_line_color.green = color->green / 65535.0;
1951  chessboard->priv->black_piece_line_color.blue = color->blue / 65535.0;
1952  if (GTK_WIDGET_REALIZED(chessboard))
1953  redraw_pieces(chessboard);
1954 }
1955 
1956 void cw_chessboard_get_black_line_color(CwChessboard* chessboard, GdkColor* color)
1957 {
1958  color->pixel = chessboard->priv->black_piece_line_color_pixel;
1959  color->red = (guint16)round(65535.0 * chessboard->priv->black_piece_line_color.red);
1960  color->green = (guint16)round(65535.0 * chessboard->priv->black_piece_line_color.green);
1961  color->blue = (guint16)round(65535.0 * chessboard->priv->black_piece_line_color.blue);
1962 }
1963 
1964 void cw_chessboard_set_cursor_color(CwChessboard* chessboard, GdkColor const* color)
1965 {
1966  chessboard->priv->cursor_color_pixel = color->pixel;
1967  chessboard->priv->cursor_color.red = color->red / 65535.0;
1968  chessboard->priv->cursor_color.green = color->green / 65535.0;
1969  chessboard->priv->cursor_color.blue = color->blue / 65535.0;
1970  invalidate_cursor(chessboard);
1971 }
1972 
1973 void cw_chessboard_get_cursor_color(CwChessboard* chessboard, GdkColor* color)
1974 {
1975  color->pixel = chessboard->priv->cursor_color_pixel;
1976  color->red = (guint16)round(65535.0 * chessboard->priv->cursor_color.red);
1977  color->green = (guint16)round(65535.0 * chessboard->priv->cursor_color.green);
1978  color->blue = (guint16)round(65535.0 * chessboard->priv->cursor_color.blue);
1979 }
1980 
1981 void cw_chessboard_set_cursor_thickness(CwChessboard* chessboard, gdouble thickness)
1982 {
1983  CwChessboardPrivate* priv = chessboard->priv;
1984  priv->cursor_thickness = MIN(MAX(0, thickness), 0.5);
1985  priv->cursor_thickness_px = MAX(1, MIN((gint)round(priv->cursor_thickness * priv->sside), priv->sside / 2));
1986  invalidate_cursor(chessboard);
1987 }
1988 
1990 {
1991  return chessboard->priv->cursor_thickness;
1992 }
1993 
1995 {
1996  GtkWidget* widget = GTK_WIDGET(chessboard);
1997  CwChessboardPrivate* priv = chessboard->priv;
1998  if (priv->show_cursor)
1999  return;
2000  priv->show_cursor = TRUE;
2001  gint x;
2002  gint y;
2003  Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
2004  gdk_window_get_pointer(widget->window,& x,& y, NULL);
2005  update_cursor_position(chessboard, x, y, TRUE);
2006 }
2007 
2009 {
2010  CwChessboardPrivate* priv = chessboard->priv;
2011  invalidate_cursor(chessboard);
2012  priv->show_cursor = FALSE;
2013 }
2014 
2015 void cw_chessboard_move_floating_piece(CwChessboard* chessboard, gint handle, gdouble x, gdouble y)
2016 {
2017  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_move_floating_piece(" <<
2018  chessboard << ", " << (int)handle << ", " << x << ", " << y << ")");
2019 
2020  GtkWidget* widget = GTK_WIDGET(chessboard);
2021  CwChessboardPrivate* priv = chessboard->priv;
2022  GdkRectangle rect;
2023 
2024  g_assert(handle >= 0 && (gsize)handle < G_N_ELEMENTS(priv->floating_piece));
2025  g_assert(!is_empty_square(priv->floating_piece[handle].code));
2026 
2027  // If x (y) is non-integer, this should really be priv->sside + 1, however
2028  // it is safe to use just sside at all times because pieces never extend
2029  // all the way to the border of a square: there is nothing drawn there,
2030  // so there is no reason to invalidate it.
2031  rect.width = priv->sside;
2032  rect.height = priv->sside;
2033  rect.x = priv->floating_piece[handle].pixmap_x + priv->top_left_pixmap_x;
2034  rect.y = priv->floating_piece[handle].pixmap_y + priv->top_left_pixmap_y;
2035  gdk_window_invalidate_rect(widget->window,& rect, FALSE);
2036 #if CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
2037  gint col = cw_chessboard_x2col(chessboard, rect.x);
2038  gint row = cw_chessboard_y2row(chessboard, rect.y);
2039  if (is_inside_board(col, row))
2040  {
2041  BoardIndex index = convert_colrow2index(col, row);
2042  guint64 redraw_mask = 1;
2043  priv->need_redraw |= (redraw_mask << index);
2044  }
2045  col += priv->flip_board ? -1 : 1;
2046  if (is_inside_board(col, row))
2047  {
2048  BoardIndex index = convert_colrow2index(col, row);
2049  guint64 redraw_mask = 1;
2050  priv->need_redraw |= (redraw_mask << index);
2051  }
2052  row += priv->flip_board ? 1 : -1;
2053  if (is_inside_board(col, row))
2054  {
2055  BoardIndex index = convert_colrow2index(col, row);
2056  guint64 redraw_mask = 1;
2057  priv->need_redraw |= (redraw_mask << index);
2058  }
2059  col += priv->flip_board ? 1 : -1;
2060  if (is_inside_board(col, row))
2061  {
2062  BoardIndex index = convert_colrow2index(col, row);
2063  guint64 redraw_mask = 1;
2064  priv->need_redraw |= (redraw_mask << index);
2065  }
2066 #endif
2067  gboolean outside_window =
2068  rect.x + rect.width < 0 ||
2069  rect.x > widget->allocation.x ||
2070  rect.y + rect.height < 0 ||
2071  rect.y > widget->allocation.y;
2072  // Redraw background of widget if the old place of the floating piece is outside the board.
2073  priv->redraw_background = priv->redraw_background ||
2074  rect.x < priv->top_left_pixmap_x ||
2075  rect.x + rect.width > priv->bottom_right_pixmap_x ||
2076  rect.y < priv->top_left_pixmap_y ||
2077  rect.y + rect.height > priv->bottom_right_pixmap_y;
2078  rect.x = (gint)trunc(x - 0.5 * priv->sside);
2079  rect.y = (gint)trunc(y - 0.5 * priv->sside);
2080 #if CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET || CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
2081  gdk_window_invalidate_rect(widget->window,& rect, FALSE);
2082  outside_window = outside_window&&
2083  (rect.x + rect.width < 0 ||
2084  rect.x > widget->allocation.x ||
2085  rect.y + rect.height < 0 ||
2086  rect.y > widget->allocation.y);
2087 #endif
2088  if (outside_window && priv->floating_piece[handle].pointer_device)
2089  {
2090  Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
2091  gdk_window_get_pointer(widget->window, NULL, NULL, NULL);
2092  }
2093  priv->floating_piece[handle].pixmap_x = rect.x - priv->top_left_pixmap_x;
2094  priv->floating_piece[handle].pixmap_y = rect.y - priv->top_left_pixmap_y;
2095  priv->floating_piece[handle].moved = TRUE;
2096 }
2097 
2099  gdouble x, gdouble y, gboolean pointer_device)
2100 {
2101  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_add_floating_piece(" << chessboard << ", code:" << code <<
2102  ", x:" << x << ", y:" << y << ", " << pointer_device << ")");
2103 
2104  CwChessboardPrivate* priv = chessboard->priv;
2105 
2106  // There can't be more than 32 floating pieces.
2107  g_assert(priv->number_of_floating_pieces < G_N_ELEMENTS(priv->floating_piece));
2108 
2109  priv->number_of_floating_pieces++;
2110  gint handle = 0;
2111  while (priv->floating_piece[handle].code != empty_square)
2112  ++handle;
2113  GdkRectangle rect;
2114  rect.x = (gint)trunc(x - 0.5 * priv->sside);
2115  rect.y = (gint)trunc(y - 0.5 * priv->sside);
2116  priv->floating_piece[handle].code = code & piece_color_mask;
2117  priv->floating_piece[handle].pixmap_x = rect.x - priv->top_left_pixmap_x;
2118  priv->floating_piece[handle].pixmap_y = rect.y - priv->top_left_pixmap_y;
2119  priv->floating_piece[handle].moved = TRUE;
2120  if (priv->floating_piece_handle != -1)
2121  pointer_device = FALSE; // Refuse to add two pointer devices at the same time.
2122  priv->floating_piece[handle].pointer_device = pointer_device;
2123  if (pointer_device)
2124  priv->floating_piece_handle = handle;
2125 #if CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET || CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
2126  // See remark in cw_chessboard_move_floating_piece.
2127  rect.width = priv->sside;
2128  rect.height = priv->sside;
2129  gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
2130 #else
2131  // FIXME: schedule an expose event instead of this:
2132  rect.width = priv->sside;
2133  rect.height = priv->sside;
2134  gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
2135 #endif
2136 
2137  Dout(dc::cwchessboardwidget, "number_of_floating_pieces = " << priv->number_of_floating_pieces);
2138  Dout(dc::cwchessboardwidget, "Allocated handle " << handle);
2139 
2140  return handle;
2141 }
2142 
2144 {
2145  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_remove_floating_piece(" << chessboard << ", handle:" << handle << ")");
2146 
2147  CwChessboardPrivate* priv = chessboard->priv;
2148  GdkRectangle rect;
2149 
2150  g_assert(handle >= 0 && (gsize)handle < G_N_ELEMENTS(priv->floating_piece));
2151  g_assert(!is_empty_square(priv->floating_piece[handle].code));
2152 
2153  // See remark in cw_chessboard_move_floating_piece.
2154  rect.width = priv->sside;
2155  rect.height = priv->sside;
2156  rect.x = priv->floating_piece[handle].pixmap_x + priv->top_left_pixmap_x;
2157  rect.y = priv->floating_piece[handle].pixmap_y + priv->top_left_pixmap_y;
2158  gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
2159 #if CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
2160  gint col = cw_chessboard_x2col(chessboard, rect.x);
2161  gint row = cw_chessboard_y2row(chessboard, rect.y);
2162  if (is_inside_board(col, row))
2163  {
2164  BoardIndex index = convert_colrow2index(col, row);
2165  guint64 redraw_mask = 1;
2166  priv->need_redraw |= (redraw_mask << index);
2167  }
2168  col += priv->flip_board ? -1 : 1;
2169  if (is_inside_board(col, row))
2170  {
2171  BoardIndex index = convert_colrow2index(col, row);
2172  guint64 redraw_mask = 1;
2173  priv->need_redraw |= (redraw_mask << index);
2174  }
2175  row += priv->flip_board ? 1 : -1;
2176  if (is_inside_board(col, row))
2177  {
2178  BoardIndex index = convert_colrow2index(col, row);
2179  guint64 redraw_mask = 1;
2180  priv->need_redraw |= (redraw_mask << index);
2181  }
2182  col += priv->flip_board ? 1 : -1;
2183  if (is_inside_board(col, row))
2184  {
2185  BoardIndex index = convert_colrow2index(col, row);
2186  guint64 redraw_mask = 1;
2187  priv->need_redraw |= (redraw_mask << index);
2188  }
2189 #endif
2190  // Redraw background of widget if the old place of the floating piece is outside the board.
2191  priv->redraw_background = priv->redraw_background ||
2192  rect.x < priv->top_left_pixmap_x ||
2193  rect.x + rect.width > priv->bottom_right_pixmap_x ||
2194  rect.y < priv->top_left_pixmap_y ||
2195  rect.y + rect.height > priv->bottom_right_pixmap_y;
2196  if (priv->floating_piece[handle].pointer_device)
2197  priv->floating_piece_handle = -1;
2198  priv->number_of_floating_pieces--;
2199  priv->floating_piece[handle].code = empty_square;
2200  Dout(dc::cwchessboardwidget, "number_of_floating_pieces = " << priv->number_of_floating_pieces);
2201 }
2202 
2204 {
2205  g_assert(handle >= 0 && (gsize)handle < G_N_ELEMENTS(chessboard->priv->floating_piece));
2206  return chessboard->priv->floating_piece[handle].code;
2207 }
2208 
2209 void cw_chessboard_enable_hud_layer(CwChessboard* chessboard, guint hud)
2210 {
2211  g_return_if_fail(hud < number_of_hud_layers);
2212  chessboard->priv->has_hud_layer[hud] = TRUE;
2213  chessboard->priv->hud_need_redraw[hud] = (guint64)-1;
2214 }
2215 
2216 void cw_chessboard_disable_hud_layer(CwChessboard* chessboard, guint hud)
2217 {
2218  g_return_if_fail(hud < number_of_hud_layers);
2219  chessboard->priv->has_hud_layer[hud] = FALSE;
2220  chessboard->priv->hud_need_redraw[hud] = (guint64)-1;
2221 }
2222 
2223 static guint64 invalidate_arrow(CwChessboard* chessboard, gint col1, gint row1, gint col2, gint row2)
2224 {
2225  Dout(dc::cwchessboardwidget|continued_cf, "Calling invalidate_arrow(" << chessboard << ", " <<
2226  col1 << ", " << row1 << ", " << col2 << ", " << row2 << ") = ");
2227  guint64 result = 0;
2228  if (col1 == col2) // Vertical arrow?
2229  {
2230  if (row1 > row2)
2231  {
2232  gint tmp = row1;
2233  row1 = row2;
2234  row2 = tmp;
2235  }
2236  guint64 bit = (guint64)1 << convert_colrow2index(col1, row1);
2237  for (gint row = row1; row <= row2; ++row, bit <<= 8)
2238  {
2239  result |= bit;
2240  invalidate_square(chessboard, col1, row);
2241  }
2242  Dout(dc::finish, std::hex << result);
2243  return result;
2244  }
2245  else if (row1 == row2) // Horizontal arrow?
2246  {
2247  if (col1 > col2)
2248  {
2249  gint tmp = col1;
2250  col1 = col2;
2251  col2 = tmp;
2252  }
2253  guint64 bit = (guint64)1 << convert_colrow2index(col1, row1);
2254  for (gint col = col1; col <= col2; ++col, bit <<= 1)
2255  {
2256  result |= bit;
2257  invalidate_square(chessboard, col, row1);
2258  }
2259  Dout(dc::finish, std::hex << result);
2260  return result;
2261  }
2262  if (row1 > row2)
2263  {
2264  // Swap point 1 and 2.
2265  gint tmp;
2266  tmp = col1; col1 = col2; col2 = tmp;
2267  tmp = row1; row1 = row2; row2 = tmp;
2268  }
2269  double const arrow_width = 0.125; // FIXME: must be half the real arrow thickness.
2270  double delta = arrow_width*
2271  sqrt((row2 - row1) * (row2 - row1) + (col2 - col1) * (col2 - col1)) / (row2 - row1);
2272  gint col_start = col1;
2273  gint row = row1;
2274  result = 0;
2275  guint64 sign = 1;
2276  if (col1 > col2)
2277  {
2278  sign = 2;
2279  delta = -delta;
2280  }
2281  for (double r = row1 + 0.5; r < row2; r += 1.0)
2282  {
2283  double c = col1 + (r - row1) * (col2 - col1) / (row2 - row1);
2284  gint col_end = (gint)round(c + delta);
2285  guint64 mask1 = (sign << convert_colrow2index(col_start, row)) - 1;
2286  guint64 mask2 = ((3 - sign) << convert_colrow2index(col_end, row)) - 1;
2287  result |= mask1 ^ mask2;
2288  col_start = (gint)round(c - delta);
2289  ++row;
2290  }
2291  guint64 mask1 = (sign << convert_colrow2index(col_start, row)) - 1;
2292  guint64 mask2 = ((3 - sign) << convert_colrow2index(col2, row)) - 1;
2293  result |= mask1 ^ mask2;
2294  Dout(dc::finish, std::hex << result);
2295  guint64 bit = 1;
2296  for (gint row = 0; row < squares; ++row)
2297  for (gint col = 0; col < squares; ++col, bit <<= 1)
2298  if ((result & bit))
2299  invalidate_square(chessboard, col, row);
2300  return result;
2301 }
2302 
2304  gint begin_col, gint begin_row, gint end_col, gint end_row, GdkColor const* color)
2305 {
2306  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_add_arrow(" << chessboard << ", " <<
2307  begin_col << ", " << begin_row << ", " << end_col << ", " << end_row << ", " << color << ")");
2308 
2309  g_return_val_if_fail(begin_col != end_col || begin_row != end_row, NULL);
2310  g_return_val_if_fail(is_inside_board(begin_col, begin_row) && is_inside_board(end_col, end_row), NULL);
2311 
2312  Arrow* arrow = (Arrow*)g_malloc(sizeof(Arrow));
2313  g_ptr_array_add(chessboard->priv->arrows, arrow);
2314  arrow->begin_col = begin_col;
2315  arrow->begin_row = begin_row;
2316  arrow->end_col = end_col;
2317  arrow->end_row = end_row;
2318  arrow->color.red = color->red / 65535.0;
2319  arrow->color.green = color->green / 65535.0;
2320  arrow->color.blue = color->blue / 65535.0;
2321  guint64 content = invalidate_arrow(chessboard, begin_col, begin_row, end_col, end_row);
2322  guint64 start_square = (guint64)1 << convert_colrow2index(begin_col, begin_row);
2323  chessboard->priv->hud_need_redraw[0] |= (arrow->has_content[0] = start_square);
2324  chessboard->priv->hud_need_redraw[1] |= (arrow->has_content[1] = content ^ start_square);
2325  return arrow;
2326 }
2327 
2328 void cw_chessboard_remove_arrow(CwChessboard* chessboard, gpointer ptr)
2329 {
2330  Dout(dc::cwchessboardwidget, "Calling cw_chessboard_remove_arrow(" << chessboard << ", " << ptr << ")");
2331  if (g_ptr_array_remove_fast(chessboard->priv->arrows, ptr))
2332  {
2333  Arrow* arrow = (Arrow*)ptr;
2334  chessboard->priv->hud_need_redraw[0] |= arrow->has_content[0];
2335  chessboard->priv->hud_need_redraw[1] |= arrow->has_content[1];
2336  g_free(ptr);
2337  }
2338 }
2339 
2340 //------------------------------------------------------------------------------
2341 // Piece graphics functions.
2342 
2343 static double const black_line_width = CONST(black_line_width, 0.026);
2344 static double const white_line_width = CONST(white_line_width, 1.5 * black_line_width);
2345 
2346 static double snap_bottom(double y, double translate, double scale, double line_width)
2347 {
2348  if (scale < 27)
2349  return y;
2350  return (round((y + 0.5 * line_width) * scale - translate) + translate) / scale - 0.5 * line_width;
2351 }
2352 
2353 static double snap_top(double y, double translate, double scale, double line_width)
2354 {
2355  if (scale < 27)
2356  return y;
2357  return (round((y - 0.5 * line_width) * scale - translate) + translate) / scale + 0.5 * line_width;
2358 }
2359 
2360 static double snap_line_width(double line_width, double scale)
2361 {
2362  if (line_width * scale < 1.0)
2363  return line_width;
2364  return trunc(line_width * scale + 0.3) / scale;
2365 }
2366 
2367 // The fill color of the pieces.
2368 static void set_fill_color(cairo_t* cr, CwChessboardPrivate* priv, gboolean white)
2369 {
2370  if (white)
2371  cairo_set_source_rgb(cr, priv->white_piece_fill_color.red,
2372  priv->white_piece_fill_color.green,
2373  priv->white_piece_fill_color.blue);
2374  else
2375  cairo_set_source_rgb(cr, priv->black_piece_fill_color.red,
2376  priv->black_piece_fill_color.green,
2377  priv->black_piece_fill_color.blue);
2378 }
2379 
2380 // The line color of the pieces.
2381 static void set_line_color(cairo_t* cr, CwChessboardPrivate* priv, gboolean white)
2382 {
2383  if (white)
2384  cairo_set_source_rgb(cr, priv->white_piece_line_color.red,
2385  priv->white_piece_line_color.green,
2386  priv->white_piece_line_color.blue);
2387  else
2388  cairo_set_source_rgb(cr, priv->black_piece_line_color.red,
2389  priv->black_piece_line_color.green,
2390  priv->black_piece_line_color.blue);
2391 }
2392 
2393 // Draw pawn.
2394 void cw_chessboard_draw_pawn(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
2395 {
2396  static double const base_outside_diameter_cm = CONST(base_outside_diameter_cm, 3.265);
2397  static double const width_pawn_cm = CONST(width_pawn_cm, 5.31);
2398  static double const base_radius = CONST(base_radius, 0.5 * (base_outside_diameter_cm / width_pawn_cm - black_line_width));
2399  static double const mid_outside_diameter_cm = CONST(mid_outside_diameter_cm, 1.98);
2400  static double const mid_radius = CONST(mid_radius, 0.5 * (mid_outside_diameter_cm / width_pawn_cm - black_line_width));
2401  static double const head_outside_diameter_cm = CONST(head_outside_diameter_cm, 1.12);
2402  static double const head_radius = CONST(head_radius, 0.5 * (head_outside_diameter_cm / width_pawn_cm - black_line_width));
2403  static double const height_pawn_cm = CONST(height_pawn_cm, 5.43);
2404  static double const bottom_pawn_cm = CONST(bottom_pawn_cm, 0.58);
2405  static double const foot_height = CONST(foot_height, 0.0387);
2406  static double const base_y = CONST(base_y, 0.5 - bottom_pawn_cm / height_pawn_cm - 0.5 * black_line_width);
2407  static double const base_scale = CONST(base_scale, 0.931);
2408  static double const mid_y = CONST(mid_y, -0.0545);
2409  static double const top_offset_cm = CONST(top_offset_cm, 0.62);
2410  static double const head_y = CONST(head_y, -0.5 + top_offset_cm / height_pawn_cm + 0.5 * black_line_width + head_radius);
2411 
2412  static double const base_angle = CONST(base_angle, 1.148);
2413  static double const mid_angle1 = CONST(mid_angle1, 0.992);
2414  static double const inner_neck_width_cm = CONST(inner_neck_width_cm, 0.41);
2415  static double const neck_right = CONST(neck_right, 0.5 * (inner_neck_width_cm / width_pawn_cm + black_line_width));
2416  static double const head_angle = CONST(head_angle, asin(neck_right / head_radius));
2417  static double const mid_scale = CONST(mid_scale, (mid_y - (head_y + head_radius * cos(head_angle)) -
2418  0.1 * black_line_width) / sqrt(mid_radius * mid_radius - neck_right * neck_right));
2419  static double const mid_angle2 = CONST(mid_angle2, asin(head_radius * sin(head_angle) / mid_radius));
2420 
2421  double const base_y_sn = snap_bottom(base_y, y, scale, black_line_width);
2422 
2423  CwChessboardPrivate* priv = chessboard->priv;
2424 
2425  cairo_save(cr);
2426  cairo_translate(cr, x, y);
2427  cairo_scale(cr, scale, scale);
2428  cairo_set_line_width(cr, black_line_width);
2429 
2430  // Draw the left-side of the base.
2431  cairo_move_to(cr, -base_radius, base_y_sn);
2432  cairo_save(cr);
2433  cairo_translate(cr, 0.0, base_y_sn - foot_height);
2434  cairo_scale(cr, 1.0, base_scale);
2435  cairo_arc(cr, 0.0, 0.0, base_radius, -M_PI, -M_PI + base_angle);
2436  cairo_restore(cr);
2437 
2438  // Draw the left-side of the mid-section.
2439  cairo_save(cr);
2440  cairo_translate(cr, 0.0, mid_y);
2441  cairo_scale(cr, 1.0, mid_scale);
2442  cairo_arc(cr, 0.0, 0.0, mid_radius, -M_PI - mid_angle1, -0.5 * M_PI - mid_angle2);
2443  cairo_restore(cr);
2444 
2445  // Draw the head of the pawn.
2446  cairo_arc(cr, 0.0, head_y, head_radius, -1.5 * M_PI + head_angle, 0.5 * M_PI - head_angle);
2447 
2448  // Draw the right-side of the mid-section.
2449  cairo_save(cr);
2450  cairo_translate(cr, 0.0, mid_y);
2451  cairo_scale(cr, 1.0, mid_scale);
2452  cairo_arc(cr, 0.0, 0.0, mid_radius, -0.5 * M_PI + mid_angle2, mid_angle1);
2453  cairo_restore(cr);
2454 
2455  // Draw the right-side of the base.
2456  cairo_save(cr);
2457  cairo_translate(cr, 0.0, base_y_sn - foot_height);
2458  cairo_scale(cr, 1.0, base_scale);
2459  cairo_arc(cr, 0.0, 0.0, base_radius, -base_angle, 0.0);
2460  cairo_restore(cr);
2461  cairo_line_to(cr, base_radius, base_y_sn);
2462 
2463  // Draw the base line of the pawn, right to left.
2464  cairo_close_path(cr);
2465 
2466  set_fill_color(cr, priv, white);
2467  cairo_fill_preserve(cr);
2468  if (white)
2469  set_line_color(cr, priv, white);
2470  cairo_stroke(cr);
2471 
2472  cairo_restore(cr);
2473 }
2474 
2475 void cw_chessboard_draw_king(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
2476 {
2477  // Measurements, in cm.
2478  static double const blob_left_cm = CONST(blob_left_cm, 1.22);
2479  static double const band_edge_left_cm = CONST(band_edge_left_cm, 2.55);
2480  static double const band_left_cm = CONST(band_left_cm, 2.67);
2481  static double const inside_left_cm = CONST(inside_left_cm, 3.06);
2482  static double const center_blob_left_cm = CONST(center_blob_left_cm, 4.525);
2483  static double const cross_left_cm = CONST(cross_left_cm, 4.71);
2484  static double const width_king_cm = CONST(width_king_cm, 10.67);
2485  static double const bottom_king_cm = CONST(bottom_king_cm, 1.155);
2486  static double const band_line_top_cm = CONST(band_line_top_cm, 2.95);
2487  static double const band_top_king_cm = CONST(band_top_king_cm, 4.04);
2488  static double const center_y_cm = CONST(center_y_cm, 5.02);
2489  static double const blob_top_cm = CONST(blob_top_cm, 7.4); // 7.28
2490  static double const center_blob_top_cm = CONST(center_blob_top_cm, 8.18);
2491  static double const cross_y_king_cm = CONST(cross_y_king_cm, 9.17);
2492  static double const cross_top_cm = CONST(cross_top_cm, 9.86);
2493  static double const height_king_cm = CONST(height_king_cm, 10.86);
2494  // Derived values.
2495  static double const mid_x_king_cm = CONST(mid_x_king_cm, width_king_cm / 2);
2496  static double const mid_y_king_cm = CONST(mid_y_king_cm, height_king_cm / 2);
2497 
2498  // Same, in coordinates.
2499  static double const blob_left = CONST(blob_left, (blob_left_cm - mid_x_king_cm) / width_king_cm);
2500  static double const band_edge_left = CONST(band_edge_left, (band_edge_left_cm - mid_x_king_cm) / width_king_cm);
2501  static double const band_left = CONST(band_left, (band_left_cm - mid_x_king_cm) / width_king_cm);
2502  static double const inside_left = CONST(inside_left, (inside_left_cm - mid_x_king_cm) / width_king_cm);
2503  static double const center_blob_left = CONST(center_blob_left, (center_blob_left_cm - mid_x_king_cm) / width_king_cm);
2504  static double const cross_left = CONST(cross_left, (cross_left_cm - mid_x_king_cm) / width_king_cm);
2505  static double const bottom_king = CONST(bottom_king, (mid_y_king_cm - bottom_king_cm) / height_king_cm);
2506  static double const band_line_top = CONST(band_line_top, (mid_y_king_cm - band_line_top_cm) / height_king_cm);
2507  static double const band_top_king = CONST(band_top_king, (mid_y_king_cm - band_top_king_cm) / height_king_cm);
2508  static double const center_y = CONST(center_y, (mid_y_king_cm - center_y_cm) / height_king_cm);
2509  static double const blob_top = CONST(blob_top, (mid_y_king_cm - blob_top_cm) / height_king_cm);
2510  static double const center_blob_top = CONST(center_blob_top, (mid_y_king_cm - center_blob_top_cm) / height_king_cm);
2511  static double const cross_y_king = CONST(cross_y_king, (mid_y_king_cm - cross_y_king_cm) / height_king_cm);
2512  static double const cross_top = CONST(cross_top, (mid_y_king_cm - cross_top_cm) / height_king_cm);
2513 
2514  // Derived values.
2515  static double const inside_radius_king = CONST(inside_radius_king, -inside_left);
2516  static double const inside_scale_king = CONST(inside_scale_king, 0.180132); // Same value as used for the queen.
2517  static double const band_top_radius = CONST(band_top_radius, -band_edge_left);
2518  static double const band_top_scale = CONST(band_top_scale, inside_scale_king);
2519  static double const band_top_y = CONST(band_top_y, band_top_king + band_top_radius * band_top_scale);
2520  static double const cos_alpha = CONST(cos_alpha, band_left / band_edge_left);
2521  static double const alpha = CONST(alpha, acos(cos_alpha));
2522  static double const band_bottom_scale = CONST(band_bottom_scale, inside_scale_king);
2523  static double const band_bottom_radius = CONST(band_bottom_radius, band_top_radius);
2524  static double const band_bottom_y = CONST(band_bottom_y, bottom_king - band_bottom_radius * band_bottom_scale);
2525  static double const dx = CONST(dx, band_top_radius * (1.0 - cos_alpha));
2526  static double const band_line_scale = CONST(band_line_scale, band_top_scale);
2527  static double const band_line_radius = CONST(band_line_radius, band_top_radius - dx);
2528  static double const band_line_y = CONST(band_line_y, band_line_top + band_line_radius * band_line_scale);
2529  static double const blob_radius = CONST(blob_radius, 0.7071067 * (blob_left + band_top_y - band_left - blob_top));
2530  static double const blob_x = CONST(blob_x, blob_left + blob_radius);
2531  static double const blob_y = CONST(blob_y, blob_top + blob_radius);
2532  static double const center_blob_radius = CONST(center_blob_radius, -center_blob_left);
2533  static double const center_blob_y = CONST(center_blob_y, center_blob_top + center_blob_radius);
2534  // Manual adjustment... looks better.
2535  static double const adjusted_center_blob_radius = CONST(adjusted_center_blob_radius, center_blob_radius + 0.01);
2536  static double const beta_king = CONST(beta_king, asin(adjusted_center_blob_radius / (center_y - center_blob_y)));
2537  static double const center2_y = CONST(center2_y, blob_y - blob_x - 1.4142136 * blob_radius);
2538 
2539  CwChessboardPrivate* priv = chessboard->priv;
2540 
2541  cairo_save(cr);
2542  cairo_translate(cr, x, y);
2543  cairo_scale(cr, scale, scale);
2544  cairo_set_line_width(cr, black_line_width);
2545 
2546  // Draw left blob.
2547  cairo_move_to(cr, band_left, band_top_y);
2548  cairo_arc(cr, blob_x, blob_y, blob_radius, 0.75 * M_PI, 1.75 * M_PI);
2549  cairo_line_to(cr, 0.0, center2_y);
2550 
2551  // Draw right blob.
2552  cairo_arc(cr, -blob_x, blob_y, blob_radius, -0.75 * M_PI, 0.25 * M_PI);
2553  cairo_line_to(cr, -band_left, band_top_y);
2554 
2555  set_fill_color(cr, priv, white);
2556  cairo_fill_preserve(cr);
2557 
2558  // Draw vertical line in the middle.
2559  cairo_move_to(cr, 0.0, band_top_y);
2560  cairo_line_to(cr, 0.0, center_y);
2561 
2562  if (white)
2563  set_line_color(cr, priv, white);
2564  cairo_stroke(cr);
2565 
2566  // Draw center blob.
2567  cairo_move_to(cr, 0.0, center_y);
2568  cairo_arc(cr, 0.0, center_blob_y, adjusted_center_blob_radius, M_PI - beta_king, beta_king);
2569  cairo_close_path(cr);
2570 
2571  if (white)
2572  set_fill_color(cr, priv, white);
2573  cairo_fill_preserve(cr);
2574  if (white)
2575  set_line_color(cr, priv, white);
2576  cairo_stroke(cr);
2577 
2578  // Draw cross.
2579  cairo_move_to(cr, 0.0, center_blob_y - adjusted_center_blob_radius);
2580  cairo_line_to(cr, 0.0, cross_top);
2581  cairo_move_to(cr, cross_left, cross_y_king);
2582  cairo_line_to(cr, -cross_left, cross_y_king);
2583  cairo_stroke(cr);
2584 
2585  // Draw half ellipse just below the blobs.
2586  cairo_save(cr);
2587  cairo_translate(cr, 0.0, band_top_y);
2588  cairo_scale(cr, 1.0, band_top_scale);
2589  cairo_arc(cr, 0.0, 0.0, band_top_radius, M_PI - alpha, 2 * M_PI + alpha);
2590  cairo_restore(cr);
2591 
2592  // Draw right side of the upper band.
2593  cairo_line_to(cr, -band_left, band_line_y);
2594 
2595  // Draw right side of lower band and bottom.
2596  cairo_save(cr);
2597  cairo_translate(cr, 0.0, band_bottom_y);
2598  cairo_scale(cr, 1.0, band_bottom_scale);
2599  cairo_arc(cr, 0.0, 0.0, band_bottom_radius, 0.0, M_PI);
2600  cairo_restore(cr);
2601 
2602  // Draw left side of lower band.
2603  cairo_line_to(cr, band_left, band_line_y);
2604 
2605  // Draw left side of upper band.
2606  cairo_close_path(cr);
2607 
2608  cairo_path_t* path = cairo_copy_path(cr);
2609 
2610  if (white)
2611  set_fill_color(cr, priv, white);
2612  cairo_fill(cr);
2613 
2614  // Draw lower half ellipse of upper band.
2615  cairo_save(cr);
2616  cairo_translate(cr, 0.0, band_line_y);
2617  cairo_scale(cr, 1.0, band_line_scale);
2618  cairo_arc(cr, 0.0, 0.0, band_line_radius, -M_PI, 0.0);
2619  cairo_restore(cr);
2620 
2621  cairo_new_sub_path(cr);
2622 
2623  // Draw opening at bottom, align it with the real bottom.
2624  cairo_save(cr);
2625  cairo_translate(cr, 0.0, band_bottom_y + band_bottom_radius * band_bottom_scale - inside_radius_king * inside_scale_king);
2626  cairo_scale(cr, 1.0, inside_scale_king);
2627  if (white)
2628  cairo_arc(cr, 0.0, 0.0, inside_radius_king, -M_PI, M_PI);
2629  else
2630  cairo_arc(cr, 0.0, 0.0, inside_radius_king, -M_PI - alpha, alpha);
2631  cairo_restore(cr);
2632 
2633  set_line_color(cr, priv, white);
2634  if (white)
2635  cairo_stroke(cr);
2636  else
2637  {
2638  cairo_set_line_width(cr, white_line_width);
2639  cairo_stroke(cr);
2640  cairo_set_line_width(cr, black_line_width);
2641  }
2642 
2643  cairo_append_path(cr, path);
2644  if (!white)
2645  set_fill_color(cr, priv, white);
2646  cairo_stroke(cr);
2647 
2648  cairo_path_destroy(path);
2649 
2650  if (!white)
2651  {
2652  // Draw the white lines inside the blobs.
2653 
2654  static double const av_line_width = CONST(av_line_width, 0.5 * (black_line_width + white_line_width));
2655  static double const da = CONST(da, av_line_width / band_top_radius);
2656  static double const dy = CONST(dy, av_line_width * tan(0.5 * beta_king));
2657 
2658  cairo_save(cr);
2659  cairo_translate(cr, 0.0, band_top_y);
2660  cairo_scale(cr, 1.0, band_top_scale);
2661  cairo_arc_negative(cr, 0.0, 0.0, band_top_radius, -0.5 * M_PI - da, M_PI + alpha + da);
2662  cairo_restore(cr);
2663 
2664  cairo_arc(cr, blob_x, blob_y, blob_radius - av_line_width, 0.75 * M_PI, 1.75 * M_PI);
2665 
2666  static double const center2b_y = CONST(center2b_y, center2_y + av_line_width * 1.4142136);
2667  static double const sin_beta = CONST(sin_beta, adjusted_center_blob_radius / (center_y - center_blob_y));
2668  static double const x_king = CONST(x_king, sin_beta * (center_y + av_line_width / sin_beta - center2b_y) / sin(0.25 * M_PI - beta_king));
2669  static double const y_king = CONST(y_king, center2b_y - x_king);
2670 
2671  cairo_line_to(cr, -x_king, y_king);
2672  cairo_line_to(cr, -av_line_width, center_y + dy);
2673 
2674  cairo_close_path(cr);
2675 
2676  cairo_new_sub_path(cr);
2677 
2678  cairo_save(cr);
2679  cairo_translate(cr, 0.0, band_top_y);
2680  cairo_scale(cr, 1.0, band_top_scale);
2681  cairo_arc_negative(cr, 0.0, 0.0, band_top_radius, -alpha - da, -0.5 * M_PI + da);
2682  cairo_restore(cr);
2683 
2684  cairo_line_to(cr, av_line_width, center_y + dy);
2685  cairo_line_to(cr, x_king, y_king);
2686 
2687  cairo_arc(cr, -blob_x, blob_y, blob_radius - av_line_width, -0.75 * M_PI, 0.25 * M_PI);
2688 
2689  cairo_close_path(cr);
2690 
2691  cairo_new_sub_path(cr);
2692 
2693  cairo_move_to(cr, 0.0, center_y - av_line_width / sin_beta);
2694  cairo_arc(cr, 0.0, center_blob_y, adjusted_center_blob_radius - av_line_width, M_PI - beta_king, beta_king);
2695 
2696  cairo_close_path(cr);
2697 
2698  set_line_color(cr, priv, white);
2699  cairo_set_line_width(cr, white_line_width);
2700  cairo_stroke(cr);
2701  }
2702 
2703  cairo_restore(cr);
2704 }
2705 
2706 void cw_chessboard_draw_queen(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
2707 {
2708  // Measurements, in cm.
2709  static double const width_queen_cm = CONST(width_queen_cm, 5.34);
2710  static double const inside_width_cm = CONST(inside_width_cm, 2.97);
2711  static double const band1_width_cm = CONST(band1_width_cm, 2.59);
2712  static double const crown_bottom_width_cm = CONST(crown_bottom_width_cm, 3.31);
2713  static double const height_queen_cm = CONST(height_queen_cm, 5.39);
2714  static double const bottom_queen_cm = CONST(bottom_queen_cm, 0.5);
2715  static double const inside_height_cm = CONST(inside_height_cm, 0.54);
2716  static double const band1_height_cm = CONST(band1_height_cm, 0.47);
2717  static double const band2_height_cm = CONST(band2_height_cm, 0.43);
2718  static double const tooth_outside_cm = CONST(tooth_outside_cm, 1.83);
2719  static double const tooth_inside_cm = CONST(tooth_inside_cm, 2.20);
2720  static double const tooth_inside2_cm = CONST(tooth_inside2_cm, 2.36);
2721  static double const ball_outside_diameter_cm = CONST(ball_outside_diameter_cm, 0.6);
2722  static double const ball_top1_cm = CONST(ball_top1_cm, 4.31);
2723  static double const ball_right1_cm = CONST(ball_right1_cm, 0.90);
2724  static double const ball_top2_cm = CONST(ball_top2_cm, 4.80);
2725  static double const ball_right2_cm = CONST(ball_right2_cm, 1.88);
2726  static double const tooth3_x_cm = CONST(tooth3_x_cm, 2.25);
2727  // Derived values.
2728  static double const mid_x_queen_cm = CONST(mid_x_queen_cm, width_queen_cm / 2);
2729  static double const mid_y_queen_cm = CONST(mid_y_queen_cm, height_queen_cm / 2);
2730 
2731  // Same, in coordinates.
2732  static double const inside_width = CONST(inside_width, inside_width_cm / width_queen_cm);
2733  static double const band1_width = CONST(band1_width, band1_width_cm / width_queen_cm);
2734  static double const crown_bottom_width = CONST(crown_bottom_width, crown_bottom_width_cm / width_queen_cm);
2735  static double const bottom_queen = CONST(bottom_queen, (mid_y_queen_cm - bottom_queen_cm) / height_queen_cm);
2736  static double const inside_height = CONST(inside_height, inside_height_cm / height_queen_cm);
2737  static double const band1_height = CONST(band1_height, band1_height_cm / height_queen_cm);
2738  static double const band2_height = CONST(band2_height, band2_height_cm / height_queen_cm);
2739  static double const tooth_outside = CONST(tooth_outside, (mid_y_queen_cm - tooth_outside_cm) / height_queen_cm);
2740  static double const tooth_inside = CONST(tooth_inside, (mid_y_queen_cm - tooth_inside_cm) / height_queen_cm);
2741  static double const tooth_inside2 = CONST(tooth_inside2, (mid_y_queen_cm - tooth_inside2_cm) / height_queen_cm);
2742  static double const ball_outside_diameter = CONST(ball_outside_diameter, ball_outside_diameter_cm / height_queen_cm);
2743  static double const ball_top1 = CONST(ball_top1, (mid_y_queen_cm - ball_top1_cm) / height_queen_cm);
2744  static double const ball_right1 = CONST(ball_right1, (ball_right1_cm - mid_x_queen_cm) / width_queen_cm);
2745  static double const ball_top2 = CONST(ball_top2, (mid_y_queen_cm - ball_top2_cm) / height_queen_cm);
2746  static double const ball_right2 = CONST(ball_right2, (ball_right2_cm - mid_x_queen_cm) / width_queen_cm);
2747  static double const tooth3_x = CONST(tooth3_x, (tooth3_x_cm - mid_x_queen_cm) / width_queen_cm);
2748 
2749  // Derived values.
2750  static double const inside_radius_queen = CONST(inside_radius_queen, inside_width / 2);
2751  static double const inside_scale_queen = CONST(inside_scale_queen, inside_height / inside_width);
2752  static double const inside_y_queen = CONST(inside_y_queen, bottom_queen - inside_radius_queen * inside_scale_queen);
2753  static double const band1_radius = CONST(band1_radius, band1_width / 2);
2754  static double const band1_scale = CONST(band1_scale, inside_scale_queen);
2755  static double const band1_y = CONST(band1_y, bottom_queen - inside_height - band1_height + band1_radius * band1_scale);
2756  static double const crown_bottom_left = CONST(crown_bottom_left, -crown_bottom_width / 2);
2757  static double const band2_radius = CONST(band2_radius ,band1_radius +
2758  (-band1_radius - crown_bottom_left) * band2_height / (band1_y - tooth_outside));
2759  static double const band2_scale = CONST(band2_scale, band1_scale);
2760  static double const band2_y = CONST(band2_y, bottom_queen - inside_height - band1_height - band2_height + band2_radius * band2_scale);
2761  static double const ball1_x = CONST(ball1_x, ball_right1 - ball_outside_diameter / 2);
2762  static double const ball2_x = CONST(ball2_x, ball_right2 - ball_outside_diameter / 2);
2763  static double const ball1_y = CONST(ball1_y, ball_top1 + ball_outside_diameter / 2);
2764  static double const ball2_y = CONST(ball2_y, ball_top2 + ball_outside_diameter / 2);
2765  static double const ball_radius_queen = CONST(ball_radius_queen, (ball_outside_diameter - black_line_width) / 2);
2766  // We calculate ball3_y, so it lays on a perfect circle with the other balls.
2767  // The distance from ballN to a point (0, ball_center_y) is:
2768  // sqrt(ballN_x^2 + (ballN_y - ball_center_y)^2), and we find
2769  // ball_center_y by setting this distance equal for ball1 and 2:
2770  // ball1_x^2 + ball1_y^2 - 2 ball1_y ball_center_y = ball2_x^2 + ball2_y^2 - 2 ball2_y ball_center_y -->
2771  static double const ball_center_y = CONST(ball_center_y,
2772  0.5 * (ball2_x * ball2_x + ball2_y * ball2_y - ball1_x * ball1_x - ball1_y * ball1_y) / (ball2_y - ball1_y));
2773  static double const ball3_y = CONST(ball3_y,
2774  ball_center_y - sqrt(ball1_x * ball1_x + (ball1_y - ball_center_y) * (ball1_y - ball_center_y)));
2775  // The tooth points are derived (which turns out better than measuring them).
2776  static double const ball1_angle = CONST(ball1_angle, atan((0.5 * (crown_bottom_left + ball2_x) - ball1_x) / (tooth_outside - ball1_y)));
2777  static double const tooth1_x = CONST(tooth1_x, ball1_x + ball_radius_queen * sin(ball1_angle));
2778  static double const tooth2_x = CONST(tooth2_x, ball2_x);
2779  static double const tooth1_top = CONST(tooth1_top, ball1_y + ball_radius_queen * cos(ball1_angle));
2780  static double const tooth2_top = CONST(tooth2_top, ball2_y + ball_radius_queen);
2781  static double const tooth3_top = CONST(tooth3_top, ball3_y + ball_radius_queen);
2782 
2783  CwChessboardPrivate* priv = chessboard->priv;
2784 
2785  cairo_save(cr);
2786  cairo_translate(cr, x, y);
2787  cairo_scale(cr, scale, scale);
2788  cairo_set_line_width(cr, black_line_width);
2789 
2790  // Draw the outline of the queen.
2791  // First fill, then stroke.
2792  for (int stroke = 0; stroke < 2; ++stroke)
2793  {
2794  // Draw the right side.
2795  cairo_move_to(cr, -tooth1_x, tooth1_top);
2796  cairo_line_to(cr, -crown_bottom_left, tooth_outside);
2797  cairo_line_to(cr, band1_radius, band1_y);
2798  // The call to arc() draws the last line part, to (inside_radius_queen, inside_y_queen).
2799 
2800  // Draw the half ellipse that makes out the bottom.
2801  cairo_save(cr);
2802  cairo_translate(cr, 0.0, inside_y_queen);
2803  cairo_scale(cr, 1.0, inside_scale_queen);
2804  cairo_arc(cr, 0.0, 0.0, inside_radius_queen, 0.0, M_PI);
2805  cairo_restore(cr);
2806 
2807  // Draw the left side.
2808  cairo_line_to(cr, -band1_radius, band1_y);
2809  cairo_line_to(cr, crown_bottom_left, tooth_outside);
2810  cairo_line_to(cr, tooth1_x, tooth1_top);
2811 
2812  // The lines of the teeth should not be 'connected' when we are stroking.
2813  if (stroke)
2814  {
2815  cairo_new_sub_path(cr);
2816  cairo_move_to(cr, tooth1_x, tooth1_top);
2817  }
2818 
2819  // Draw right-side of left-most tooth.
2820  cairo_line_to(cr, tooth2_x, tooth_inside);
2821 
2822  // Draw left-side of second tooth.
2823  cairo_line_to(cr, tooth2_x, tooth2_top);
2824 
2825  if (stroke)
2826  {
2827  cairo_new_sub_path(cr);
2828  cairo_move_to(cr, tooth2_x, tooth2_top);
2829  }
2830 
2831  // Draw right-side of second tooth.
2832  cairo_line_to(cr, tooth3_x, tooth_inside2);
2833 
2834  // Draw left-side of middle tooth.
2835  cairo_line_to(cr, 0.0, tooth3_top);
2836 
2837  if (stroke)
2838  {
2839  cairo_new_sub_path(cr);
2840  cairo_move_to(cr, 0.0, tooth3_top);
2841  }
2842 
2843  // Draw right-side of middle tooth.
2844  cairo_line_to(cr, -tooth3_x, tooth_inside2);
2845 
2846  // Draw left-side of fourth tooth.
2847  cairo_line_to(cr, -tooth2_x, tooth2_top);
2848 
2849  if (stroke)
2850  {
2851  cairo_new_sub_path(cr);
2852  cairo_move_to(cr, -tooth2_x, tooth2_top);
2853  }
2854 
2855  // Draw right-side of fourth tooth.
2856  cairo_line_to(cr, -tooth2_x, tooth_inside);
2857 
2858  // Draw left-side of last tooth.
2859  cairo_line_to(cr, -tooth1_x, tooth1_top);
2860 
2861  if (stroke)
2862  {
2863  if (white)
2864  set_line_color(cr, priv, white);
2865  else
2866  set_fill_color(cr, priv, white);
2867  cairo_stroke(cr);
2868  }
2869  else
2870  {
2871  set_fill_color(cr, priv, white);
2872  cairo_fill(cr);
2873 
2874  // Draw the upper side of the bottom ellipse.
2875  cairo_save(cr);
2876  cairo_translate(cr, 0.0, inside_y_queen);
2877  cairo_scale(cr, 1.0, inside_scale_queen);
2878  cairo_arc(cr, 0.0, 0.0, inside_radius_queen, -M_PI, 0.0);
2879  cairo_restore(cr);
2880 
2881  cairo_new_sub_path(cr);
2882 
2883  // Draw the half ellipse of band1.
2884  cairo_save(cr);
2885  cairo_translate(cr, 0.0, band1_y);
2886  cairo_scale(cr, 1.0, band1_scale);
2887  cairo_arc(cr, 0.0, 0.0, band1_radius, -M_PI, 0.0);
2888  cairo_restore(cr);
2889 
2890  set_line_color(cr, priv, white);
2891  if (white)
2892  cairo_stroke(cr);
2893  else
2894  {
2895  cairo_set_line_width(cr, white_line_width);
2896  cairo_stroke(cr);
2897  cairo_set_line_width(cr, black_line_width);
2898  }
2899  }
2900  }
2901 
2902  // Draw the five balls.
2903 
2904  cairo_arc(cr, ball1_x, ball1_y, ball_radius_queen, -M_PI, M_PI);
2905 
2906  if (white)
2907  set_fill_color(cr, priv, white);
2908  cairo_fill_preserve(cr);
2909  if (white)
2910  set_line_color(cr, priv, white);
2911  cairo_stroke(cr);
2912 
2913  cairo_arc(cr, ball2_x, ball2_y, ball_radius_queen, -M_PI, M_PI);
2914 
2915  if (white)
2916  set_fill_color(cr, priv, white);
2917  cairo_fill_preserve(cr);
2918  if (white)
2919  set_line_color(cr, priv, white);
2920  cairo_stroke(cr);
2921 
2922  cairo_arc(cr, 0.0, ball3_y, ball_radius_queen, -M_PI, M_PI);
2923 
2924  if (white)
2925  set_fill_color(cr, priv, white);
2926  cairo_fill_preserve(cr);
2927  if (white)
2928  set_line_color(cr, priv, white);
2929  cairo_stroke(cr);
2930 
2931  cairo_arc(cr, -ball2_x, ball2_y, ball_radius_queen, -M_PI, M_PI);
2932 
2933  if (white)
2934  set_fill_color(cr, priv, white);
2935  cairo_fill_preserve(cr);
2936  if (white)
2937  set_line_color(cr, priv, white);
2938  cairo_stroke(cr);
2939 
2940  cairo_arc(cr, -ball1_x, ball1_y, ball_radius_queen, -M_PI, M_PI);
2941 
2942  if (white)
2943  set_fill_color(cr, priv, white);
2944  cairo_fill_preserve(cr);
2945  if (white)
2946  set_line_color(cr, priv, white);
2947  cairo_stroke(cr);
2948 
2949  if (white)
2950  {
2951  // Draw the splines at the bottom of the teeth.
2952  // The top left y-coordinate.
2953  static double const y0_queen = CONST(y0_queen, 0.0952);
2954  // The y-coordinate of the middle point.
2955  static double const ym = CONST(ym, 0.0331);
2956  // The top left y-coordinate lays on the left side of the first tooth.
2957  // Calculate the x-coordinate:
2958  static double const x0_queen = CONST(x0_queen, tooth1_x + (y0_queen - tooth1_top) * (crown_bottom_left - tooth1_x) / (tooth_outside - tooth1_top));
2959  // The (apparent) tilting angle.
2960  static double const tilt_angle = CONST(tilt_angle, atan((ym - y0_queen) / x0_queen));
2961 
2962  // The angle that the control lines make with the y-axis, before
2963  // mapping them onto a cylinder and before tilting the cylinder.
2964  static double const beta_queen = CONST(beta_queen, 1.202);
2965  // The length of the control lines.
2966  static double const len = CONST(len, 0.1728);
2967  // The y-value of the control points before tilting (relative to y0_queen).
2968  static double const py = CONST(py, len * cos(beta_queen));
2969  static double const y0_plus_py_cos_tilt_angle = CONST(y0_plus_py_cos_tilt_angle, y0_queen + py * cos(tilt_angle));
2970  static double const sin_tilt_angle = CONST(sin_tilt_angle, sin(tilt_angle));
2971  // The x-offset of the control points (this is an angle).
2972  static double px_offset = CONST(px_offset, len * sin(beta_queen));
2973 
2974  cairo_move_to(cr, crown_bottom_left, tooth_outside);
2975  cairo_line_to(cr, x0_queen, y0_queen);
2976 
2977  // We have to draw N splines.
2978  int const N = 4;
2979  for (int i = 0; i < N; ++i)
2980  {
2981  double const alpha = i * M_PI / N;
2982  // The spline points before tilting.
2983  double px2 = x0_queen * cos(alpha + px_offset);
2984  double pz2 = - x0_queen * sin(alpha + px_offset);
2985  double px3 = x0_queen * cos(alpha + M_PI / N - px_offset);
2986  double pz3 = - x0_queen * sin(alpha + M_PI / N - px_offset);
2987  double px4 = x0_queen * cos(alpha + M_PI / N);
2988  double pz4 = - x0_queen * sin(alpha + M_PI / N);
2989  // Calculate the tilted values. This only has influence on the y value
2990  // (we rotate around the x-axis, and the resulting z-value doesn't interest us).
2991  double tpy2 = y0_plus_py_cos_tilt_angle - pz2 * sin_tilt_angle;
2992  double tpy3 = y0_plus_py_cos_tilt_angle - pz3 * sin_tilt_angle;
2993  double tpy4 = y0_queen - pz4 * sin_tilt_angle;
2994  cairo_curve_to(cr, px2, tpy2, px3, tpy3, px4, tpy4);
2995  }
2996 
2997  cairo_line_to(cr, -crown_bottom_left, tooth_outside);
2998  }
2999 
3000  // Draw the half ellipse of band2.
3001  cairo_save(cr);
3002  cairo_translate(cr, 0.0, band2_y);
3003  cairo_scale(cr, 1.0, band2_scale);
3004  cairo_arc_negative(cr, 0.0, 0.0, band2_radius, -0.15, -M_PI + 0.15);
3005  cairo_restore(cr);
3006 
3007  if (white)
3008  {
3009  cairo_close_path(cr);
3010  set_fill_color(cr, priv, white);
3011  cairo_fill_preserve(cr);
3012  }
3013  else
3014  cairo_set_line_width(cr, white_line_width);
3015  set_line_color(cr, priv, white);
3016  cairo_stroke(cr);
3017 
3018  cairo_restore(cr);
3019 }
3020 
3021 void cw_chessboard_draw_rook(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
3022 {
3023  // Measurements, in cm.
3024  static double const width_rook_cm = CONST(width_rook_cm, 5.33);
3025  static double const foot_left_cm = CONST(foot_left_cm, 0.90);
3026  static double const base_left_cm = CONST(base_left_cm, 1.26);
3027  static double const tower_left_cm = CONST(tower_left_cm, 1.64);
3028  static double const opening_left_cm = CONST(opening_left_cm, 1.795);
3029  static double const opening_right_cm = CONST(opening_right_cm, 2.315);
3030  static double const height_rook_cm = CONST(height_rook_cm, 5.30);
3031  static double const bottom_rook_cm = CONST(bottom_rook_cm, 0.58);
3032  static double const foot_top_cm = CONST(foot_top_cm, 0.95);
3033  static double const base_top_cm = CONST(base_top_cm, 1.41);
3034  static double const tower_bottom_cm = CONST(tower_bottom_cm, 1.76);
3035  static double const tower_top_cm = CONST(tower_top_cm, 3.43);
3036  static double const top_bottom_cm = CONST(top_bottom_cm, 3.81);
3037  static double const opening_bottom_cm = CONST(opening_bottom_cm, 4.25);
3038  // static double const top_top_cm = 4.61;
3039 
3040  // In coordinates.
3041  static double const foot_left = CONST(foot_left, -0.5 + foot_left_cm / width_rook_cm + 0.5 * black_line_width);
3042  static double const base_left = CONST(base_left, -0.5 + base_left_cm / width_rook_cm + 0.5 * black_line_width);
3043  static double const tower_left = CONST(tower_left, -0.5 + tower_left_cm / width_rook_cm + 0.5 * black_line_width);
3044  static double const opening_left = CONST(opening_left, -0.5 + opening_left_cm / width_rook_cm + 0.5 * black_line_width);
3045  static double const opening_right = CONST(opening_right, -0.5 + opening_right_cm / width_rook_cm + 0.5 * black_line_width);
3046  static double const bottom_rook = CONST(bottom_rook, 0.5 - bottom_rook_cm / height_rook_cm - 0.5 * black_line_width);
3047  static double const foot_top = CONST(foot_top, 0.5 - foot_top_cm / height_rook_cm - 0.5 * black_line_width);
3048  static double const base_top = CONST(base_top, 0.5 - base_top_cm / height_rook_cm - 0.5 * black_line_width);
3049  static double const tower_bottom = CONST(tower_bottom, 0.5 - tower_bottom_cm / height_rook_cm - 0.5 * black_line_width);
3050  static double const tower_top = CONST(tower_top, 0.5 - tower_top_cm / height_rook_cm - 0.5 * black_line_width);
3051  static double const top_bottom = CONST(top_bottom, 0.5 - top_bottom_cm / height_rook_cm - 0.5 * black_line_width);
3052  static double const opening_bottom = CONST(opening_bottom, 0.5 - opening_bottom_cm / height_rook_cm - 0.5 * black_line_width);
3053  // static double const top_top = 0.5 - top_top_cm / height_rook_cm - 0.5 * black_line_width;
3054  // For alignment purposes, it's better to have the rook* precisely* centered.
3055  static double const top_top = CONST(top_top, -bottom_rook);
3056 
3057  // Snapped coordinates.
3058  double const inner_line_width = white ? black_line_width : snap_line_width(white_line_width, scale);
3059  double const bottom_sn = snap_bottom(bottom_rook, y, scale, black_line_width);
3060  double const foot_top_sn = snap_bottom(foot_top, y, scale, inner_line_width);
3061  double const base_top_sn = snap_bottom(base_top, y, scale, inner_line_width);
3062  double const tower_bottom_sn = snap_bottom(tower_bottom, y, scale, inner_line_width);
3063  double const tower_top_sn = snap_top(tower_top, y, scale, inner_line_width);
3064  double const top_bottom_sn = snap_top(top_bottom, y, scale, inner_line_width);
3065  double const opening_bottom_sn = snap_top(opening_bottom, y, scale, black_line_width);
3066  double const top_top_sn = snap_top(top_top, y, scale, black_line_width);
3067 
3068  CwChessboardPrivate* priv = chessboard->priv;
3069 
3070  cairo_save(cr);
3071  cairo_translate(cr, x, y);
3072  cairo_scale(cr, scale, scale);
3073  cairo_set_line_width(cr, black_line_width);
3074 
3075  // Draw left side.
3076  cairo_move_to(cr, foot_left, bottom_sn);
3077  cairo_line_to(cr, foot_left, foot_top_sn);
3078  cairo_line_to(cr, base_left, foot_top_sn);
3079  cairo_line_to(cr, base_left, base_top_sn);
3080  cairo_line_to(cr, tower_left, tower_bottom_sn);
3081  cairo_line_to(cr, tower_left, tower_top_sn);
3082  cairo_line_to(cr, base_left, top_bottom_sn);
3083  cairo_line_to(cr, base_left, top_top_sn);
3084 
3085  // Draw top side.
3086  cairo_line_to(cr, opening_left, top_top_sn);
3087  cairo_line_to(cr, opening_left, opening_bottom_sn);
3088  cairo_line_to(cr, opening_right, opening_bottom_sn);
3089  cairo_line_to(cr, opening_right, top_top_sn);
3090  cairo_line_to(cr, -opening_right, top_top_sn);
3091  cairo_line_to(cr, -opening_right, opening_bottom_sn);
3092  cairo_line_to(cr, -opening_left, opening_bottom_sn);
3093  cairo_line_to(cr, -opening_left, top_top_sn);
3094  cairo_line_to(cr, -base_left, top_top_sn);
3095 
3096  // Draw right side.
3097  cairo_line_to(cr, -base_left, top_bottom_sn);
3098  cairo_line_to(cr, -tower_left, tower_top_sn);
3099  cairo_line_to(cr, -tower_left, tower_bottom_sn);
3100  cairo_line_to(cr, -base_left, base_top_sn);
3101  cairo_line_to(cr, -base_left, foot_top_sn);
3102  cairo_line_to(cr, -foot_left, foot_top_sn);
3103  cairo_line_to(cr, -foot_left, bottom_sn);
3104 
3105  // Draw bottom line.
3106  cairo_close_path(cr);
3107  cairo_path_t* path = cairo_copy_path(cr);
3108 
3109  set_fill_color(cr, priv, white);
3110  cairo_fill(cr);
3111 
3112  // Draw inner horizontal lines.
3113  cairo_move_to(cr, base_left + 0.5 * black_line_width, foot_top_sn);
3114  cairo_line_to(cr, -base_left - 0.5 * black_line_width, foot_top_sn);
3115  cairo_new_sub_path(cr);
3116  cairo_move_to(cr, base_left, base_top_sn);
3117  cairo_line_to(cr, -base_left, base_top_sn);
3118  cairo_new_sub_path(cr);
3119  cairo_move_to(cr, tower_left + (white ? 0.0 : (0.5 * black_line_width)), tower_bottom_sn);
3120  cairo_line_to(cr, -tower_left - (white ? 0.0 : (0.5 * black_line_width)), tower_bottom_sn);
3121  cairo_new_sub_path(cr);
3122  cairo_move_to(cr, tower_left + (white ? 0.0 : (0.5 * black_line_width)), tower_top_sn);
3123  cairo_line_to(cr, -tower_left - (white ? 0.0 : (0.5 * black_line_width)), tower_top_sn);
3124  cairo_new_sub_path(cr);
3125  cairo_move_to(cr, base_left + black_line_width * 0.5, top_bottom_sn);
3126  cairo_line_to(cr, -base_left - black_line_width * 0.5, top_bottom_sn);
3127 
3128  set_line_color(cr, priv, white);
3129  if (white)
3130  cairo_stroke(cr);
3131  else
3132  {
3133  cairo_set_line_width(cr, inner_line_width);
3134  cairo_stroke(cr);
3135  cairo_set_line_width(cr, black_line_width);
3136  }
3137 
3138  cairo_append_path(cr, path);
3139  if (!white)
3140  set_fill_color(cr, priv, white);
3141  cairo_stroke(cr);
3142 
3143  cairo_path_destroy(path);
3144 
3145  cairo_restore(cr);
3146 }
3147 
3148 void cw_chessboard_draw_bishop(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
3149 {
3150  // Measurements, in cm.
3151  static double const width_bishop_cm = CONST(width_bishop_cm, 5.34);
3152  static double const ribbon_width_cm = CONST(ribbon_width_cm, 0.49);
3153  static double const ribbon_bottom_left_cm = CONST(ribbon_bottom_left_cm, 0.72);
3154  static double const ribbon_top_left_cm = CONST(ribbon_top_left_cm, 2.28);
3155  static double const inside_outer_diameter_cm = CONST(inside_outer_diameter_cm, 2.0);
3156  static double const circle_diameter_cm = CONST(circle_diameter_cm, 2.44);
3157  static double const cross_width_cm = CONST(cross_width_cm, 0.93);
3158  static double const ball_outer_diameter_cm = CONST(ball_outer_diameter_cm, 0.81);
3159  static double const ball_inner_diameter_cm = CONST(ball_inner_diameter_cm, 0.41);
3160  static double const circle_start_angle = CONST(circle_start_angle, 0.767);
3161  static double const ribbon_end_angle = CONST(ribbon_end_angle, 1.097);
3162  static double const height_bishop_cm = CONST(height_bishop_cm, 5.44);
3163  static double const ribbon_bottom_y1_cm = CONST(ribbon_bottom_y1_cm, 0.52);
3164  static double const ribbon_bottom_y2_cm = CONST(ribbon_bottom_y2_cm, 0.76);
3165  static double const ribbon_bottom_y3_cm = CONST(ribbon_bottom_y3_cm, 0.55);
3166  static double const ribbon_top_y1_cm = CONST(ribbon_top_y1_cm, 0.99);
3167  static double const ribbon_top_y2_cm = CONST(ribbon_top_y2_cm, 1.25);
3168  static double const ribbon_inside_y_cm = CONST(ribbon_inside_y_cm, 0.93);
3169  static double const inside_bottom_cm = CONST(inside_bottom_cm, 1.34);
3170  static double const inside_top_cm = CONST(inside_top_cm, 1.86);
3171  static double const band_top_bishop_cm = CONST(band_top_bishop_cm, 2.34);
3172  static double const circle_y_cm = CONST(circle_y_cm, 3.11);
3173  static double const cross_y_bishop_cm = CONST(cross_y_bishop_cm, 3.24);
3174  static double const point_y_cm = CONST(point_y_cm, 4.47);
3175  static double const ball_y_cm = CONST(ball_y_cm, 4.675);
3176  static double const sp1_x_cm = CONST(sp1_x_cm, 2.1);
3177  static double const sp1_y_cm = CONST(sp1_y_cm, 3.95);
3178  static double const ribbon_bottom_x1_cm = CONST(ribbon_bottom_x1_cm, 3.34);
3179  static double const ribbon_bottom_x2_cm = CONST(ribbon_bottom_x2_cm, 4.1);
3180  static double const ribbon_top_x1_cm = CONST(ribbon_top_x1_cm, 3.54);
3181  static double const ribbon_top_x2_cm = CONST(ribbon_top_x2_cm, 4.24);
3182 
3183  // Translate to coordinates.
3184  static double const ribbon_width = CONST(ribbon_width, ribbon_width_cm / height_bishop_cm);
3185  static double const ribbon_bottom_left = CONST(ribbon_bottom_left, -0.5 + ribbon_bottom_left_cm / width_bishop_cm);
3186  static double const ribbon_bottom_x1 = CONST(ribbon_bottom_x1, -0.5 + ribbon_bottom_x1_cm / width_bishop_cm);
3187  static double const ribbon_bottom_x2 = CONST(ribbon_bottom_x2, -0.5 + ribbon_bottom_x2_cm / width_bishop_cm);
3188  static double const ribbon_top_x1 = CONST(ribbon_top_x1, -0.5 + ribbon_top_x1_cm / width_bishop_cm);
3189  static double const ribbon_top_x2 = CONST(ribbon_top_x2, -0.5 + ribbon_top_x2_cm / width_bishop_cm);
3190  static double const ribbon_top_left = CONST(ribbon_top_left, -0.5 + ribbon_top_left_cm / width_bishop_cm);
3191  static double const inside_radius_bishop = CONST(inside_radius_bishop, 0.5 * (inside_outer_diameter_cm / width_bishop_cm - black_line_width));
3192  static double const circle_radius = CONST(circle_radius, 0.5 * circle_diameter_cm / width_bishop_cm);
3193  static double const cross_leg = CONST(cross_leg, 0.5 * cross_width_cm / width_bishop_cm);
3194  static double const ball_radius_bishop = CONST(ball_radius_bishop, 0.25 * (ball_outer_diameter_cm + ball_inner_diameter_cm) / width_bishop_cm);
3195  static double const ball_line_width = CONST(ball_line_width, black_line_width); // 0.5 * (ball_outer_diameter_cm - ball_inner_diameter_cm) / width_bishop_cm
3196  static double const ribbon_bottom_y1 = CONST(ribbon_bottom_y1, 0.5 - ribbon_bottom_y1_cm / height_bishop_cm - 0.5 * black_line_width);
3197  static double const ribbon_bottom_y2 = CONST(ribbon_bottom_y2, 0.5 - ribbon_bottom_y2_cm / height_bishop_cm + 0.5 * black_line_width);
3198  static double const ribbon_bottom_y3 = CONST(ribbon_bottom_y3, 0.5 - ribbon_bottom_y3_cm / height_bishop_cm);
3199  static double const ribbon_inside_y = CONST(ribbon_inside_y, 0.5 - ribbon_inside_y_cm / height_bishop_cm);
3200  static double const ribbon_top_y1 = CONST(ribbon_top_y1, 0.5 - ribbon_top_y1_cm / height_bishop_cm - 0.5 * black_line_width);
3201  static double const ribbon_top_y2 = CONST(ribbon_top_y2, 0.5 - ribbon_top_y2_cm / height_bishop_cm + 0.5 * black_line_width);
3202  static double const inside_scale_bishop = CONST(inside_scale_bishop, ((inside_top_cm - inside_bottom_cm) / height_bishop_cm - black_line_width) / (2 * inside_radius_bishop));
3203  static double const inside_y_bishop = CONST(inside_y_bishop, 0.5 - 0.5 * (inside_top_cm + inside_bottom_cm) / height_bishop_cm);
3204  static double const inside_bottom = CONST(inside_bottom, 0.5 - inside_bottom_cm / height_bishop_cm - 0.5 * black_line_width);
3205  static double const band_top_bishop = CONST(band_top_bishop, 0.5 - band_top_bishop_cm / height_bishop_cm + 0.5 * black_line_width);
3206  static double const circle_y = CONST(circle_y, 0.5 - circle_y_cm / height_bishop_cm);
3207  static double const cross_y_bishop = CONST(cross_y_bishop, 0.5 - cross_y_bishop_cm / height_bishop_cm);
3208  static double const point_y = CONST(point_y, 0.5 - point_y_cm / height_bishop_cm);
3209  static double const ball_y = CONST(ball_y, 0.5 - ball_y_cm / height_bishop_cm);
3210  static double const inside_angle = CONST(inside_angle, acos(-ribbon_top_left / inside_radius_bishop));
3211  static double const sp1_x = CONST(sp1_x, -0.5 + sp1_x_cm / width_bishop_cm);
3212  static double const sp1_y = CONST(sp1_y, 0.5 - sp1_y_cm / height_bishop_cm);
3213 
3214  // Precalculations for the ribbon.
3215  static double const spline_magic = CONST(spline_magic, 0.551784);
3216  static double const cp2_x = CONST(cp2_x, ribbon_bottom_y1 - ribbon_inside_y);
3217  static double const sp2_x = CONST(sp2_x, spline_magic * cp2_x);
3218  static double const sp2_y = CONST(sp2_y, ribbon_inside_y + spline_magic * (ribbon_bottom_y1 - ribbon_inside_y));
3219  static double const sp3_x = CONST(sp3_x, ribbon_bottom_x1 - spline_magic * (ribbon_bottom_x1 - cp2_x));
3220  static double const sp3_y = CONST(sp3_y, ribbon_bottom_y1);
3221  static double const sp4_x = CONST(sp4_x, ribbon_bottom_x1 + spline_magic * (ribbon_bottom_x2 - ribbon_bottom_x1));
3222  static double const sp4_y = CONST(sp4_y, ribbon_bottom_y1);
3223  static double const sp5_x = CONST(sp5_x, ribbon_bottom_x2 - spline_magic * (ribbon_bottom_x2 - ribbon_bottom_x1));
3224  static double const sp5_y = CONST(sp5_y, ribbon_bottom_y2);
3225  static double const cp6_x = CONST(cp6_x, -ribbon_bottom_left - (ribbon_bottom_y3 - ribbon_bottom_y2) * tan(ribbon_end_angle));
3226  static double const sp6_x = CONST(sp6_x, ribbon_bottom_x2 + spline_magic * (cp6_x - ribbon_bottom_x2));
3227  static double const sp6_y = CONST(sp6_y, ribbon_bottom_y2);
3228  static double const sp7_x = CONST(sp7_x, -ribbon_bottom_left - spline_magic * (-ribbon_bottom_left - cp6_x));
3229  static double const sp7_y = CONST(sp7_y, ribbon_bottom_y3 - spline_magic * (ribbon_bottom_y3 - ribbon_bottom_y2));
3230  static double const ribbon_end_top_x = CONST(ribbon_end_top_x, -ribbon_bottom_left + ribbon_width * cos(ribbon_end_angle));
3231  static double const ribbon_end_top_y = CONST(ribbon_end_top_y, ribbon_bottom_y3 - ribbon_width * sin(ribbon_end_angle));
3232  static double const cp8_x = CONST(cp8_x, ribbon_end_top_x - (ribbon_end_top_y - ribbon_top_y2) * tan(ribbon_end_angle));
3233  static double const sp8_x = CONST(sp8_x, ribbon_end_top_x - spline_magic * (ribbon_end_top_x - cp8_x));
3234  static double const sp8_y = CONST(sp8_y, ribbon_end_top_y - spline_magic * (ribbon_end_top_y - ribbon_top_y2));
3235  static double const sp9_x = CONST(sp9_x, ribbon_top_x2 + spline_magic * (cp8_x - ribbon_top_x2));
3236  static double const sp9_y = CONST(sp9_y, ribbon_top_y2);
3237  static double const sp10_x = CONST(sp10_x, ribbon_top_x2 - spline_magic * (ribbon_top_x2 - ribbon_top_x1));
3238  static double const sp10_y = CONST(sp10_y, ribbon_top_y2);
3239  static double const sp11_x = CONST(sp11_x, ribbon_top_x1 + spline_magic * (ribbon_top_x2 - ribbon_top_x1));
3240  static double const sp11_y = CONST(sp11_y, ribbon_top_y1);
3241  static double const ribbon_top_y3 = CONST(ribbon_top_y3, 0.2695);
3242  static double const sp12_x = CONST(sp12_x, ribbon_top_x1 - spline_magic * (ribbon_top_x1 + ribbon_top_left));
3243  static double const sp12_y = CONST(sp12_y, ribbon_top_y1);
3244  static double const sp13_x = CONST(sp13_x, -ribbon_top_left);
3245  static double const sp13_y = CONST(sp13_y, ribbon_top_y3 + 0.509 * spline_magic * (ribbon_top_y1 - ribbon_top_y3));
3246 
3247  CwChessboardPrivate* priv = chessboard->priv;
3248 
3249  cairo_save(cr);
3250  cairo_translate(cr, x, y);
3251  cairo_scale(cr, scale, scale);
3252  cairo_set_line_width(cr, black_line_width);
3253 
3254  // Draw the ribbon.
3255 
3256  // Part 14.
3257  cairo_move_to(cr, -ribbon_top_x1, ribbon_top_y1);
3258  cairo_curve_to(cr, -sp11_x, sp11_y, -sp10_x, sp10_y, -ribbon_top_x2, ribbon_top_y2);
3259 
3260  // Part 13.
3261  cairo_curve_to(cr, -sp9_x, sp9_y, -sp8_x, sp8_y, -ribbon_end_top_x, ribbon_end_top_y);
3262 
3263  // Part 12.
3264  cairo_line_to(cr, ribbon_bottom_left, ribbon_bottom_y3);
3265 
3266  // Part 11.
3267  cairo_curve_to(cr, -sp7_x, sp7_y, -sp6_x, sp6_y, -ribbon_bottom_x2, ribbon_bottom_y2);
3268 
3269  // Part 10.
3270  cairo_curve_to(cr, -sp5_x, sp5_y, -sp4_x, sp4_y, -ribbon_bottom_x1, ribbon_bottom_y1);
3271 
3272  // Part 9.
3273  cairo_curve_to(cr, -sp3_x, sp3_y, -sp2_x, sp2_y, 0.0, ribbon_inside_y);
3274 
3275  // Part 1.
3276  cairo_curve_to(cr, sp2_x, sp2_y, sp3_x, sp3_y, ribbon_bottom_x1, ribbon_bottom_y1);
3277 
3278  // Part 2.
3279  cairo_curve_to(cr, sp4_x, sp4_y, sp5_x, sp5_y, ribbon_bottom_x2, ribbon_bottom_y2);
3280 
3281  // Part 3.
3282  cairo_curve_to(cr, sp6_x, sp6_y, sp7_x, sp7_y, -ribbon_bottom_left, ribbon_bottom_y3);
3283 
3284  // Part 4.
3285  cairo_line_to(cr, ribbon_end_top_x, ribbon_end_top_y);
3286 
3287  // Part 5.
3288  cairo_curve_to(cr, sp8_x, sp8_y, sp9_x, sp9_y, ribbon_top_x2, ribbon_top_y2);
3289 
3290  // Part 6.
3291  cairo_curve_to(cr, sp10_x, sp10_y, sp11_x, sp11_y, ribbon_top_x1, ribbon_top_y1);
3292 
3293  if (!white)
3294  {
3295  set_fill_color(cr, priv, white);
3296  cairo_fill_preserve(cr);
3297  cairo_stroke(cr);
3298  cairo_move_to(cr, ribbon_top_x1, ribbon_top_y1);
3299  }
3300 
3301  // Part 7.
3302  cairo_curve_to(cr, sp12_x, sp12_y, sp13_x, sp13_y, -ribbon_top_left, ribbon_top_y3);
3303 
3304  // Part 8 and 17.
3305  cairo_save(cr);
3306  cairo_translate(cr, 0.0, inside_y_bishop);
3307  cairo_scale(cr, 1.0, inside_scale_bishop);
3308  cairo_arc(cr, 0.0, 0.0, inside_radius_bishop, inside_angle, M_PI - inside_angle);
3309  cairo_restore(cr);
3310 
3311  // Part 16.
3312  cairo_line_to(cr, ribbon_top_left, ribbon_top_y3);
3313 
3314  // Part 15.
3315  cairo_curve_to(cr, -sp13_x, sp13_y, -sp12_x, sp12_y, -ribbon_top_x1 + 0.01 * black_line_width, ribbon_top_y1);
3316  cairo_close_path(cr);
3317 
3318  if (white)
3319  set_fill_color(cr, priv, white);
3320  else
3321  set_line_color(cr, priv, white);
3322  cairo_fill_preserve(cr);
3323  if (white)
3324  set_line_color(cr, priv, white);
3325  else
3326  set_fill_color(cr, priv, white);
3327  cairo_stroke(cr);
3328 
3329  // Draw vertical line between left and right ribbon.
3330  cairo_move_to(cr, 0.0, inside_bottom);
3331  cairo_line_to(cr, 0.0, ribbon_inside_y);
3332  cairo_stroke(cr);
3333 
3334  // Draw the outline of the bishop.
3335 
3336  cairo_save(cr);
3337  cairo_translate(cr, 0.0, inside_y_bishop);
3338  cairo_scale(cr, 1.0, inside_scale_bishop);
3339  cairo_arc(cr, 0.0, 0.0, inside_radius_bishop, 0.0, -M_PI);
3340  cairo_restore(cr);
3341 
3342  cairo_arc(cr, 0.0, circle_y, circle_radius, -M_PI - circle_start_angle, -M_PI);
3343 
3344  cairo_curve_to(cr, -circle_radius, circle_y - 0.0848, sp1_x - 0.02657, sp1_y + 0.01722, sp1_x, sp1_y);
3345  cairo_curve_to(cr, sp1_x + 0.08845, sp1_y - 0.05733, -0.000333, point_y + 0.000265, 0.0, point_y);
3346  cairo_curve_to(cr, 0.000333, point_y + 0.000265, -sp1_x - 0.08845, sp1_y - 0.05733, -sp1_x, sp1_y);
3347  cairo_curve_to(cr, -sp1_x + 0.02657, sp1_y + 0.01722, circle_radius, circle_y - 0.0848, circle_radius, circle_y);
3348 
3349  cairo_arc(cr, 0.0, circle_y, circle_radius, 0.0, circle_start_angle);
3350 
3351  cairo_close_path(cr);
3352 
3353  if (white)
3354  set_fill_color(cr, priv, white);
3355  cairo_fill_preserve(cr);
3356  if (white)
3357  set_line_color(cr, priv, white);
3358  cairo_stroke(cr);
3359 
3360  // Draw inside lines.
3361  if (!white)
3362  set_line_color(cr, priv, white);
3363  cairo_save(cr);
3364  if (!white)
3365  {
3366  static double const x2_bishop = CONST(x2_bishop, -circle_radius * cos(circle_start_angle));
3367  static double const y2_bishop = CONST(y2_bishop, (circle_y + circle_radius * sin(circle_start_angle)));
3368  cairo_move_to(cr, -inside_radius_bishop, inside_y_bishop);
3369  cairo_line_to(cr, x2_bishop, y2_bishop);
3370  cairo_line_to(cr, -x2_bishop, y2_bishop);
3371  cairo_line_to(cr, inside_radius_bishop, inside_y_bishop);
3372  cairo_close_path(cr);
3373  cairo_clip(cr);
3374  }
3375  cairo_save(cr);
3376  cairo_translate(cr, 0.0, inside_y_bishop);
3377  cairo_scale(cr, 1.0, inside_scale_bishop);
3378  cairo_arc(cr, 0.0, 0.0, inside_radius_bishop, -M_PI, 0.0);
3379  cairo_restore(cr);
3380  if (white)
3381  cairo_stroke(cr);
3382  else
3383  {
3384  cairo_set_line_width(cr, white_line_width);
3385  cairo_stroke(cr);
3386  cairo_set_line_width(cr, black_line_width);
3387  }
3388  cairo_restore(cr);
3389 
3390  // Ok, some real math needed here.
3391  // First, we scale the y-axis - so that the problem changes to a circle instead of an ellipse.
3392  // Also, flip the y-axis, so that the top of the screen becomes the positive y-axis.
3393  // This means that instead of using the normal 'y' values, we now should use y / -inside_scale_bishop.
3394  // Next, calculate the line through (-inside_radius_bishop, inside_y_bishop) and the
3395  // start of the circle with vector parametric formula: V = X0 + U t1,
3396  // where U is the unit vector (u1, u2), t1 is the parameter and X0 = (x0, 0), the
3397  // point where the line crosses the x-axis.
3398  static double const x1 = CONST(x1, -inside_radius_bishop);
3399  static double const y1 = CONST(y1, inside_y_bishop / -inside_scale_bishop);
3400  static double const x2 = CONST(x2, -circle_radius * cos(circle_start_angle));
3401  static double const y2 = CONST(y2, (circle_y + circle_radius * sin(circle_start_angle)) / -inside_scale_bishop);
3402  static double const d = CONST(d, sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)));
3403  static double const u1 = CONST(u1, (x2 - x1) / d);
3404  static double const u2 = CONST(u2, (y2 - y1) / d);
3405  static double const x0 = CONST(x0, x1 + (x2 - x1) * (0 - y1) / (y2 - y1));
3406  // A line through the center of the circle, perpendicular to this line is:
3407  // V = Y0 + W t2, where Y0 = (0, y0) and W = (u2, -u1).
3408  // Those two lines cross therefore where,
3409  // x: x0 + u1 t1 = u2 t2
3410  // y: u2 t1 = y0 - u1 t2
3411  // Since the t2 parameter gives the distance from the center in terms of a unit vector,
3412  // the distance from the center to the crossing point is |t2|, which we find by cancelling t1:
3413  // x0 u2 + u1 (y0 - u1 t2) = u2^2 t2 --> t2 = x0 u2 + u1 y0, since u1^2 + u2^2 = 1.
3414  // In the crossing point, this value is negative (u2 is positive, and the cross point
3415  // lays left of the center). Hence we find that the distance is -t2 =
3416  // - x0 u2 - u1 y0 = band_top_bishop / -inside_scale_bishop - y0.
3417  static double const y0 = CONST(y0, (band_top_bishop / -inside_scale_bishop + x0 * u2) / (1 - u1));
3418  static double const band_radius = CONST(band_radius, band_top_bishop / -inside_scale_bishop - y0);
3419  static double const angle = CONST(angle, atan(u1 / u2));
3420  cairo_save(cr);
3421  cairo_scale(cr, 1.0, -inside_scale_bishop);
3422  if (!white)
3423  {
3424  static double const t2 = CONST(t2, x0 * u2 + u1 * y0);
3425  static double const t1 = CONST(t1, (y0 - u1 * t2) / u2);
3426  static double const x = CONST(x, x0 + u1 * t1);
3427  cairo_move_to(cr, x, y0);
3428  cairo_line_to(cr, x + d * u1, y0 + d * u2);
3429  cairo_line_to(cr, -x - d * u1, y0 + d * u2);
3430  cairo_line_to(cr, -x, y0);
3431  cairo_close_path(cr);
3432  cairo_clip(cr);
3433  }
3434  cairo_arc(cr, 0.0, y0, band_radius, angle, M_PI - angle);
3435  // Reverse the scale before stroking, without restoring the clipping area.
3436  cairo_scale(cr, 1.0, -1.0 / inside_scale_bishop);
3437  if (white)
3438  cairo_stroke(cr);
3439  else
3440  {
3441  cairo_set_line_width(cr, white_line_width);
3442  cairo_stroke(cr);
3443  cairo_set_line_width(cr, black_line_width);
3444  }
3445  cairo_restore(cr);
3446 
3447  // Draw the cross.
3448  cairo_move_to(cr, -cross_leg, cross_y_bishop);
3449  cairo_line_to(cr, cross_leg, cross_y_bishop);
3450  cairo_move_to(cr, 0.0, cross_y_bishop - cross_leg);
3451  cairo_line_to(cr, 0.0, cross_y_bishop + cross_leg);
3452  if (white)
3453  cairo_stroke(cr);
3454  else
3455  {
3456  cairo_set_line_width(cr, white_line_width);
3457  cairo_stroke(cr);
3458  cairo_set_line_width(cr, black_line_width);
3459  }
3460 
3461  if (!white)
3462  {
3463  cairo_move_to(cr, -inside_radius_bishop, inside_y_bishop);
3464  cairo_arc(cr, 0.0, circle_y, circle_radius, -M_PI - circle_start_angle, -M_PI);
3465  cairo_move_to(cr, inside_radius_bishop, inside_y_bishop);
3466  cairo_arc_negative(cr, 0.0, circle_y, circle_radius, circle_start_angle, 0.0);
3467  set_fill_color(cr, priv, white);
3468  cairo_stroke(cr);
3469  }
3470 
3471  // Draw the little ball on the top.
3472  cairo_set_line_width(cr, ball_line_width);
3473  cairo_arc(cr, 0.0, ball_y, ball_radius_bishop, -M_PI, M_PI);
3474  if (white)
3475  set_fill_color(cr, priv, white);
3476  cairo_fill_preserve(cr);
3477  if (white)
3478  set_line_color(cr, priv, white);
3479  cairo_stroke(cr);
3480 
3481  cairo_restore(cr);
3482 }
3483 
3484 void cw_chessboard_draw_knight(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
3485 {
3486  // Measurements.
3487  static double const height_knight_cm = CONST(height_knight_cm, 21.9);
3488  static double const pixels_per_cm = CONST(pixels_per_cm, 32.467);
3489  static double const min_nose_x_px = CONST(min_nose_x_px, 8.0);
3490  static double const right_ear_y_px = CONST(right_ear_y_px, 15.0); // See 'Draw right ear'.
3491  static double const bottom_right_x_px = CONST(bottom_right_x_px, 582.82); // See 'back' curve.
3492  static double const bottom_right_y_px = CONST(bottom_right_y_px, 580.82);
3493  static double const bottom_left_x_px = CONST(bottom_left_x_px, 190.00); // See 'front' curve.
3494  // Derived.
3495  static double const pixel_scale = CONST(pixel_scale, 1.0 / (pixels_per_cm * height_knight_cm));
3496  static double const knight_black_line_width = CONST(knight_black_line_width, 0.95 * black_line_width / pixel_scale);
3497  static double const knight_white_line_width = CONST(knight_white_line_width, 1.3 * knight_black_line_width);
3498  static double const knight_white_glyp_line_width = CONST(knight_white_glyp_line_width, knight_white_line_width - knight_black_line_width);
3499 
3500  // The outline of the knight in coordinates, without translation.
3501  static double const max_y = CONST(max_y, bottom_right_y_px * pixel_scale);
3502  static double const min_y = CONST(min_y, right_ear_y_px * pixel_scale);
3503  static double const max_x = CONST(max_x, bottom_right_x_px * pixel_scale);
3504  static double const min_x = CONST(min_x, min_nose_x_px * pixel_scale);
3505 
3506  // Calculate coordinate offsets, needed to center the knight.
3507  static double const pixel_translate_x = CONST(pixel_translate_x, -(max_x + min_x) / 2);
3508  static double const pixel_translate_y = CONST(pixel_translate_y, -(max_y + min_y) / 2);
3509 
3510  CwChessboardPrivate* priv = chessboard->priv;
3511 
3512  cairo_save(cr);
3513  cairo_translate(cr, x, y);
3514  cairo_scale(cr, scale, scale);
3515 
3516  // At this point, the coordinates run from -0.5 till 0.5.
3517  // However, we use pixels as coordinates (as measured in gimp)
3518  // Translate the image so that the pixel-coordinate center falls on (0, 0).
3519  cairo_translate(cr, pixel_translate_x, pixel_translate_y);
3520  // Scale, so we can use "pixel-coordinates".
3521  cairo_scale(cr, pixel_scale, pixel_scale);
3522 
3523  // Fill body.
3524  cairo_move_to(cr, 319.00, 299.00);
3525  cairo_curve_to(cr, 322.00, 449.00, 165.00, 445.00, 192.00, 570.00);
3526  cairo_curve_to(cr, 192.00, 570.00, 568.50, 571.00, 568.50, 571.00);
3527  cairo_curve_to(cr, 577.00, 426.00, 533.00, 99.00, 340.50, 88.50);
3528  cairo_curve_to(cr, 245.50, 87.50, 206.00, 86.00, 195.00, 102.00);
3529  set_fill_color(cr, priv, white);
3530  cairo_fill(cr);
3531 
3532  // Draw shadow.
3533  cairo_move_to(cr, 315.00, 300.33);
3534  cairo_curve_to(cr, 301.43, 300.80, 291.75, 314.52, 282.00, 325.00);
3535  cairo_curve_to(cr, 298.67, 317.33, 316.33, 325.00, 317.33, 344.33);
3536  cairo_curve_to(cr, 321.33, 337.33, 326.00, 326.00, 315.00, 300.33);
3537  if (white)
3538  set_line_color(cr, priv, white);
3539  cairo_fill(cr);
3540 
3541  // Draw back.
3542  // Change the thickness of the top of the back to a line width:
3543  static double const back_top_offset = CONST(back_top_offset, (93.00 - knight_black_line_width) - 82.00);
3544  cairo_move_to(cr, 582.82, 580.82);
3545  cairo_curve_to(cr, 589.00, 359.00, 530.00,85.00, 332.00, 82.00 + back_top_offset);
3546  cairo_curve_to(cr, 320.87, 82.04 + back_top_offset, 314.25, 82.12 + back_top_offset, 302.50, 82.38 + back_top_offset);
3547  cairo_curve_to(cr, 302.75, 95.38, 296.22, 93.73, 319.50, 94.00);
3548  cairo_curve_to(cr, 510.50, 93.00, 556.12, 359.00, 556.12, 563.00);
3549  cairo_close_path(cr);
3550  cairo_fill(cr);
3551 
3552  // Draw front.
3553  cairo_move_to(cr, 190.00, 570.00);
3554  cairo_curve_to(cr, 190.00, 550.75, 190.00, 549.00, 190.00, 540.00);
3555  cairo_curve_to(cr, 190.00, 493.25, 210.50, 482.50, 285.00, 409.50);
3556  cairo_curve_to(cr, 298.25, 391.75, 313.00, 357.50, 317.75, 344.75);
3557  cairo_curve_to(cr, 320.25, 340.00, 320.25, 330.00, 320.00, 280.00);
3558  cairo_set_line_width(cr, knight_black_line_width);
3559  cairo_stroke(cr);
3560 
3561  // Draw head.
3562  cairo_move_to(cr, 144.00, 31.50);
3563  cairo_curve_to(cr, 122.50, 67.00, 147.50, 57.50, 146.00, 105.00);
3564  cairo_curve_to(cr, 112.00, 125.50, 123.00, 140.50, 102.50, 170.00);
3565  cairo_curve_to(cr, 84.00, 199.50, 128.00, 181.50, 33.50, 313.50);
3566  cairo_curve_to(cr, -23.00, 414.00, 81.50, 468.00, 130.00, 447.50);
3567  cairo_curve_to(cr, 182.50, 398.00, 142.50, 427.00, 179.50, 390.00);
3568  cairo_curve_to(cr, 194.50, 376.50, 212.50, 349.50, 237.50, 347.00);
3569  cairo_curve_to(cr, 268.00, 344.00, 283.50, 323.50, 306.00, 301.00);
3570  cairo_curve_to(cr, 327.50, 276.50, 330.00, 264.50, 330.00, 228.50);
3571  if (white)
3572  set_fill_color(cr, priv, white);
3573  cairo_fill_preserve(cr);
3574  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
3575  if (white)
3576  set_line_color(cr, priv, white);
3577  cairo_stroke(cr);
3578  cairo_move_to(cr, 201.00, 94.50);
3579  cairo_curve_to(cr, 184.50, 54.50, 152.00, 43.50, 144.00, 31.50);
3580  cairo_stroke(cr);
3581 
3582  // Draw between ears.
3583  cairo_move_to(cr, 170.50, 136.50);
3584  cairo_curve_to(cr, 170.00, 129.50, 175.50, 125.00, 183.50, 116.00);
3585  cairo_curve_to(cr, 204.50, 91.00, 216.00, 94.00, 238.00, 91.00);
3586  cairo_stroke(cr);
3587 
3588  if (!white)
3589  {
3590  // Draw white hair.
3591  cairo_move_to(cr, 529.00, 570.00);
3592  cairo_curve_to(cr, 530.50, 352.00, 476.50, 128.50, 334.00, 121.00);
3593  cairo_curve_to(cr, 310.50, 118.50, 310.00, 117.50, 296.50, 117.50);
3594  cairo_curve_to(cr, 291.50, 100.00, 252.50, 95.50, 242.20, 119.35);
3595  cairo_curve_to(cr, 227.55, 120.95, 212.22, 124.23, 198.50, 130.50);
3596  cairo_curve_to(cr, 178.00, 137.50, 158.50, 147.50, 154.00, 137.00);
3597  cairo_curve_to(cr, 149.50, 127.00, 145.50, 121.00, 204.00, 100.00);
3598  cairo_curve_to(cr, 226.50, 90.00, 276.50, 92.00, 319.50, 94.00);
3599  cairo_curve_to(cr, 510.50, 93.00, 556.00, 354.00, 556.00, 570.00);
3600  cairo_curve_to(cr, 548.06, 571.00, 537.73, 569.45, 529.00, 570.00);
3601  set_line_color(cr, priv, white);
3602  cairo_fill(cr);
3603  }
3604 
3605  // Draw bottom.
3606  double dummy = bottom_left_x_px;
3607  double bottom_right_y_px_sn = bottom_right_y_px;
3608  if (scale >= 27)
3609  {
3610  // Snap bottom to grid.
3611  cairo_user_to_device(cr,& dummy,& bottom_right_y_px_sn);
3612  bottom_right_y_px_sn = round(bottom_right_y_px_sn);
3613  cairo_device_to_user(cr,& dummy,& bottom_right_y_px_sn);
3614  }
3615  cairo_rectangle(cr, bottom_left_x_px - 0.5 * knight_black_line_width,
3616  bottom_right_y_px_sn - knight_black_line_width,
3617  bottom_right_x_px - (bottom_left_x_px - 0.5 * knight_black_line_width),
3618  knight_black_line_width);
3619  if (!white)
3620  set_fill_color(cr, priv, white);
3621  cairo_fill(cr);
3622 
3623  // Draw mouth.
3624  cairo_move_to(cr, 113.67, 389.33);
3625  cairo_curve_to(cr, 121.00, 388.00, 129.33, 406.67, 120.67, 414.67);
3626  cairo_curve_to(cr, 114.33, 419.33, 104.33, 431.00, 112.67, 444.00);
3627  //cairo_curve_to(cr, 133.53, 466.89, 104.22, 478.73, 93.00, 446.67);
3628  cairo_line_to(cr, 93.00, 446.67);
3629  cairo_curve_to(cr, 89.00, 418.67, 94.67, 417.33, 100.00, 412.67);
3630  cairo_curve_to(cr, 112.67, 402.00, 100.67, 394.67, 113.67, 389.33);
3631  if (!white)
3632  set_line_color(cr, priv, white);
3633  if (white)
3634  cairo_fill(cr);
3635  else
3636  {
3637  cairo_fill_preserve(cr);
3638  cairo_set_line_width(cr, knight_white_glyp_line_width);
3639  cairo_stroke(cr);
3640  cairo_set_line_width(cr, knight_black_line_width);
3641  }
3642 
3643  // Redraw a part of the head.
3644  cairo_move_to(cr, 33.50, 313.50);
3645  cairo_curve_to(cr, -23.00, 414.00, 81.50, 468.00, 130.00, 447.50);
3646  if (!white)
3647  set_fill_color(cr, priv, white);
3648  cairo_stroke(cr);
3649 
3650  if (!white)
3651  {
3652  // Draw jaw.
3653  cairo_move_to(cr, 312.32, 293.46);
3654  cairo_curve_to(cr, 328.01, 273.63, 330.00, 260.62, 330.00, 228.50);
3655  cairo_set_line_width(cr, knight_white_line_width);
3656  set_line_color(cr, priv, white);
3657  cairo_stroke(cr);
3658  cairo_set_line_width(cr, knight_black_line_width);
3659  }
3660 
3661  // Draw right ear.
3662  for (int stroke = 0; stroke < 2; ++stroke)
3663  {
3664  cairo_move_to(cr, 242.00, 114.00);
3665  cairo_curve_to(cr, 235.00, 76.00, 235.50, 92.50, 267.00, 15.00); // 15.00 corresponds to min_y
3666  if (stroke)
3667  cairo_move_to(cr, 267.00, 15.00);
3668  cairo_curve_to(cr, 309.50, 85.50, 312.00, 88.00, 295.00, 117.00);
3669  if (stroke)
3670  {
3671  if (white)
3672  set_line_color(cr, priv, white);
3673  cairo_stroke(cr);
3674  }
3675  else
3676  {
3677  if (white)
3678  set_fill_color(cr, priv, white);
3679  else
3680  set_fill_color(cr, priv, white);
3681  cairo_fill(cr);
3682  }
3683  }
3684 
3685  if (!white)
3686  set_line_color(cr, priv, white);
3687 
3688  // Draw nose.
3689  cairo_move_to(cr, 76.00, 363.00);
3690  cairo_curve_to(cr, 66.00, 372.33, 78.33, 379.00, 66.00, 384.00);
3691  cairo_curve_to(cr, 21.00, 399.00, 61.67, 331.00, 79.67, 341.67);
3692  cairo_curve_to(cr, 81.00, 342.00, 84.67, 353.33, 76.00, 363.00);
3693  if (white)
3694  cairo_fill(cr);
3695  else
3696  {
3697  cairo_fill_preserve(cr);
3698  cairo_set_line_width(cr, knight_white_glyp_line_width);
3699  cairo_stroke(cr);
3700  cairo_set_line_width(cr, knight_black_line_width);
3701  }
3702 
3703  // Draw eye.
3704  cairo_move_to(cr, 173.33, 208.00);
3705  cairo_curve_to(cr, 180.67, 207.00, 182.00, 197.67, 182.00, 197.67);
3706  cairo_curve_to(cr, 184.59, 176.98, 182.28, 177.30, 190.67, 173.00);
3707  cairo_curve_to(cr, 201.00, 169.33, 198.33, 146.00, 173.33, 161.67);
3708  cairo_curve_to(cr, 146.00, 181.33, 130.67, 192.00, 128.33, 202.67);
3709  cairo_curve_to(cr, 124.00, 233.33, 131.00, 227.33, 144.67, 207.00);
3710  cairo_curve_to(cr, 150.67, 201.00, 158.67, 193.67, 162.33, 203.33);
3711  cairo_curve_to(cr, 164.67, 206.00, 165.63, 209.29, 173.33, 208.00);
3712  if (white)
3713  cairo_fill(cr);
3714  else
3715  {
3716  cairo_fill_preserve(cr);
3717  cairo_set_line_width(cr, knight_white_glyp_line_width);
3718  cairo_stroke(cr);
3719  }
3720 
3721  cairo_restore(cr);
3722 }
3723 
3724 //-----------------------------------------------------------------------------
3725 // Mouse events.
3726 
3727 static gboolean cw_chessboard_motion_notify(GtkWidget* widget, GdkEventMotion* event)
3728 {
3729  Dout(dc::motion_event, "Calling cw_chessboard_motion_notify(" << widget << ", " << event << ")");
3730  update_cursor_position(CW_CHESSBOARD(widget), event->x, event->y, FALSE);
3731  // Always look for another handler.
3732  return FALSE;
3733 }
3734 
3735 static gboolean cw_chessboard_default_motion_notify(GtkWidget* widget, GdkEventMotion* event)
3736 {
3737  Dout(dc::motion_event, "Calling cw_chessboard_default_motion_notify(" << widget << ", " << event << ")");
3738 
3739  CwChessboard* chessboard = CW_CHESSBOARD(widget);
3740  CwChessboardPrivate* priv = chessboard->priv;
3741 
3742  if (priv->floating_piece_handle != -1)
3743  {
3744  double hsside = 0.5 * chessboard->sside;
3745  double fraction = hsside - (gint)hsside;
3746  cw_chessboard_move_floating_piece(chessboard, priv->floating_piece_handle, event->x - fraction, event->y - fraction);
3747  return TRUE;
3748  }
3749 
3750  return FALSE;
3751 }
3752 
void cw_chessboard_set_square(CwChessboard* chessboard, gint col, gint row, CwChessboardCode code)
CwChessboardCode const white_bishop
void cw_chessboard_set_cursor_thickness(CwChessboard* chessboard, gdouble thickness)
CwChessboardCode const empty_square
gint const sside
Square side in pixels (read only).
Definition: CwChessboard.h:110
void cw_chessboard_get_border_color(CwChessboard* chessboard, GdkColor* color)
uint16_t CwChessboardCode
A code to specify a chess piece.
gboolean cw_chessboard_get_active_turn_indicator(CwChessboard* chessboard)
void cw_chessboard_free_color_handle(CwChessboard* chessboard, CwChessboardColorHandle handle)
void cw_chessboard_draw_knight(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
uint8_t const row_mask
A mask for the bits used for the row in IndexData.
Definition: Index.h:50
ColorData const white
A constant representing the color white.
Definition: Color.h:55
CWCHESSBOARD_INLINE gint cw_chessboard_x2col(CwChessboard* chessboard, gdouble x)
void cw_chessboard_set_white_line_color(CwChessboard* chessboard, GdkColor const* color)
CwChessboardCode const black_queen
void cw_chessboard_draw_bishop(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
void(* draw_piece[6])(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble sside, gboolean white)
Definition: CwChessboard.h:201
void cw_chessboard_draw_pawn(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
void cw_chessboard_set_background_colors(CwChessboard* chessboard, CwChessboardColorHandle const* handles)
CWCHESSBOARD_INLINE gint cw_chessboard_y2row(CwChessboard* chessboard, gdouble y)
CwChessboardCode const white_pawn
gboolean cw_chessboard_get_draw_border(CwChessboard* chessboard)
void cw_chessboard_get_white_line_color(CwChessboard* chessboard, GdkColor* color)
void cw_chessboard_default_draw_turn_indicator(CwChessboard* chessboard, gboolean white, gboolean on)
void(* draw_hud_layer)(CwChessboard* chessboard, cairo_t* cr, gint sside, guint hud)
Definition: CwChessboard.h:223
This file contains the declaration of the GTK+ widget CwChessboard.
CwChessboardColorHandle cw_chessboard_allocate_color_handle_rgb(CwChessboard* chessboard, gdouble red, gdouble green, gdouble blue)
void cw_chessboard_draw_king(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
A GTK+ chessboard widget.
Definition: CwChessboard.h:101
CwChessboardCode const white_queen
CwChessboardCode cw_chessboard_get_floating_piece(CwChessboard* chessboard, gint handle)
void cw_chessboard_default_draw_hud_layer(CwChessboard* chessboard, cairo_t* cr, gint sside, guint hud)
void cw_chessboard_set_marker_level(CwChessboard* chessboard, gboolean below)
void(* cursor_left_chessboard)(CwChessboard* chessboard, gint prev_col, gint prev_row)
Definition: CwChessboard.h:253
void cw_chessboard_set_marker_thickness(CwChessboard* chessboard, gdouble thickness)
void cw_chessboard_default_draw_border(CwChessboard* chessboard)
gboolean cw_chessboard_get_draw_turn_indicators(CwChessboard* chessboard)
CwChessboardCode cw_chessboard_get_square(CwChessboard* chessboard, gint col, gint row)
void(* draw_turn_indicator)(CwChessboard* chessboard, gboolean white, gboolean on)
Definition: CwChessboard.h:182
CwChessboardCode const black_pawn
CwChessboardCode const white_rook
gdouble cw_chessboard_get_cursor_thickness(CwChessboard* chessboard)
gint cw_chessboard_default_calc_board_border_width(CwChessboard const* chessboard, gint sside)
void cw_chessboard_draw_queen(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
void cw_chessboard_draw_rook(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
void cw_chessboard_hide_cursor(CwChessboard* chessboard)
CwChessboardCode const black_bishop
void cw_chessboard_set_flip_board(CwChessboard* chessboard, gboolean flip)
void cw_chessboard_get_black_line_color(CwChessboard* chessboard, GdkColor* color)
unsigned char CwChessboardColorHandle
A color handle used for background markers.
Definition: CwChessboard.h:79
CwChessboardCode const black_knight
void cw_chessboard_get_cursor_color(CwChessboard* chessboard, GdkColor* color)
CWCHESSBOARD_INLINE void cw_chessboard_colrow2xy(CwChessboard* chessboard, gint col, gint row, gint* x, gint* y)
The Class structure of CwChessboard.
Definition: CwChessboard.h:125
void cw_chessboard_disable_hud_layer(CwChessboard* chessboard, guint hud)
gboolean(* draw_hud_square)(CwChessboard* chessboard, cairo_t* cr, gint col, gint row, gint sside, guint hud)
Definition: CwChessboard.h:239
void cw_chessboard_set_draw_border(CwChessboard* chessboard, gboolean draw)
gpointer cw_chessboard_add_arrow(CwChessboard* chessboard, gint begin_col, gint begin_row, gint end_col, gint end_row, GdkColor const* color)
void cw_chessboard_get_light_square_color(CwChessboard* chessboard, GdkColor* color)
CwChessboardColorHandle cw_chessboard_get_marker_color(CwChessboard* chessboard, gint col, gint row)
gint const top_left_a1_y
The y coordinate of the top-left pixel of square a1 (read-only). Despite the name, if the board is flipped then it&#39;s square h8.
Definition: CwChessboard.h:114
gint(* calc_board_border_width)(CwChessboard const* chessboard, gint sside)
Definition: CwChessboard.h:151
void cw_chessboard_set_black_line_color(CwChessboard* chessboard, GdkColor const* color)
void(* draw_border)(CwChessboard* chessboard)
Definition: CwChessboard.h:166
void cw_chessboard_set_white_fill_color(CwChessboard* chessboard, GdkColor const* color)
CwChessboardCode const white_knight
gint cw_chessboard_add_floating_piece(CwChessboard* chessboard, CwChessboardCode code, gdouble x, gdouble y, gboolean pointer_device)
void cw_chessboard_get_dark_square_color(CwChessboard* chessboard, GdkColor* color)
void cw_chessboard_set_border_color(CwChessboard* chessboard, GdkColor const* color)
void cw_chessboard_set_draw_turn_indicators(CwChessboard* chessboard, gboolean draw)
void cw_chessboard_set_dark_square_color(CwChessboard* chessboard, GdkColor const* color)
void cw_chessboard_set_cursor_color(CwChessboard* chessboard, GdkColor const* color)
CwChessboardCode const white_king
void cw_chessboard_set_black_fill_color(CwChessboard* chessboard, GdkColor const* color)
gboolean cw_chessboard_default_draw_hud_square(CwChessboard* chessboard, cairo_t* cr, gint col, gint row, gint sside, guint hud)
void(* cursor_entered_square)(CwChessboard* chessboard, gint prev_col, gint prev_row, gint col, gint row)
Definition: CwChessboard.h:269
void cw_chessboard_get_white_fill_color(CwChessboard* chessboard, GdkColor* color)
void cw_chessboard_set_marker_color(CwChessboard* chessboard, gint col, gint row, CwChessboardColorHandle mahandle)
void cw_chessboard_move_floating_piece(CwChessboard* chessboard, gint handle, gdouble x, gdouble y)
GtkWidget * cw_chessboard_new(void)
void cw_chessboard_enable_hud_layer(CwChessboard* chessboard, guint hud)
void cw_chessboard_set_active_turn_indicator(CwChessboard* chessboard, gboolean white)
gboolean cw_chessboard_get_flip_board(CwChessboard* chessboard)
void cw_chessboard_show_cursor(CwChessboard* chessboard)
gdouble cw_chessboard_get_marker_thickness(CwChessboard* chessboard)
void cw_chessboard_remove_arrow(CwChessboard* chessboard, gpointer ptr)
CwChessboardColorHandle cw_chessboard_get_background_color(CwChessboard* chessboard, gint col, gint row)
This file contains the definitions of the CwChessboardCode constants.
gint const top_left_a1_x
The x coordinate of the top-left pixel of square a1 (read-only). Despite the name, if the board is flipped then it&#39;s square h8.
Definition: CwChessboard.h:112
gboolean const flip_board
TRUE if the board is flipped (read-only).
Definition: CwChessboard.h:116
void cw_chessboard_get_background_colors(CwChessboard* chessboard, CwChessboardColorHandle* handles)
void cw_chessboard_set_background_color(CwChessboard* chessboard, gint col, gint row, CwChessboardColorHandle bghandle)
uint8_t const col_mask
A mask for the bits used for the column in IndexData.
Definition: Index.h:49
void cw_chessboard_set_light_square_color(CwChessboard* chessboard, GdkColor const* color)
CwChessboardCode const black_rook
void cw_chessboard_remove_floating_piece(CwChessboard* chessboard, gint handle)
void cw_chessboard_get_black_fill_color(CwChessboard* chessboard, GdkColor* color)
CwChessboardCode const black_king

Copyright © 2006 - 2010 Carlo Wood.  All rights reserved.