d790116d632f1464b7ef0845ded7fa44e8d75f7e
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P(());\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
130 \r
131 typedef struct {\r
132   POINT sq[2];    /* board coordinates of from, to squares */\r
133 } HighlightInfo;\r
134 \r
135 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 \r
138 typedef struct { // [HGM] atomic\r
139   int fromX, fromY, toX, toY, radius;\r
140 } ExplodeInfo;\r
141 \r
142 static ExplodeInfo explodeInfo;\r
143 \r
144 /* Window class names */\r
145 char szAppName[] = "WinBoard";\r
146 char szConsoleName[] = "WBConsole";\r
147 \r
148 /* Title bar text */\r
149 char szTitle[] = "WinBoard";\r
150 char szConsoleTitle[] = "I C S Interaction";\r
151 \r
152 char *programName;\r
153 char *settingsFileName;\r
154 Boolean saveSettingsOnExit;\r
155 char installDir[MSG_SIZ];\r
156 int errorExitStatus;\r
157 \r
158 BoardSize boardSize;\r
159 Boolean chessProgram;\r
160 //static int boardX, boardY;\r
161 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
162 static int squareSize, lineGap, minorSize;\r
163 static int winW, winH;\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
165 static int logoHeight = 0;\r
166 static char messageText[MESSAGE_TEXT_MAX];\r
167 static int clockTimerEvent = 0;\r
168 static int loadGameTimerEvent = 0;\r
169 static int analysisTimerEvent = 0;\r
170 static DelayedEventCallback delayedTimerCallback;\r
171 static int delayedTimerEvent = 0;\r
172 static int buttonCount = 2;\r
173 char *icsTextMenuString;\r
174 char *icsNames;\r
175 char *firstChessProgramNames;\r
176 char *secondChessProgramNames;\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 Boolean alwaysOnTop = FALSE;\r
182 RECT boardRect;\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
184   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
185 HPALETTE hPal;\r
186 ColorClass currentColorClass;\r
187 \r
188 HWND hCommPort = NULL;    /* currently open comm port */\r
189 static HWND hwndPause;    /* pause button */\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,\r
192   blackSquareBrush, /* [HGM] for band between board and holdings */\r
193   explodeBrush,     /* [HGM] atomic */\r
194   markerBrush,      /* [HGM] markers */\r
195   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
198 static HPEN gridPen = NULL;\r
199 static HPEN highlightPen = NULL;\r
200 static HPEN premovePen = NULL;\r
201 static NPLOGPALETTE pLogPal;\r
202 static BOOL paletteChanged = FALSE;\r
203 static HICON iconWhite, iconBlack, iconCurrent;\r
204 static int doingSizing = FALSE;\r
205 static int lastSizing = 0;\r
206 static int prevStderrPort;\r
207 static HBITMAP userLogo;\r
208 \r
209 static HBITMAP liteBackTexture = NULL;\r
210 static HBITMAP darkBackTexture = NULL;\r
211 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
212 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int backTextureSquareSize = 0;\r
214 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
215 \r
216 #if __GNUC__ && !defined(_winmajor)\r
217 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
218 #else\r
219 #if defined(_winmajor)\r
220 #define oldDialog (_winmajor < 4)\r
221 #else\r
222 #define oldDialog 0\r
223 #endif\r
224 #endif\r
225 \r
226 typedef struct {\r
227   char *name;\r
228   int squareSize;\r
229   int lineGap;\r
230   int smallLayout;\r
231   int tinyLayout;\r
232   int cliWidth, cliHeight;\r
233 } SizeInfo;\r
234 \r
235 SizeInfo sizeInfo[] = \r
236 {\r
237   { "tiny",     21, 0, 1, 1, 0, 0 },\r
238   { "teeny",    25, 1, 1, 1, 0, 0 },\r
239   { "dinky",    29, 1, 1, 1, 0, 0 },\r
240   { "petite",   33, 1, 1, 1, 0, 0 },\r
241   { "slim",     37, 2, 1, 0, 0, 0 },\r
242   { "small",    40, 2, 1, 0, 0, 0 },\r
243   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
244   { "middling", 49, 2, 0, 0, 0, 0 },\r
245   { "average",  54, 2, 0, 0, 0, 0 },\r
246   { "moderate", 58, 3, 0, 0, 0, 0 },\r
247   { "medium",   64, 3, 0, 0, 0, 0 },\r
248   { "bulky",    72, 3, 0, 0, 0, 0 },\r
249   { "large",    80, 3, 0, 0, 0, 0 },\r
250   { "big",      87, 3, 0, 0, 0, 0 },\r
251   { "huge",     95, 3, 0, 0, 0, 0 },\r
252   { "giant",    108, 3, 0, 0, 0, 0 },\r
253   { "colossal", 116, 4, 0, 0, 0, 0 },\r
254   { "titanic",  129, 4, 0, 0, 0, 0 },\r
255   { NULL, 0, 0, 0, 0, 0, 0 }\r
256 };\r
257 \r
258 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
259 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
260 {\r
261   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
262   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
263   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
264   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
265   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
266   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
267   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
268   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
269   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
270   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
279 };\r
280 \r
281 MyFont *font[NUM_SIZES][NUM_FONTS];\r
282 \r
283 typedef struct {\r
284   char *label;\r
285   int id;\r
286   HWND hwnd;\r
287   WNDPROC wndproc;\r
288 } MyButtonDesc;\r
289 \r
290 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
291 #define N_BUTTONS 5\r
292 \r
293 MyButtonDesc buttonDesc[N_BUTTONS] =\r
294 {\r
295   {"<<", IDM_ToStart, NULL, NULL},\r
296   {"<", IDM_Backward, NULL, NULL},\r
297   {"P", IDM_Pause, NULL, NULL},\r
298   {">", IDM_Forward, NULL, NULL},\r
299   {">>", IDM_ToEnd, NULL, NULL},\r
300 };\r
301 \r
302 int tinyLayout = 0, smallLayout = 0;\r
303 #define MENU_BAR_ITEMS 7\r
304 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
305   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
306   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
307 };\r
308 \r
309 \r
310 MySound sounds[(int)NSoundClasses];\r
311 MyTextAttribs textAttribs[(int)NColorClasses];\r
312 \r
313 MyColorizeAttribs colorizeAttribs[] = {\r
314   { (COLORREF)0, 0, "Shout Text" },\r
315   { (COLORREF)0, 0, "SShout/CShout" },\r
316   { (COLORREF)0, 0, "Channel 1 Text" },\r
317   { (COLORREF)0, 0, "Channel Text" },\r
318   { (COLORREF)0, 0, "Kibitz Text" },\r
319   { (COLORREF)0, 0, "Tell Text" },\r
320   { (COLORREF)0, 0, "Challenge Text" },\r
321   { (COLORREF)0, 0, "Request Text" },\r
322   { (COLORREF)0, 0, "Seek Text" },\r
323   { (COLORREF)0, 0, "Normal Text" },\r
324   { (COLORREF)0, 0, "None" }\r
325 };\r
326 \r
327 \r
328 \r
329 static char *commentTitle;\r
330 static char *commentText;\r
331 static int commentIndex;\r
332 static Boolean editComment = FALSE;\r
333 \r
334 \r
335 char errorTitle[MSG_SIZ];\r
336 char errorMessage[2*MSG_SIZ];\r
337 HWND errorDialog = NULL;\r
338 BOOLEAN moveErrorMessageUp = FALSE;\r
339 BOOLEAN consoleEcho = TRUE;\r
340 CHARFORMAT consoleCF;\r
341 COLORREF consoleBackgroundColor;\r
342 \r
343 char *programVersion;\r
344 \r
345 #define CPReal 1\r
346 #define CPComm 2\r
347 #define CPSock 3\r
348 #define CPRcmd 4\r
349 typedef int CPKind;\r
350 \r
351 typedef struct {\r
352   CPKind kind;\r
353   HANDLE hProcess;\r
354   DWORD pid;\r
355   HANDLE hTo;\r
356   HANDLE hFrom;\r
357   SOCKET sock;\r
358   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
359 } ChildProc;\r
360 \r
361 #define INPUT_SOURCE_BUF_SIZE 4096\r
362 \r
363 typedef struct _InputSource {\r
364   CPKind kind;\r
365   HANDLE hFile;\r
366   SOCKET sock;\r
367   int lineByLine;\r
368   HANDLE hThread;\r
369   DWORD id;\r
370   char buf[INPUT_SOURCE_BUF_SIZE];\r
371   char *next;\r
372   DWORD count;\r
373   int error;\r
374   InputCallback func;\r
375   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
376   VOIDSTAR closure;\r
377 } InputSource;\r
378 \r
379 InputSource *consoleInputSource;\r
380 \r
381 DCB dcb;\r
382 \r
383 /* forward */\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
385 VOID ConsoleCreate();\r
386 LRESULT CALLBACK\r
387   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);\r
391 LRESULT CALLBACK\r
392   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
394 void ParseIcsTextMenu(char *icsTextMenuString);\r
395 VOID PopUpMoveDialog(char firstchar);\r
396 VOID PopUpNameDialog(char firstchar);\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
398 \r
399 /* [AS] */\r
400 int NewGameFRC();\r
401 int GameListOptions();\r
402 \r
403 int dummy; // [HGM] for obsolete args\r
404 \r
405 HWND hwndMain = NULL;        /* root window*/\r
406 HWND hwndConsole = NULL;\r
407 HWND commentDialog = NULL;\r
408 HWND moveHistoryDialog = NULL;\r
409 HWND evalGraphDialog = NULL;\r
410 HWND engineOutputDialog = NULL;\r
411 HWND gameListDialog = NULL;\r
412 HWND editTagsDialog = NULL;\r
413 \r
414 int commentUp = FALSE;\r
415 \r
416 WindowPlacement wpMain;\r
417 WindowPlacement wpConsole;\r
418 WindowPlacement wpComment;\r
419 WindowPlacement wpMoveHistory;\r
420 WindowPlacement wpEvalGraph;\r
421 WindowPlacement wpEngineOutput;\r
422 WindowPlacement wpGameList;\r
423 WindowPlacement wpTags;\r
424 \r
425 VOID EngineOptionsPopup(); // [HGM] settings\r
426 \r
427 VOID GothicPopUp(char *title, VariantClass variant);\r
428 /*\r
429  * Setting "frozen" should disable all user input other than deleting\r
430  * the window.  We do this while engines are initializing themselves.\r
431  */\r
432 static int frozen = 0;\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];\r
434 void FreezeUI()\r
435 {\r
436   HMENU hmenu;\r
437   int i;\r
438 \r
439   if (frozen) return;\r
440   frozen = 1;\r
441   hmenu = GetMenu(hwndMain);\r
442   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
443     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
444   }\r
445   DrawMenuBar(hwndMain);\r
446 }\r
447 \r
448 /* Undo a FreezeUI */\r
449 void ThawUI()\r
450 {\r
451   HMENU hmenu;\r
452   int i;\r
453 \r
454   if (!frozen) return;\r
455   frozen = 0;\r
456   hmenu = GetMenu(hwndMain);\r
457   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
458     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
459   }\r
460   DrawMenuBar(hwndMain);\r
461 }\r
462 \r
463 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
464 \r
465 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
466 #ifdef JAWS\r
467 #include "jaws.c"\r
468 #else\r
469 #define JAWS_INIT\r
470 #define JAWS_ARGS\r
471 #define JAWS_ALT_INTERCEPT\r
472 #define JAWS_KB_NAVIGATION\r
473 #define JAWS_MENU_ITEMS\r
474 #define JAWS_SILENCE\r
475 #define JAWS_REPLAY\r
476 #define JAWS_ACCEL\r
477 #define JAWS_COPYRIGHT\r
478 #define JAWS_DELETE(X) X\r
479 #define SAYMACHINEMOVE()\r
480 #define SAY(X)\r
481 #endif\r
482 \r
483 /*---------------------------------------------------------------------------*\\r
484  *\r
485  * WinMain\r
486  *\r
487 \*---------------------------------------------------------------------------*/\r
488 \r
489 int APIENTRY\r
490 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
491         LPSTR lpCmdLine, int nCmdShow)\r
492 {\r
493   MSG msg;\r
494   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
495 //  INITCOMMONCONTROLSEX ex;\r
496 \r
497   debugFP = stderr;\r
498 \r
499   LoadLibrary("RICHED32.DLL");\r
500   consoleCF.cbSize = sizeof(CHARFORMAT);\r
501 \r
502   if (!InitApplication(hInstance)) {\r
503     return (FALSE);\r
504   }\r
505   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
506     return (FALSE);\r
507   }\r
508 \r
509   JAWS_INIT\r
510 \r
511 //  InitCommonControlsEx(&ex);\r
512   InitCommonControls();\r
513 \r
514   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
515   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
516   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
517 \r
518   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
519 \r
520   while (GetMessage(&msg, /* message structure */\r
521                     NULL, /* handle of window receiving the message */\r
522                     0,    /* lowest message to examine */\r
523                     0))   /* highest message to examine */\r
524     {\r
525 \r
526       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
527         // [HGM] navigate: switch between all windows with tab\r
528         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
529         int i, currentElement = 0;\r
530 \r
531         // first determine what element of the chain we come from (if any)\r
532         if(appData.icsActive) {\r
533             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
534             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
535         }\r
536         if(engineOutputDialog && EngineOutputIsUp()) {\r
537             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
538             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
539         }\r
540         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
541             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
542         }\r
543         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
544         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
545         if(msg.hwnd == e1)                 currentElement = 2; else\r
546         if(msg.hwnd == e2)                 currentElement = 3; else\r
547         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
548         if(msg.hwnd == mh)                currentElement = 4; else\r
549         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
550         if(msg.hwnd == hText)  currentElement = 5; else\r
551         if(msg.hwnd == hInput) currentElement = 6; else\r
552         for (i = 0; i < N_BUTTONS; i++) {\r
553             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
554         }\r
555 \r
556         // determine where to go to\r
557         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
558           do {\r
559             currentElement = (currentElement + direction) % 7;\r
560             switch(currentElement) {\r
561                 case 0:\r
562                   h = hwndMain; break; // passing this case always makes the loop exit\r
563                 case 1:\r
564                   h = buttonDesc[0].hwnd; break; // could be NULL\r
565                 case 2:\r
566                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
567                   h = e1; break;\r
568                 case 3:\r
569                   if(!EngineOutputIsUp()) continue;\r
570                   h = e2; break;\r
571                 case 4:\r
572                   if(!MoveHistoryIsUp()) continue;\r
573                   h = mh; break;\r
574 //              case 6: // input to eval graph does not seem to get here!\r
575 //                if(!EvalGraphIsUp()) continue;\r
576 //                h = evalGraphDialog; break;\r
577                 case 5:\r
578                   if(!appData.icsActive) continue;\r
579                   SAY("display");\r
580                   h = hText; break;\r
581                 case 6:\r
582                   if(!appData.icsActive) continue;\r
583                   SAY("input");\r
584                   h = hInput; break;\r
585             }\r
586           } while(h == 0);\r
587 \r
588           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
589           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
590           SetFocus(h);\r
591 \r
592           continue; // this message now has been processed\r
593         }\r
594       }\r
595 \r
596       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
597           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
598           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
599           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
600           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
601           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
602           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
603           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
604           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
605           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
606         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
607         for(i=0; i<MAX_CHAT; i++) \r
608             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
609                 done = 1; break;\r
610         }\r
611         if(done) continue; // [HGM] chat: end patch\r
612         TranslateMessage(&msg); /* Translates virtual key codes */\r
613         DispatchMessage(&msg);  /* Dispatches message to window */\r
614       }\r
615     }\r
616 \r
617 \r
618   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
619 }\r
620 \r
621 /*---------------------------------------------------------------------------*\\r
622  *\r
623  * Initialization functions\r
624  *\r
625 \*---------------------------------------------------------------------------*/\r
626 \r
627 void\r
628 SetUserLogo()\r
629 {   // update user logo if necessary\r
630     static char oldUserName[MSG_SIZ], *curName;\r
631 \r
632     if(appData.autoLogo) {\r
633           curName = UserName();\r
634           if(strcmp(curName, oldUserName)) {\r
635                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
636                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
637                 strcpy(oldUserName, curName);\r
638           }\r
639     }\r
640 }\r
641 \r
642 BOOL\r
643 InitApplication(HINSTANCE hInstance)\r
644 {\r
645   WNDCLASS wc;\r
646 \r
647   /* Fill in window class structure with parameters that describe the */\r
648   /* main window. */\r
649 \r
650   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
651   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
652   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
653   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
654   wc.hInstance     = hInstance;         /* Owner of this class */\r
655   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
656   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
657   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
658   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
659   wc.lpszClassName = szAppName;                 /* Name to register as */\r
660 \r
661   /* Register the window class and return success/failure code. */\r
662   if (!RegisterClass(&wc)) return FALSE;\r
663 \r
664   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
665   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
666   wc.cbClsExtra    = 0;\r
667   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
668   wc.hInstance     = hInstance;\r
669   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
670   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
671   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
672   wc.lpszMenuName  = NULL;\r
673   wc.lpszClassName = szConsoleName;\r
674 \r
675   if (!RegisterClass(&wc)) return FALSE;\r
676   return TRUE;\r
677 }\r
678 \r
679 \r
680 /* Set by InitInstance, used by EnsureOnScreen */\r
681 int screenHeight, screenWidth;\r
682 \r
683 void\r
684 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
685 {\r
686 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
687   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
688   if (*x > screenWidth - 32) *x = 0;\r
689   if (*y > screenHeight - 32) *y = 0;\r
690   if (*x < minX) *x = minX;\r
691   if (*y < minY) *y = minY;\r
692 }\r
693 \r
694 BOOL\r
695 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
696 {\r
697   HWND hwnd; /* Main window handle. */\r
698   int ibs;\r
699   WINDOWPLACEMENT wp;\r
700   char *filepart;\r
701 \r
702   hInst = hInstance;    /* Store instance handle in our global variable */\r
703   programName = szAppName;\r
704 \r
705   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
706     *filepart = NULLCHAR;\r
707   } else {\r
708     GetCurrentDirectory(MSG_SIZ, installDir);\r
709   }\r
710   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
711   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
712   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
713   /* xboard, and older WinBoards, controlled the move sound with the\r
714      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
715      always turn the option on (so that the backend will call us),\r
716      then let the user turn the sound off by setting it to silence if\r
717      desired.  To accommodate old winboard.ini files saved by old\r
718      versions of WinBoard, we also turn off the sound if the option\r
719      was initially set to false. [HGM] taken out of InitAppData */\r
720   if (!appData.ringBellAfterMoves) {\r
721     sounds[(int)SoundMove].name = strdup("");\r
722     appData.ringBellAfterMoves = TRUE;\r
723   }\r
724   if (appData.debugMode) {\r
725     debugFP = fopen(appData.nameOfDebugFile, "w");\r
726     setbuf(debugFP, NULL);\r
727   }\r
728 \r
729   InitBackEnd1();\r
730 \r
731 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
732 //  InitEngineUCI( installDir, &second );\r
733 \r
734   /* Create a main window for this application instance. */\r
735   hwnd = CreateWindow(szAppName, szTitle,\r
736                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
737                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
738                       NULL, NULL, hInstance, NULL);\r
739   hwndMain = hwnd;\r
740 \r
741   /* If window could not be created, return "failure" */\r
742   if (!hwnd) {\r
743     return (FALSE);\r
744   }\r
745 \r
746   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
747   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
748       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
749 \r
750       if (first.programLogo == NULL && appData.debugMode) {\r
751           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
752       }\r
753   } else if(appData.autoLogo) {\r
754       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
755         char buf[MSG_SIZ];\r
756         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
757         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
758       }\r
759   }\r
760 \r
761   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
762       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
763 \r
764       if (second.programLogo == NULL && appData.debugMode) {\r
765           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
766       }\r
767   } else if(appData.autoLogo) {\r
768       char buf[MSG_SIZ];\r
769       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
770         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
771         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
772       } else\r
773       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
774         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
775         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
776       }\r
777   }\r
778 \r
779   SetUserLogo();\r
780 \r
781   iconWhite = LoadIcon(hInstance, "icon_white");\r
782   iconBlack = LoadIcon(hInstance, "icon_black");\r
783   iconCurrent = iconWhite;\r
784   InitDrawingColors();\r
785   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
786   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
787   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
788     /* Compute window size for each board size, and use the largest\r
789        size that fits on this screen as the default. */\r
790     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
791     if (boardSize == (BoardSize)-1 &&\r
792         winH <= screenHeight\r
793            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
794         && winW <= screenWidth) {\r
795       boardSize = (BoardSize)ibs;\r
796     }\r
797   }\r
798 \r
799   InitDrawingSizes(boardSize, 0);\r
800   InitMenuChecks();\r
801   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
802 \r
803   /* [AS] Load textures if specified */\r
804   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
805   \r
806   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
807       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
808       liteBackTextureMode = appData.liteBackTextureMode;\r
809 \r
810       if (liteBackTexture == NULL && appData.debugMode) {\r
811           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
812       }\r
813   }\r
814   \r
815   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
816       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
817       darkBackTextureMode = appData.darkBackTextureMode;\r
818 \r
819       if (darkBackTexture == NULL && appData.debugMode) {\r
820           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
821       }\r
822   }\r
823 \r
824   mysrandom( (unsigned) time(NULL) );\r
825 \r
826   /* [AS] Restore layout */\r
827   if( wpMoveHistory.visible ) {\r
828       MoveHistoryPopUp();\r
829   }\r
830 \r
831   if( wpEvalGraph.visible ) {\r
832       EvalGraphPopUp();\r
833   }\r
834 \r
835   if( wpEngineOutput.visible ) {\r
836       EngineOutputPopUp();\r
837   }\r
838 \r
839   InitBackEnd2();\r
840 \r
841   /* Make the window visible; update its client area; and return "success" */\r
842   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
843   wp.length = sizeof(WINDOWPLACEMENT);\r
844   wp.flags = 0;\r
845   wp.showCmd = nCmdShow;\r
846   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
847   wp.rcNormalPosition.left = wpMain.x;\r
848   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
849   wp.rcNormalPosition.top = wpMain.y;\r
850   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
851   SetWindowPlacement(hwndMain, &wp);\r
852 \r
853   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
854                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
855 \r
856   if (hwndConsole) {\r
857 #if AOT_CONSOLE\r
858     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
859                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
860 #endif\r
861     ShowWindow(hwndConsole, nCmdShow);\r
862   }\r
863   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
864   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
865 \r
866   return TRUE;\r
867 \r
868 }\r
869 \r
870 VOID\r
871 InitMenuChecks()\r
872 {\r
873   HMENU hmenu = GetMenu(hwndMain);\r
874 \r
875   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
876                         MF_BYCOMMAND|((appData.icsActive &&\r
877                                        *appData.icsCommPort != NULLCHAR) ?\r
878                                       MF_ENABLED : MF_GRAYED));\r
879   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
880                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
881                                      MF_CHECKED : MF_UNCHECKED));\r
882 }\r
883 \r
884 //---------------------------------------------------------------------------------------------------------\r
885 \r
886 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
887 #define XBOARD FALSE\r
888 \r
889 #define OPTCHAR "/"\r
890 #define SEPCHAR "="\r
891 \r
892 #include "args.h"\r
893 \r
894 // front-end part of option handling\r
895 \r
896 VOID\r
897 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
898 {\r
899   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
900   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
901   DeleteDC(hdc);\r
902   lf->lfWidth = 0;\r
903   lf->lfEscapement = 0;\r
904   lf->lfOrientation = 0;\r
905   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
906   lf->lfItalic = mfp->italic;\r
907   lf->lfUnderline = mfp->underline;\r
908   lf->lfStrikeOut = mfp->strikeout;\r
909   lf->lfCharSet = mfp->charset;\r
910   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
911   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
912   lf->lfQuality = DEFAULT_QUALITY;\r
913   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
914   strcpy(lf->lfFaceName, mfp->faceName);\r
915 }\r
916 \r
917 void\r
918 CreateFontInMF(MyFont *mf)\r
919\r
920   LFfromMFP(&mf->lf, &mf->mfp);\r
921   if (mf->hf) DeleteObject(mf->hf);\r
922   mf->hf = CreateFontIndirect(&mf->lf);\r
923 }\r
924 \r
925 // [HGM] This platform-dependent table provides the location for storing the color info\r
926 void *\r
927 colorVariable[] = {\r
928   &whitePieceColor, \r
929   &blackPieceColor, \r
930   &lightSquareColor,\r
931   &darkSquareColor, \r
932   &highlightSquareColor,\r
933   &premoveHighlightColor,\r
934   NULL,\r
935   &consoleBackgroundColor,\r
936   &appData.fontForeColorWhite,\r
937   &appData.fontBackColorWhite,\r
938   &appData.fontForeColorBlack,\r
939   &appData.fontBackColorBlack,\r
940   &appData.evalHistColorWhite,\r
941   &appData.evalHistColorBlack,\r
942   &appData.highlightArrowColor,\r
943 };\r
944 \r
945 /* Command line font name parser.  NULL name means do nothing.\r
946    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
947    For backward compatibility, syntax without the colon is also\r
948    accepted, but font names with digits in them won't work in that case.\r
949 */\r
950 VOID\r
951 ParseFontName(char *name, MyFontParams *mfp)\r
952 {\r
953   char *p, *q;\r
954   if (name == NULL) return;\r
955   p = name;\r
956   q = strchr(p, ':');\r
957   if (q) {\r
958     if (q - p >= sizeof(mfp->faceName))\r
959       ExitArgError("Font name too long:", name);\r
960     memcpy(mfp->faceName, p, q - p);\r
961     mfp->faceName[q - p] = NULLCHAR;\r
962     p = q + 1;\r
963   } else {\r
964     q = mfp->faceName;\r
965     while (*p && !isdigit(*p)) {\r
966       *q++ = *p++;\r
967       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
968         ExitArgError("Font name too long:", name);\r
969     }\r
970     while (q > mfp->faceName && q[-1] == ' ') q--;\r
971     *q = NULLCHAR;\r
972   }\r
973   if (!*p) ExitArgError("Font point size missing:", name);\r
974   mfp->pointSize = (float) atof(p);\r
975   mfp->bold = (strchr(p, 'b') != NULL);\r
976   mfp->italic = (strchr(p, 'i') != NULL);\r
977   mfp->underline = (strchr(p, 'u') != NULL);\r
978   mfp->strikeout = (strchr(p, 's') != NULL);\r
979   mfp->charset = DEFAULT_CHARSET;\r
980   q = strchr(p, 'c');\r
981   if (q)\r
982     mfp->charset = (BYTE) atoi(q+1);\r
983 }\r
984 \r
985 void\r
986 ParseFont(char *name, int number)\r
987 { // wrapper to shield back-end from 'font'\r
988   ParseFontName(name, &font[boardSize][number]->mfp);\r
989 }\r
990 \r
991 void\r
992 SetFontDefaults()\r
993 { // in WB  we have a 2D array of fonts; this initializes their description\r
994   int i, j;\r
995   /* Point font array elements to structures and\r
996      parse default font names */\r
997   for (i=0; i<NUM_FONTS; i++) {\r
998     for (j=0; j<NUM_SIZES; j++) {\r
999       font[j][i] = &fontRec[j][i];\r
1000       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1001     }\r
1002   }\r
1003 }\r
1004 \r
1005 void\r
1006 CreateFonts()\r
1007 { // here we create the actual fonts from the selected descriptions\r
1008   int i, j;\r
1009   for (i=0; i<NUM_FONTS; i++) {\r
1010     for (j=0; j<NUM_SIZES; j++) {\r
1011       CreateFontInMF(font[j][i]);\r
1012     }\r
1013   }\r
1014 }\r
1015 /* Color name parser.\r
1016    X version accepts X color names, but this one\r
1017    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1018 COLORREF\r
1019 ParseColorName(char *name)\r
1020 {\r
1021   int red, green, blue, count;\r
1022   char buf[MSG_SIZ];\r
1023 \r
1024   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1025   if (count != 3) {\r
1026     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1027       &red, &green, &blue);\r
1028   }\r
1029   if (count != 3) {\r
1030     sprintf(buf, "Can't parse color name %s", name);\r
1031     DisplayError(buf, 0);\r
1032     return RGB(0, 0, 0);\r
1033   }\r
1034   return PALETTERGB(red, green, blue);\r
1035 }\r
1036 \r
1037 void\r
1038 ParseColor(int n, char *name)\r
1039 { // for WinBoard the color is an int, which needs to be derived from the string\r
1040   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1041 }\r
1042 \r
1043 void\r
1044 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1045 {\r
1046   char *e = argValue;\r
1047   int eff = 0;\r
1048 \r
1049   while (*e) {\r
1050     if (*e == 'b')      eff |= CFE_BOLD;\r
1051     else if (*e == 'i') eff |= CFE_ITALIC;\r
1052     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1053     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1054     else if (*e == '#' || isdigit(*e)) break;\r
1055     e++;\r
1056   }\r
1057   *effects = eff;\r
1058   *color   = ParseColorName(e);\r
1059 }\r
1060 \r
1061 void\r
1062 ParseTextAttribs(ColorClass cc, char *s)\r
1063 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1064     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1065     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1066 }\r
1067 \r
1068 void\r
1069 ParseBoardSize(void *addr, char *name)\r
1070 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1071   BoardSize bs = SizeTiny;\r
1072   while (sizeInfo[bs].name != NULL) {\r
1073     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1074         *(BoardSize *)addr = bs;\r
1075         return;\r
1076     }\r
1077     bs++;\r
1078   }\r
1079   ExitArgError("Unrecognized board size value", name);\r
1080 }\r
1081 \r
1082 void\r
1083 LoadAllSounds()\r
1084 { // [HGM] import name from appData first\r
1085   ColorClass cc;\r
1086   SoundClass sc;\r
1087   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1088     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1089     textAttribs[cc].sound.data = NULL;\r
1090     MyLoadSound(&textAttribs[cc].sound);\r
1091   }\r
1092   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1093     textAttribs[cc].sound.name = strdup("");\r
1094     textAttribs[cc].sound.data = NULL;\r
1095   }\r
1096   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1097     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1098     sounds[sc].data = NULL;\r
1099     MyLoadSound(&sounds[sc]);\r
1100   }\r
1101 }\r
1102 \r
1103 void\r
1104 SetCommPortDefaults()\r
1105 {\r
1106    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1107   dcb.DCBlength = sizeof(DCB);\r
1108   dcb.BaudRate = 9600;\r
1109   dcb.fBinary = TRUE;\r
1110   dcb.fParity = FALSE;\r
1111   dcb.fOutxCtsFlow = FALSE;\r
1112   dcb.fOutxDsrFlow = FALSE;\r
1113   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1114   dcb.fDsrSensitivity = FALSE;\r
1115   dcb.fTXContinueOnXoff = TRUE;\r
1116   dcb.fOutX = FALSE;\r
1117   dcb.fInX = FALSE;\r
1118   dcb.fNull = FALSE;\r
1119   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1120   dcb.fAbortOnError = FALSE;\r
1121   dcb.ByteSize = 7;\r
1122   dcb.Parity = SPACEPARITY;\r
1123   dcb.StopBits = ONESTOPBIT;\r
1124 }\r
1125 \r
1126 // [HGM] args: these three cases taken out to stay in front-end\r
1127 void\r
1128 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1129 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1130         // while the curent board size determines the element. This system should be ported to XBoard.\r
1131         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1132         int bs;\r
1133         for (bs=0; bs<NUM_SIZES; bs++) {\r
1134           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1135           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1136           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1137             ad->argName, mfp->faceName, mfp->pointSize,\r
1138             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1139             mfp->bold ? "b" : "",\r
1140             mfp->italic ? "i" : "",\r
1141             mfp->underline ? "u" : "",\r
1142             mfp->strikeout ? "s" : "",\r
1143             (int)mfp->charset);\r
1144         }\r
1145       }\r
1146 \r
1147 void\r
1148 ExportSounds()\r
1149 { // [HGM] copy the names from the internal WB variables to appData\r
1150   ColorClass cc;\r
1151   SoundClass sc;\r
1152   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1153     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1154   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1155     (&appData.soundMove)[sc] = sounds[sc].name;\r
1156 }\r
1157 \r
1158 void\r
1159 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1160 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1161         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1162         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1163           (ta->effects & CFE_BOLD) ? "b" : "",\r
1164           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1165           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1166           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1167           (ta->effects) ? " " : "",\r
1168           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1169       }\r
1170 \r
1171 void\r
1172 SaveColor(FILE *f, ArgDescriptor *ad)\r
1173 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1174         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1175         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1176           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1177 }\r
1178 \r
1179 void\r
1180 SaveBoardSize(FILE *f, char *name, void *addr)\r
1181 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1182   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1183 }\r
1184 \r
1185 void\r
1186 ParseCommPortSettings(char *s)\r
1187 { // wrapper to keep dcb from back-end\r
1188   ParseCommSettings(s, &dcb);\r
1189 }\r
1190 \r
1191 void\r
1192 GetWindowCoords()\r
1193 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1194   GetActualPlacement(hwndMain, &wpMain);\r
1195   GetActualPlacement(hwndConsole, &wpConsole);\r
1196   GetActualPlacement(commentDialog, &wpComment);\r
1197   GetActualPlacement(editTagsDialog, &wpTags);\r
1198   GetActualPlacement(gameListDialog, &wpGameList);\r
1199   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1200   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1201   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1202 }\r
1203 \r
1204 void\r
1205 PrintCommPortSettings(FILE *f, char *name)\r
1206 { // wrapper to shield back-end from DCB\r
1207       PrintCommSettings(f, name, &dcb);\r
1208 }\r
1209 \r
1210 int\r
1211 MySearchPath(char *installDir, char *name, char *fullname)\r
1212 {\r
1213   char *dummy;\r
1214   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1215 }\r
1216 \r
1217 int\r
1218 MyGetFullPathName(char *name, char *fullname)\r
1219 {\r
1220   char *dummy;\r
1221   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1222 }\r
1223 \r
1224 int\r
1225 MainWindowUp()\r
1226 { // [HGM] args: allows testing if main window is realized from back-end\r
1227   return hwndMain != NULL;\r
1228 }\r
1229 \r
1230 void\r
1231 PopUpStartupDialog()\r
1232 {\r
1233     FARPROC lpProc;\r
1234     \r
1235     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1236     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1237     FreeProcInstance(lpProc);\r
1238 }\r
1239 \r
1240 /*---------------------------------------------------------------------------*\\r
1241  *\r
1242  * GDI board drawing routines\r
1243  *\r
1244 \*---------------------------------------------------------------------------*/\r
1245 \r
1246 /* [AS] Draw square using background texture */\r
1247 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1248 {\r
1249     XFORM   x;\r
1250 \r
1251     if( mode == 0 ) {\r
1252         return; /* Should never happen! */\r
1253     }\r
1254 \r
1255     SetGraphicsMode( dst, GM_ADVANCED );\r
1256 \r
1257     switch( mode ) {\r
1258     case 1:\r
1259         /* Identity */\r
1260         break;\r
1261     case 2:\r
1262         /* X reflection */\r
1263         x.eM11 = -1.0;\r
1264         x.eM12 = 0;\r
1265         x.eM21 = 0;\r
1266         x.eM22 = 1.0;\r
1267         x.eDx = (FLOAT) dw + dx - 1;\r
1268         x.eDy = 0;\r
1269         dx = 0;\r
1270         SetWorldTransform( dst, &x );\r
1271         break;\r
1272     case 3:\r
1273         /* Y reflection */\r
1274         x.eM11 = 1.0;\r
1275         x.eM12 = 0;\r
1276         x.eM21 = 0;\r
1277         x.eM22 = -1.0;\r
1278         x.eDx = 0;\r
1279         x.eDy = (FLOAT) dh + dy - 1;\r
1280         dy = 0;\r
1281         SetWorldTransform( dst, &x );\r
1282         break;\r
1283     case 4:\r
1284         /* X/Y flip */\r
1285         x.eM11 = 0;\r
1286         x.eM12 = 1.0;\r
1287         x.eM21 = 1.0;\r
1288         x.eM22 = 0;\r
1289         x.eDx = (FLOAT) dx;\r
1290         x.eDy = (FLOAT) dy;\r
1291         dx = 0;\r
1292         dy = 0;\r
1293         SetWorldTransform( dst, &x );\r
1294         break;\r
1295     }\r
1296 \r
1297     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1298 \r
1299     x.eM11 = 1.0;\r
1300     x.eM12 = 0;\r
1301     x.eM21 = 0;\r
1302     x.eM22 = 1.0;\r
1303     x.eDx = 0;\r
1304     x.eDy = 0;\r
1305     SetWorldTransform( dst, &x );\r
1306 \r
1307     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1308 }\r
1309 \r
1310 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1311 enum {\r
1312     PM_WP = (int) WhitePawn, \r
1313     PM_WN = (int) WhiteKnight, \r
1314     PM_WB = (int) WhiteBishop, \r
1315     PM_WR = (int) WhiteRook, \r
1316     PM_WQ = (int) WhiteQueen, \r
1317     PM_WF = (int) WhiteFerz, \r
1318     PM_WW = (int) WhiteWazir, \r
1319     PM_WE = (int) WhiteAlfil, \r
1320     PM_WM = (int) WhiteMan, \r
1321     PM_WO = (int) WhiteCannon, \r
1322     PM_WU = (int) WhiteUnicorn, \r
1323     PM_WH = (int) WhiteNightrider, \r
1324     PM_WA = (int) WhiteAngel, \r
1325     PM_WC = (int) WhiteMarshall, \r
1326     PM_WAB = (int) WhiteCardinal, \r
1327     PM_WD = (int) WhiteDragon, \r
1328     PM_WL = (int) WhiteLance, \r
1329     PM_WS = (int) WhiteCobra, \r
1330     PM_WV = (int) WhiteFalcon, \r
1331     PM_WSG = (int) WhiteSilver, \r
1332     PM_WG = (int) WhiteGrasshopper, \r
1333     PM_WK = (int) WhiteKing,\r
1334     PM_BP = (int) BlackPawn, \r
1335     PM_BN = (int) BlackKnight, \r
1336     PM_BB = (int) BlackBishop, \r
1337     PM_BR = (int) BlackRook, \r
1338     PM_BQ = (int) BlackQueen, \r
1339     PM_BF = (int) BlackFerz, \r
1340     PM_BW = (int) BlackWazir, \r
1341     PM_BE = (int) BlackAlfil, \r
1342     PM_BM = (int) BlackMan,\r
1343     PM_BO = (int) BlackCannon, \r
1344     PM_BU = (int) BlackUnicorn, \r
1345     PM_BH = (int) BlackNightrider, \r
1346     PM_BA = (int) BlackAngel, \r
1347     PM_BC = (int) BlackMarshall, \r
1348     PM_BG = (int) BlackGrasshopper, \r
1349     PM_BAB = (int) BlackCardinal,\r
1350     PM_BD = (int) BlackDragon,\r
1351     PM_BL = (int) BlackLance,\r
1352     PM_BS = (int) BlackCobra,\r
1353     PM_BV = (int) BlackFalcon,\r
1354     PM_BSG = (int) BlackSilver,\r
1355     PM_BK = (int) BlackKing\r
1356 };\r
1357 \r
1358 static HFONT hPieceFont = NULL;\r
1359 static HBITMAP hPieceMask[(int) EmptySquare];\r
1360 static HBITMAP hPieceFace[(int) EmptySquare];\r
1361 static int fontBitmapSquareSize = 0;\r
1362 static char pieceToFontChar[(int) EmptySquare] =\r
1363                               { 'p', 'n', 'b', 'r', 'q', \r
1364                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1365                       'k', 'o', 'm', 'v', 't', 'w', \r
1366                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1367                                                               'l' };\r
1368 \r
1369 extern BOOL SetCharTable( char *table, const char * map );\r
1370 /* [HGM] moved to backend.c */\r
1371 \r
1372 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1373 {\r
1374     HBRUSH hbrush;\r
1375     BYTE r1 = GetRValue( color );\r
1376     BYTE g1 = GetGValue( color );\r
1377     BYTE b1 = GetBValue( color );\r
1378     BYTE r2 = r1 / 2;\r
1379     BYTE g2 = g1 / 2;\r
1380     BYTE b2 = b1 / 2;\r
1381     RECT rc;\r
1382 \r
1383     /* Create a uniform background first */\r
1384     hbrush = CreateSolidBrush( color );\r
1385     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1386     FillRect( hdc, &rc, hbrush );\r
1387     DeleteObject( hbrush );\r
1388     \r
1389     if( mode == 1 ) {\r
1390         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1391         int steps = squareSize / 2;\r
1392         int i;\r
1393 \r
1394         for( i=0; i<steps; i++ ) {\r
1395             BYTE r = r1 - (r1-r2) * i / steps;\r
1396             BYTE g = g1 - (g1-g2) * i / steps;\r
1397             BYTE b = b1 - (b1-b2) * i / steps;\r
1398 \r
1399             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1400             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1401             FillRect( hdc, &rc, hbrush );\r
1402             DeleteObject(hbrush);\r
1403         }\r
1404     }\r
1405     else if( mode == 2 ) {\r
1406         /* Diagonal gradient, good more or less for every piece */\r
1407         POINT triangle[3];\r
1408         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1409         HBRUSH hbrush_old;\r
1410         int steps = squareSize;\r
1411         int i;\r
1412 \r
1413         triangle[0].x = squareSize - steps;\r
1414         triangle[0].y = squareSize;\r
1415         triangle[1].x = squareSize;\r
1416         triangle[1].y = squareSize;\r
1417         triangle[2].x = squareSize;\r
1418         triangle[2].y = squareSize - steps;\r
1419 \r
1420         for( i=0; i<steps; i++ ) {\r
1421             BYTE r = r1 - (r1-r2) * i / steps;\r
1422             BYTE g = g1 - (g1-g2) * i / steps;\r
1423             BYTE b = b1 - (b1-b2) * i / steps;\r
1424 \r
1425             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1426             hbrush_old = SelectObject( hdc, hbrush );\r
1427             Polygon( hdc, triangle, 3 );\r
1428             SelectObject( hdc, hbrush_old );\r
1429             DeleteObject(hbrush);\r
1430             triangle[0].x++;\r
1431             triangle[2].y++;\r
1432         }\r
1433 \r
1434         SelectObject( hdc, hpen );\r
1435     }\r
1436 }\r
1437 \r
1438 /*\r
1439     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1440     seems to work ok. The main problem here is to find the "inside" of a chess\r
1441     piece: follow the steps as explained below.\r
1442 */\r
1443 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1444 {\r
1445     HBITMAP hbm;\r
1446     HBITMAP hbm_old;\r
1447     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1448     RECT rc;\r
1449     SIZE sz;\r
1450     POINT pt;\r
1451     int backColor = whitePieceColor; \r
1452     int foreColor = blackPieceColor;\r
1453     \r
1454     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1455         backColor = appData.fontBackColorWhite;\r
1456         foreColor = appData.fontForeColorWhite;\r
1457     }\r
1458     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1459         backColor = appData.fontBackColorBlack;\r
1460         foreColor = appData.fontForeColorBlack;\r
1461     }\r
1462 \r
1463     /* Mask */\r
1464     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1465 \r
1466     hbm_old = SelectObject( hdc, hbm );\r
1467 \r
1468     rc.left = 0;\r
1469     rc.top = 0;\r
1470     rc.right = squareSize;\r
1471     rc.bottom = squareSize;\r
1472 \r
1473     /* Step 1: background is now black */\r
1474     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1475 \r
1476     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1477 \r
1478     pt.x = (squareSize - sz.cx) / 2;\r
1479     pt.y = (squareSize - sz.cy) / 2;\r
1480 \r
1481     SetBkMode( hdc, TRANSPARENT );\r
1482     SetTextColor( hdc, chroma );\r
1483     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1484     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1485 \r
1486     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1487     /* Step 3: the area outside the piece is filled with white */\r
1488 //    FloodFill( hdc, 0, 0, chroma );\r
1489     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1490     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1491     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1492     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1493     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1494     /* \r
1495         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1496         but if the start point is not inside the piece we're lost!\r
1497         There should be a better way to do this... if we could create a region or path\r
1498         from the fill operation we would be fine for example.\r
1499     */\r
1500 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1501     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1502 \r
1503     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1504         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1505         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1506 \r
1507         SelectObject( dc2, bm2 );\r
1508         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1509         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1510         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1511         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1512         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1513 \r
1514         DeleteDC( dc2 );\r
1515         DeleteObject( bm2 );\r
1516     }\r
1517 \r
1518     SetTextColor( hdc, 0 );\r
1519     /* \r
1520         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1521         draw the piece again in black for safety.\r
1522     */\r
1523     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1524 \r
1525     SelectObject( hdc, hbm_old );\r
1526 \r
1527     if( hPieceMask[index] != NULL ) {\r
1528         DeleteObject( hPieceMask[index] );\r
1529     }\r
1530 \r
1531     hPieceMask[index] = hbm;\r
1532 \r
1533     /* Face */\r
1534     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1535 \r
1536     SelectObject( hdc, hbm );\r
1537 \r
1538     {\r
1539         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1540         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1541         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1542 \r
1543         SelectObject( dc1, hPieceMask[index] );\r
1544         SelectObject( dc2, bm2 );\r
1545         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1546         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1547         \r
1548         /* \r
1549             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1550             the piece background and deletes (makes transparent) the rest.\r
1551             Thanks to that mask, we are free to paint the background with the greates\r
1552             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1553             We use this, to make gradients and give the pieces a "roundish" look.\r
1554         */\r
1555         SetPieceBackground( hdc, backColor, 2 );\r
1556         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1557 \r
1558         DeleteDC( dc2 );\r
1559         DeleteDC( dc1 );\r
1560         DeleteObject( bm2 );\r
1561     }\r
1562 \r
1563     SetTextColor( hdc, foreColor );\r
1564     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1565 \r
1566     SelectObject( hdc, hbm_old );\r
1567 \r
1568     if( hPieceFace[index] != NULL ) {\r
1569         DeleteObject( hPieceFace[index] );\r
1570     }\r
1571 \r
1572     hPieceFace[index] = hbm;\r
1573 }\r
1574 \r
1575 static int TranslatePieceToFontPiece( int piece )\r
1576 {\r
1577     switch( piece ) {\r
1578     case BlackPawn:\r
1579         return PM_BP;\r
1580     case BlackKnight:\r
1581         return PM_BN;\r
1582     case BlackBishop:\r
1583         return PM_BB;\r
1584     case BlackRook:\r
1585         return PM_BR;\r
1586     case BlackQueen:\r
1587         return PM_BQ;\r
1588     case BlackKing:\r
1589         return PM_BK;\r
1590     case WhitePawn:\r
1591         return PM_WP;\r
1592     case WhiteKnight:\r
1593         return PM_WN;\r
1594     case WhiteBishop:\r
1595         return PM_WB;\r
1596     case WhiteRook:\r
1597         return PM_WR;\r
1598     case WhiteQueen:\r
1599         return PM_WQ;\r
1600     case WhiteKing:\r
1601         return PM_WK;\r
1602 \r
1603     case BlackAngel:\r
1604         return PM_BA;\r
1605     case BlackMarshall:\r
1606         return PM_BC;\r
1607     case BlackFerz:\r
1608         return PM_BF;\r
1609     case BlackNightrider:\r
1610         return PM_BH;\r
1611     case BlackAlfil:\r
1612         return PM_BE;\r
1613     case BlackWazir:\r
1614         return PM_BW;\r
1615     case BlackUnicorn:\r
1616         return PM_BU;\r
1617     case BlackCannon:\r
1618         return PM_BO;\r
1619     case BlackGrasshopper:\r
1620         return PM_BG;\r
1621     case BlackMan:\r
1622         return PM_BM;\r
1623     case BlackSilver:\r
1624         return PM_BSG;\r
1625     case BlackLance:\r
1626         return PM_BL;\r
1627     case BlackFalcon:\r
1628         return PM_BV;\r
1629     case BlackCobra:\r
1630         return PM_BS;\r
1631     case BlackCardinal:\r
1632         return PM_BAB;\r
1633     case BlackDragon:\r
1634         return PM_BD;\r
1635 \r
1636     case WhiteAngel:\r
1637         return PM_WA;\r
1638     case WhiteMarshall:\r
1639         return PM_WC;\r
1640     case WhiteFerz:\r
1641         return PM_WF;\r
1642     case WhiteNightrider:\r
1643         return PM_WH;\r
1644     case WhiteAlfil:\r
1645         return PM_WE;\r
1646     case WhiteWazir:\r
1647         return PM_WW;\r
1648     case WhiteUnicorn:\r
1649         return PM_WU;\r
1650     case WhiteCannon:\r
1651         return PM_WO;\r
1652     case WhiteGrasshopper:\r
1653         return PM_WG;\r
1654     case WhiteMan:\r
1655         return PM_WM;\r
1656     case WhiteSilver:\r
1657         return PM_WSG;\r
1658     case WhiteLance:\r
1659         return PM_WL;\r
1660     case WhiteFalcon:\r
1661         return PM_WV;\r
1662     case WhiteCobra:\r
1663         return PM_WS;\r
1664     case WhiteCardinal:\r
1665         return PM_WAB;\r
1666     case WhiteDragon:\r
1667         return PM_WD;\r
1668     }\r
1669 \r
1670     return 0;\r
1671 }\r
1672 \r
1673 void CreatePiecesFromFont()\r
1674 {\r
1675     LOGFONT lf;\r
1676     HDC hdc_window = NULL;\r
1677     HDC hdc = NULL;\r
1678     HFONT hfont_old;\r
1679     int fontHeight;\r
1680     int i;\r
1681 \r
1682     if( fontBitmapSquareSize < 0 ) {\r
1683         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1684         return;\r
1685     }\r
1686 \r
1687     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1688         fontBitmapSquareSize = -1;\r
1689         return;\r
1690     }\r
1691 \r
1692     if( fontBitmapSquareSize != squareSize ) {\r
1693         hdc_window = GetDC( hwndMain );\r
1694         hdc = CreateCompatibleDC( hdc_window );\r
1695 \r
1696         if( hPieceFont != NULL ) {\r
1697             DeleteObject( hPieceFont );\r
1698         }\r
1699         else {\r
1700             for( i=0; i<=(int)BlackKing; i++ ) {\r
1701                 hPieceMask[i] = NULL;\r
1702                 hPieceFace[i] = NULL;\r
1703             }\r
1704         }\r
1705 \r
1706         fontHeight = 75;\r
1707 \r
1708         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1709             fontHeight = appData.fontPieceSize;\r
1710         }\r
1711 \r
1712         fontHeight = (fontHeight * squareSize) / 100;\r
1713 \r
1714         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1715         lf.lfWidth = 0;\r
1716         lf.lfEscapement = 0;\r
1717         lf.lfOrientation = 0;\r
1718         lf.lfWeight = FW_NORMAL;\r
1719         lf.lfItalic = 0;\r
1720         lf.lfUnderline = 0;\r
1721         lf.lfStrikeOut = 0;\r
1722         lf.lfCharSet = DEFAULT_CHARSET;\r
1723         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1724         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1725         lf.lfQuality = PROOF_QUALITY;\r
1726         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
1727         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
1728         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
1729 \r
1730         hPieceFont = CreateFontIndirect( &lf );\r
1731 \r
1732         if( hPieceFont == NULL ) {\r
1733             fontBitmapSquareSize = -2;\r
1734         }\r
1735         else {\r
1736             /* Setup font-to-piece character table */\r
1737             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
1738                 /* No (or wrong) global settings, try to detect the font */\r
1739                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
1740                     /* Alpha */\r
1741                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
1742                 }\r
1743                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
1744                     /* DiagramTT* family */\r
1745                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
1746                 }\r
1747                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
1748                     /* Fairy symbols */\r
1749                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
1750                 }\r
1751                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
1752                     /* Good Companion (Some characters get warped as literal :-( */\r
1753                     char s[] = "1cmWG0??S??oYI23wgQU";\r
1754                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
1755                     SetCharTable(pieceToFontChar, s);\r
1756                 }\r
1757                 else {\r
1758                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
1759                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
1760                 }\r
1761             }\r
1762 \r
1763             /* Create bitmaps */\r
1764             hfont_old = SelectObject( hdc, hPieceFont );\r
1765             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
1766                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
1767                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
1768 \r
1769             SelectObject( hdc, hfont_old );\r
1770 \r
1771             fontBitmapSquareSize = squareSize;\r
1772         }\r
1773     }\r
1774 \r
1775     if( hdc != NULL ) {\r
1776         DeleteDC( hdc );\r
1777     }\r
1778 \r
1779     if( hdc_window != NULL ) {\r
1780         ReleaseDC( hwndMain, hdc_window );\r
1781     }\r
1782 }\r
1783 \r
1784 HBITMAP\r
1785 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
1786 {\r
1787   char name[128];\r
1788 \r
1789   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
1790   if (gameInfo.event &&\r
1791       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
1792       strcmp(name, "k80s") == 0) {\r
1793     strcpy(name, "tim");\r
1794   }\r
1795   return LoadBitmap(hinst, name);\r
1796 }\r
1797 \r
1798 \r
1799 /* Insert a color into the program's logical palette\r
1800    structure.  This code assumes the given color is\r
1801    the result of the RGB or PALETTERGB macro, and it\r
1802    knows how those macros work (which is documented).\r
1803 */\r
1804 VOID\r
1805 InsertInPalette(COLORREF color)\r
1806 {\r
1807   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
1808 \r
1809   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
1810     DisplayFatalError("Too many colors", 0, 1);\r
1811     pLogPal->palNumEntries--;\r
1812     return;\r
1813   }\r
1814 \r
1815   pe->peFlags = (char) 0;\r
1816   pe->peRed = (char) (0xFF & color);\r
1817   pe->peGreen = (char) (0xFF & (color >> 8));\r
1818   pe->peBlue = (char) (0xFF & (color >> 16));\r
1819   return;\r
1820 }\r
1821 \r
1822 \r
1823 VOID\r
1824 InitDrawingColors()\r
1825 {\r
1826   if (pLogPal == NULL) {\r
1827     /* Allocate enough memory for a logical palette with\r
1828      * PALETTESIZE entries and set the size and version fields\r
1829      * of the logical palette structure.\r
1830      */\r
1831     pLogPal = (NPLOGPALETTE)\r
1832       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
1833                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
1834     pLogPal->palVersion    = 0x300;\r
1835   }\r
1836   pLogPal->palNumEntries = 0;\r
1837 \r
1838   InsertInPalette(lightSquareColor);\r
1839   InsertInPalette(darkSquareColor);\r
1840   InsertInPalette(whitePieceColor);\r
1841   InsertInPalette(blackPieceColor);\r
1842   InsertInPalette(highlightSquareColor);\r
1843   InsertInPalette(premoveHighlightColor);\r
1844 \r
1845   /*  create a logical color palette according the information\r
1846    *  in the LOGPALETTE structure.\r
1847    */\r
1848   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
1849 \r
1850   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
1851   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
1852   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
1853   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
1854   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
1855   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
1856   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
1857   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
1858   /* [AS] Force rendering of the font-based pieces */\r
1859   if( fontBitmapSquareSize > 0 ) {\r
1860     fontBitmapSquareSize = 0;\r
1861   }\r
1862 }\r
1863 \r
1864 \r
1865 int\r
1866 BoardWidth(int boardSize, int n)\r
1867 { /* [HGM] argument n added to allow different width and height */\r
1868   int lineGap = sizeInfo[boardSize].lineGap;\r
1869 \r
1870   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1871       lineGap = appData.overrideLineGap;\r
1872   }\r
1873 \r
1874   return (n + 1) * lineGap +\r
1875           n * sizeInfo[boardSize].squareSize;\r
1876 }\r
1877 \r
1878 /* Respond to board resize by dragging edge */\r
1879 VOID\r
1880 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
1881 {\r
1882   BoardSize newSize = NUM_SIZES - 1;\r
1883   static int recurse = 0;\r
1884   if (IsIconic(hwndMain)) return;\r
1885   if (recurse > 0) return;\r
1886   recurse++;\r
1887   while (newSize > 0) {\r
1888         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
1889         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
1890            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
1891     newSize--;\r
1892   } \r
1893   boardSize = newSize;\r
1894   InitDrawingSizes(boardSize, flags);\r
1895   recurse--;\r
1896 }\r
1897 \r
1898 \r
1899 \r
1900 VOID\r
1901 InitDrawingSizes(BoardSize boardSize, int flags)\r
1902 {\r
1903   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
1904   ChessSquare piece;\r
1905   static int oldBoardSize = -1, oldTinyLayout = 0;\r
1906   HDC hdc;\r
1907   SIZE clockSize, messageSize;\r
1908   HFONT oldFont;\r
1909   char buf[MSG_SIZ];\r
1910   char *str;\r
1911   HMENU hmenu = GetMenu(hwndMain);\r
1912   RECT crect, wrect, oldRect;\r
1913   int offby;\r
1914   LOGBRUSH logbrush;\r
1915 \r
1916   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
1917   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
1918 \r
1919   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
1920   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
1921 \r
1922   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
1923   oldRect.top = wpMain.y;\r
1924   oldRect.right = wpMain.x + wpMain.width;\r
1925   oldRect.bottom = wpMain.y + wpMain.height;\r
1926 \r
1927   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
1928   smallLayout = sizeInfo[boardSize].smallLayout;\r
1929   squareSize = sizeInfo[boardSize].squareSize;\r
1930   lineGap = sizeInfo[boardSize].lineGap;\r
1931   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
1932 \r
1933   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1934       lineGap = appData.overrideLineGap;\r
1935   }\r
1936 \r
1937   if (tinyLayout != oldTinyLayout) {\r
1938     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
1939     if (tinyLayout) {\r
1940       style &= ~WS_SYSMENU;\r
1941       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
1942                  "&Minimize\tCtrl+F4");\r
1943     } else {\r
1944       style |= WS_SYSMENU;\r
1945       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
1946     }\r
1947     SetWindowLong(hwndMain, GWL_STYLE, style);\r
1948 \r
1949     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
1950       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
1951         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
1952     }\r
1953     DrawMenuBar(hwndMain);\r
1954   }\r
1955 \r
1956   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
1957   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
1958 \r
1959   /* Get text area sizes */\r
1960   hdc = GetDC(hwndMain);\r
1961   if (appData.clockMode) {\r
1962     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
1963   } else {\r
1964     sprintf(buf, "White");\r
1965   }\r
1966   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
1967   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
1968   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
1969   str = "We only care about the height here";\r
1970   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
1971   SelectObject(hdc, oldFont);\r
1972   ReleaseDC(hwndMain, hdc);\r
1973 \r
1974   /* Compute where everything goes */\r
1975   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
1976         /* [HGM] logo: if either logo is on, reserve space for it */\r
1977         logoHeight =  2*clockSize.cy;\r
1978         leftLogoRect.left   = OUTER_MARGIN;\r
1979         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
1980         leftLogoRect.top    = OUTER_MARGIN;\r
1981         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
1982 \r
1983         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
1984         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
1985         rightLogoRect.top    = OUTER_MARGIN;\r
1986         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
1987 \r
1988 \r
1989     whiteRect.left = leftLogoRect.right;\r
1990     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
1991     whiteRect.top = OUTER_MARGIN;\r
1992     whiteRect.bottom = whiteRect.top + logoHeight;\r
1993 \r
1994     blackRect.right = rightLogoRect.left;\r
1995     blackRect.left = whiteRect.right + INNER_MARGIN;\r
1996     blackRect.top = whiteRect.top;\r
1997     blackRect.bottom = whiteRect.bottom;\r
1998   } else {\r
1999     whiteRect.left = OUTER_MARGIN;\r
2000     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2001     whiteRect.top = OUTER_MARGIN;\r
2002     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2003 \r
2004     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2005     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2006     blackRect.top = whiteRect.top;\r
2007     blackRect.bottom = whiteRect.bottom;\r
2008   }\r
2009 \r
2010   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2011   if (appData.showButtonBar) {\r
2012     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2013       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2014   } else {\r
2015     messageRect.right = OUTER_MARGIN + boardWidth;\r
2016   }\r
2017   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2018   messageRect.bottom = messageRect.top + messageSize.cy;\r
2019 \r
2020   boardRect.left = OUTER_MARGIN;\r
2021   boardRect.right = boardRect.left + boardWidth;\r
2022   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2023   boardRect.bottom = boardRect.top + boardHeight;\r
2024 \r
2025   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2026   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2027   oldBoardSize = boardSize;\r
2028   oldTinyLayout = tinyLayout;\r
2029   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2030   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2031     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2032   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2033   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2034   wpMain.height = winH; //       without disturbing window attachments\r
2035   GetWindowRect(hwndMain, &wrect);\r
2036   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2037                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2038 \r
2039   // [HGM] placement: let attached windows follow size change.\r
2040   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2041   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2042   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2043   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2044   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2045 \r
2046   /* compensate if menu bar wrapped */\r
2047   GetClientRect(hwndMain, &crect);\r
2048   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2049   wpMain.height += offby;\r
2050   switch (flags) {\r
2051   case WMSZ_TOPLEFT:\r
2052     SetWindowPos(hwndMain, NULL, \r
2053                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2054                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2055     break;\r
2056 \r
2057   case WMSZ_TOPRIGHT:\r
2058   case WMSZ_TOP:\r
2059     SetWindowPos(hwndMain, NULL, \r
2060                  wrect.left, wrect.bottom - wpMain.height, \r
2061                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2062     break;\r
2063 \r
2064   case WMSZ_BOTTOMLEFT:\r
2065   case WMSZ_LEFT:\r
2066     SetWindowPos(hwndMain, NULL, \r
2067                  wrect.right - wpMain.width, wrect.top, \r
2068                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2069     break;\r
2070 \r
2071   case WMSZ_BOTTOMRIGHT:\r
2072   case WMSZ_BOTTOM:\r
2073   case WMSZ_RIGHT:\r
2074   default:\r
2075     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2076                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2077     break;\r
2078   }\r
2079 \r
2080   hwndPause = NULL;\r
2081   for (i = 0; i < N_BUTTONS; i++) {\r
2082     if (buttonDesc[i].hwnd != NULL) {\r
2083       DestroyWindow(buttonDesc[i].hwnd);\r
2084       buttonDesc[i].hwnd = NULL;\r
2085     }\r
2086     if (appData.showButtonBar) {\r
2087       buttonDesc[i].hwnd =\r
2088         CreateWindow("BUTTON", buttonDesc[i].label,\r
2089                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2090                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2091                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2092                      (HMENU) buttonDesc[i].id,\r
2093                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2094       if (tinyLayout) {\r
2095         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2096                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2097                     MAKELPARAM(FALSE, 0));\r
2098       }\r
2099       if (buttonDesc[i].id == IDM_Pause)\r
2100         hwndPause = buttonDesc[i].hwnd;\r
2101       buttonDesc[i].wndproc = (WNDPROC)\r
2102         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2103     }\r
2104   }\r
2105   if (gridPen != NULL) DeleteObject(gridPen);\r
2106   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2107   if (premovePen != NULL) DeleteObject(premovePen);\r
2108   if (lineGap != 0) {\r
2109     logbrush.lbStyle = BS_SOLID;\r
2110     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2111     gridPen =\r
2112       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2113                    lineGap, &logbrush, 0, NULL);\r
2114     logbrush.lbColor = highlightSquareColor;\r
2115     highlightPen =\r
2116       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2117                    lineGap, &logbrush, 0, NULL);\r
2118 \r
2119     logbrush.lbColor = premoveHighlightColor; \r
2120     premovePen =\r
2121       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2122                    lineGap, &logbrush, 0, NULL);\r
2123 \r
2124     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2125     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2126       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2127       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2128         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2129       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2130         BOARD_WIDTH * (squareSize + lineGap);\r
2131       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2132     }\r
2133     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2134       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2135       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2136         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2137         lineGap / 2 + (i * (squareSize + lineGap));\r
2138       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2139         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2140       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2141     }\r
2142   }\r
2143 \r
2144   /* [HGM] Licensing requirement */\r
2145 #ifdef GOTHIC\r
2146   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2147 #endif\r
2148 #ifdef FALCON\r
2149   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2150 #endif\r
2151   GothicPopUp( "", VariantNormal);\r
2152 \r
2153 \r
2154 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2155 \r
2156   /* Load piece bitmaps for this board size */\r
2157   for (i=0; i<=2; i++) {\r
2158     for (piece = WhitePawn;\r
2159          (int) piece < (int) BlackPawn;\r
2160          piece = (ChessSquare) ((int) piece + 1)) {\r
2161       if (pieceBitmap[i][piece] != NULL)\r
2162         DeleteObject(pieceBitmap[i][piece]);\r
2163     }\r
2164   }\r
2165 \r
2166   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2167   // Orthodox Chess pieces\r
2168   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2169   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2170   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2171   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2172   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2173   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2174   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2175   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2176   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2177   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2178   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2179   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2180   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2181   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2182   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2183   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2184     // in Shogi, Hijack the unused Queen for Lance\r
2185     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2186     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2187     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2188   } else {\r
2189     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2190     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2191     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2192   }\r
2193 \r
2194   if(squareSize <= 72 && squareSize >= 33) { \r
2195     /* A & C are available in most sizes now */\r
2196     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2197       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2198       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2199       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2200       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2201       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2202       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2203       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2204       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2205       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2206       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2207       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2208       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2209     } else { // Smirf-like\r
2210       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2211       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2212       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2213     }\r
2214     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2215       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2216       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2217       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2218     } else { // WinBoard standard\r
2219       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2220       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2221       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2222     }\r
2223   }\r
2224 \r
2225 \r
2226   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2227     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2228     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2229     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2230     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2231     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2232     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2233     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2234     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2235     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2236     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2237     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2238     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2239     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2240     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2241     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2242     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2243     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2244     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2245     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2246     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2247     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2248     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2249     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2250     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2251     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2252     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2253     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2254     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2255     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2256     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2257 \r
2258     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2259       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2260       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2261       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2262       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2263       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2264       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2265       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2266       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2267       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2268       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2269       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2270       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2271     } else {\r
2272       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2273       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2274       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2275       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2276       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2277       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2278       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2279       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2280       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2281       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2282       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2283       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2284     }\r
2285 \r
2286   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2287     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2288     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2289     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2290     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2291     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2292     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2293     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2294     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2295     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2296     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2297     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2298     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2299     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2300     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2301   }\r
2302 \r
2303 \r
2304   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2305   /* special Shogi support in this size */\r
2306   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2307       for (piece = WhitePawn;\r
2308            (int) piece < (int) BlackPawn;\r
2309            piece = (ChessSquare) ((int) piece + 1)) {\r
2310         if (pieceBitmap[i][piece] != NULL)\r
2311           DeleteObject(pieceBitmap[i][piece]);\r
2312       }\r
2313     }\r
2314   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2315   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2316   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2317   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2318   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2319   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2320   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2321   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2322   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2323   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2324   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2325   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2326   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2327   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2328   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2329   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2330   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2331   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2332   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2333   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2334   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2335   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2336   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2337   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2338   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2339   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2340   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2341   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2342   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2343   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2344   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2345   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2346   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2347   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2348   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2349   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2350   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2351   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2352   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2353   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2354   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2355   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2356   minorSize = 0;\r
2357   }\r
2358 }\r
2359 \r
2360 HBITMAP\r
2361 PieceBitmap(ChessSquare p, int kind)\r
2362 {\r
2363   if ((int) p >= (int) BlackPawn)\r
2364     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2365 \r
2366   return pieceBitmap[kind][(int) p];\r
2367 }\r
2368 \r
2369 /***************************************************************/\r
2370 \r
2371 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2372 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2373 /*\r
2374 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2375 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2376 */\r
2377 \r
2378 VOID\r
2379 SquareToPos(int row, int column, int * x, int * y)\r
2380 {\r
2381   if (flipView) {\r
2382     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2383     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2384   } else {\r
2385     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2386     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2387   }\r
2388 }\r
2389 \r
2390 VOID\r
2391 DrawCoordsOnDC(HDC hdc)\r
2392 {\r
2393   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
2394   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
2395   char str[2] = { NULLCHAR, NULLCHAR };\r
2396   int oldMode, oldAlign, x, y, start, i;\r
2397   HFONT oldFont;\r
2398   HBRUSH oldBrush;\r
2399 \r
2400   if (!appData.showCoords)\r
2401     return;\r
2402 \r
2403   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2404 \r
2405   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2406   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2407   oldAlign = GetTextAlign(hdc);\r
2408   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2409 \r
2410   y = boardRect.top + lineGap;\r
2411   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2412 \r
2413   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2414   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2415     str[0] = files[start + i];\r
2416     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2417     y += squareSize + lineGap;\r
2418   }\r
2419 \r
2420   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2421 \r
2422   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2423   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2424     str[0] = ranks[start + i];\r
2425     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2426     x += squareSize + lineGap;\r
2427   }    \r
2428 \r
2429   SelectObject(hdc, oldBrush);\r
2430   SetBkMode(hdc, oldMode);\r
2431   SetTextAlign(hdc, oldAlign);\r
2432   SelectObject(hdc, oldFont);\r
2433 }\r
2434 \r
2435 VOID\r
2436 DrawGridOnDC(HDC hdc)\r
2437 {\r
2438   HPEN oldPen;\r
2439  \r
2440   if (lineGap != 0) {\r
2441     oldPen = SelectObject(hdc, gridPen);\r
2442     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2443     SelectObject(hdc, oldPen);\r
2444   }\r
2445 }\r
2446 \r
2447 #define HIGHLIGHT_PEN 0\r
2448 #define PREMOVE_PEN   1\r
2449 \r
2450 VOID\r
2451 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2452 {\r
2453   int x1, y1;\r
2454   HPEN oldPen, hPen;\r
2455   if (lineGap == 0) return;\r
2456   if (flipView) {\r
2457     x1 = boardRect.left +\r
2458       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2459     y1 = boardRect.top +\r
2460       lineGap/2 + y * (squareSize + lineGap);\r
2461   } else {\r
2462     x1 = boardRect.left +\r
2463       lineGap/2 + x * (squareSize + lineGap);\r
2464     y1 = boardRect.top +\r
2465       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2466   }\r
2467   hPen = pen ? premovePen : highlightPen;\r
2468   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2469   MoveToEx(hdc, x1, y1, NULL);\r
2470   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2471   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2472   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2473   LineTo(hdc, x1, y1);\r
2474   SelectObject(hdc, oldPen);\r
2475 }\r
2476 \r
2477 VOID\r
2478 DrawHighlightsOnDC(HDC hdc)\r
2479 {\r
2480   int i;\r
2481   for (i=0; i<2; i++) {\r
2482     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
2483       DrawHighlightOnDC(hdc, TRUE,\r
2484                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
2485                         HIGHLIGHT_PEN);\r
2486   }\r
2487   for (i=0; i<2; i++) {\r
2488     if (premoveHighlightInfo.sq[i].x >= 0 && \r
2489         premoveHighlightInfo.sq[i].y >= 0) {\r
2490         DrawHighlightOnDC(hdc, TRUE,\r
2491                           premoveHighlightInfo.sq[i].x, \r
2492                           premoveHighlightInfo.sq[i].y,\r
2493                           PREMOVE_PEN);\r
2494     }\r
2495   }\r
2496 }\r
2497 \r
2498 /* Note: sqcolor is used only in monoMode */\r
2499 /* Note that this code is largely duplicated in woptions.c,\r
2500    function DrawSampleSquare, so that needs to be updated too */\r
2501 VOID\r
2502 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2503 {\r
2504   HBITMAP oldBitmap;\r
2505   HBRUSH oldBrush;\r
2506   int tmpSize;\r
2507 \r
2508   if (appData.blindfold) return;\r
2509 \r
2510   /* [AS] Use font-based pieces if needed */\r
2511   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
2512     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2513     CreatePiecesFromFont();\r
2514 \r
2515     if( fontBitmapSquareSize == squareSize ) {\r
2516         int index = TranslatePieceToFontPiece(piece);\r
2517 \r
2518         SelectObject( tmphdc, hPieceMask[ index ] );\r
2519 \r
2520         BitBlt( hdc,\r
2521             x, y,\r
2522             squareSize, squareSize,\r
2523             tmphdc,\r
2524             0, 0,\r
2525             SRCAND );\r
2526 \r
2527         SelectObject( tmphdc, hPieceFace[ index ] );\r
2528 \r
2529         BitBlt( hdc,\r
2530             x, y,\r
2531             squareSize, squareSize,\r
2532             tmphdc,\r
2533             0, 0,\r
2534             SRCPAINT );\r
2535 \r
2536         return;\r
2537     }\r
2538   }\r
2539 \r
2540   if (appData.monoMode) {\r
2541     SelectObject(tmphdc, PieceBitmap(piece, \r
2542       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2543     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2544            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2545   } else {\r
2546     tmpSize = squareSize;\r
2547     if(minorSize &&\r
2548         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2549          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2550       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2551       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2552       x += (squareSize - minorSize)>>1;\r
2553       y += squareSize - minorSize - 2;\r
2554       tmpSize = minorSize;\r
2555     }\r
2556     if (color || appData.allWhite ) {\r
2557       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2558       if( color )\r
2559               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2560       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2561       if(appData.upsideDown && color==flipView)\r
2562         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2563       else\r
2564         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2565       /* Use black for outline of white pieces */\r
2566       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2567       if(appData.upsideDown && color==flipView)\r
2568         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2569       else\r
2570         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2571     } else {\r
2572       /* Use square color for details of black pieces */\r
2573       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2574       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2575       if(appData.upsideDown && !flipView)\r
2576         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2577       else\r
2578         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2579     }\r
2580     SelectObject(hdc, oldBrush);\r
2581     SelectObject(tmphdc, oldBitmap);\r
2582   }\r
2583 }\r
2584 \r
2585 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2586 int GetBackTextureMode( int algo )\r
2587 {\r
2588     int result = BACK_TEXTURE_MODE_DISABLED;\r
2589 \r
2590     switch( algo ) \r
2591     {\r
2592         case BACK_TEXTURE_MODE_PLAIN:\r
2593             result = 1; /* Always use identity map */\r
2594             break;\r
2595         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2596             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2597             break;\r
2598     }\r
2599 \r
2600     return result;\r
2601 }\r
2602 \r
2603 /* \r
2604     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2605     to handle redraws cleanly (as random numbers would always be different).\r
2606 */\r
2607 VOID RebuildTextureSquareInfo()\r
2608 {\r
2609     BITMAP bi;\r
2610     int lite_w = 0;\r
2611     int lite_h = 0;\r
2612     int dark_w = 0;\r
2613     int dark_h = 0;\r
2614     int row;\r
2615     int col;\r
2616 \r
2617     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2618 \r
2619     if( liteBackTexture != NULL ) {\r
2620         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2621             lite_w = bi.bmWidth;\r
2622             lite_h = bi.bmHeight;\r
2623         }\r
2624     }\r
2625 \r
2626     if( darkBackTexture != NULL ) {\r
2627         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2628             dark_w = bi.bmWidth;\r
2629             dark_h = bi.bmHeight;\r
2630         }\r
2631     }\r
2632 \r
2633     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2634         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2635             if( (col + row) & 1 ) {\r
2636                 /* Lite square */\r
2637                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2638                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2639                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2640                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2641                 }\r
2642             }\r
2643             else {\r
2644                 /* Dark square */\r
2645                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2646                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2647                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2648                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2649                 }\r
2650             }\r
2651         }\r
2652     }\r
2653 }\r
2654 \r
2655 /* [AS] Arrow highlighting support */\r
2656 \r
2657 static int A_WIDTH = 5; /* Width of arrow body */\r
2658 \r
2659 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2660 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2661 \r
2662 static double Sqr( double x )\r
2663 {\r
2664     return x*x;\r
2665 }\r
2666 \r
2667 static int Round( double x )\r
2668 {\r
2669     return (int) (x + 0.5);\r
2670 }\r
2671 \r
2672 /* Draw an arrow between two points using current settings */\r
2673 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2674 {\r
2675     POINT arrow[7];\r
2676     double dx, dy, j, k, x, y;\r
2677 \r
2678     if( d_x == s_x ) {\r
2679         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2680 \r
2681         arrow[0].x = s_x + A_WIDTH;\r
2682         arrow[0].y = s_y;\r
2683 \r
2684         arrow[1].x = s_x + A_WIDTH;\r
2685         arrow[1].y = d_y - h;\r
2686 \r
2687         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2688         arrow[2].y = d_y - h;\r
2689 \r
2690         arrow[3].x = d_x;\r
2691         arrow[3].y = d_y;\r
2692 \r
2693         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2694         arrow[4].y = d_y - h;\r
2695 \r
2696         arrow[5].x = s_x - A_WIDTH;\r
2697         arrow[5].y = d_y - h;\r
2698 \r
2699         arrow[6].x = s_x - A_WIDTH;\r
2700         arrow[6].y = s_y;\r
2701     }\r
2702     else if( d_y == s_y ) {\r
2703         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2704 \r
2705         arrow[0].x = s_x;\r
2706         arrow[0].y = s_y + A_WIDTH;\r
2707 \r
2708         arrow[1].x = d_x - w;\r
2709         arrow[1].y = s_y + A_WIDTH;\r
2710 \r
2711         arrow[2].x = d_x - w;\r
2712         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
2713 \r
2714         arrow[3].x = d_x;\r
2715         arrow[3].y = d_y;\r
2716 \r
2717         arrow[4].x = d_x - w;\r
2718         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
2719 \r
2720         arrow[5].x = d_x - w;\r
2721         arrow[5].y = s_y - A_WIDTH;\r
2722 \r
2723         arrow[6].x = s_x;\r
2724         arrow[6].y = s_y - A_WIDTH;\r
2725     }\r
2726     else {\r
2727         /* [AS] Needed a lot of paper for this! :-) */\r
2728         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
2729         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
2730   \r
2731         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
2732 \r
2733         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
2734 \r
2735         x = s_x;\r
2736         y = s_y;\r
2737 \r
2738         arrow[0].x = Round(x - j);\r
2739         arrow[0].y = Round(y + j*dx);\r
2740 \r
2741         arrow[1].x = Round(x + j);\r
2742         arrow[1].y = Round(y - j*dx);\r
2743 \r
2744         if( d_x > s_x ) {\r
2745             x = (double) d_x - k;\r
2746             y = (double) d_y - k*dy;\r
2747         }\r
2748         else {\r
2749             x = (double) d_x + k;\r
2750             y = (double) d_y + k*dy;\r
2751         }\r
2752 \r
2753         arrow[2].x = Round(x + j);\r
2754         arrow[2].y = Round(y - j*dx);\r
2755 \r
2756         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
2757         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
2758 \r
2759         arrow[4].x = d_x;\r
2760         arrow[4].y = d_y;\r
2761 \r
2762         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
2763         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
2764 \r
2765         arrow[6].x = Round(x - j);\r
2766         arrow[6].y = Round(y + j*dx);\r
2767     }\r
2768 \r
2769     Polygon( hdc, arrow, 7 );\r
2770 }\r
2771 \r
2772 /* [AS] Draw an arrow between two squares */\r
2773 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
2774 {\r
2775     int s_x, s_y, d_x, d_y;\r
2776     HPEN hpen;\r
2777     HPEN holdpen;\r
2778     HBRUSH hbrush;\r
2779     HBRUSH holdbrush;\r
2780     LOGBRUSH stLB;\r
2781 \r
2782     if( s_col == d_col && s_row == d_row ) {\r
2783         return;\r
2784     }\r
2785 \r
2786     /* Get source and destination points */\r
2787     SquareToPos( s_row, s_col, &s_x, &s_y);\r
2788     SquareToPos( d_row, d_col, &d_x, &d_y);\r
2789 \r
2790     if( d_y > s_y ) {\r
2791         d_y += squareSize / 4;\r
2792     }\r
2793     else if( d_y < s_y ) {\r
2794         d_y += 3 * squareSize / 4;\r
2795     }\r
2796     else {\r
2797         d_y += squareSize / 2;\r
2798     }\r
2799 \r
2800     if( d_x > s_x ) {\r
2801         d_x += squareSize / 4;\r
2802     }\r
2803     else if( d_x < s_x ) {\r
2804         d_x += 3 * squareSize / 4;\r
2805     }\r
2806     else {\r
2807         d_x += squareSize / 2;\r
2808     }\r
2809 \r
2810     s_x += squareSize / 2;\r
2811     s_y += squareSize / 2;\r
2812 \r
2813     /* Adjust width */\r
2814     A_WIDTH = squareSize / 14;\r
2815 \r
2816     /* Draw */\r
2817     stLB.lbStyle = BS_SOLID;\r
2818     stLB.lbColor = appData.highlightArrowColor;\r
2819     stLB.lbHatch = 0;\r
2820 \r
2821     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
2822     holdpen = SelectObject( hdc, hpen );\r
2823     hbrush = CreateBrushIndirect( &stLB );\r
2824     holdbrush = SelectObject( hdc, hbrush );\r
2825 \r
2826     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
2827 \r
2828     SelectObject( hdc, holdpen );\r
2829     SelectObject( hdc, holdbrush );\r
2830     DeleteObject( hpen );\r
2831     DeleteObject( hbrush );\r
2832 }\r
2833 \r
2834 BOOL HasHighlightInfo()\r
2835 {\r
2836     BOOL result = FALSE;\r
2837 \r
2838     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
2839         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
2840     {\r
2841         result = TRUE;\r
2842     }\r
2843 \r
2844     return result;\r
2845 }\r
2846 \r
2847 BOOL IsDrawArrowEnabled()\r
2848 {\r
2849     BOOL result = FALSE;\r
2850 \r
2851     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
2852         result = TRUE;\r
2853     }\r
2854 \r
2855     return result;\r
2856 }\r
2857 \r
2858 VOID DrawArrowHighlight( HDC hdc )\r
2859 {\r
2860     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
2861         DrawArrowBetweenSquares( hdc,\r
2862             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
2863             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
2864     }\r
2865 }\r
2866 \r
2867 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
2868 {\r
2869     HRGN result = NULL;\r
2870 \r
2871     if( HasHighlightInfo() ) {\r
2872         int x1, y1, x2, y2;\r
2873         int sx, sy, dx, dy;\r
2874 \r
2875         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
2876         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
2877 \r
2878         sx = MIN( x1, x2 );\r
2879         sy = MIN( y1, y2 );\r
2880         dx = MAX( x1, x2 ) + squareSize;\r
2881         dy = MAX( y1, y2 ) + squareSize;\r
2882 \r
2883         result = CreateRectRgn( sx, sy, dx, dy );\r
2884     }\r
2885 \r
2886     return result;\r
2887 }\r
2888 \r
2889 /*\r
2890     Warning: this function modifies the behavior of several other functions. \r
2891     \r
2892     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
2893     needed. Unfortunately, the decision whether or not to perform a full or partial\r
2894     repaint is scattered all over the place, which is not good for features such as\r
2895     "arrow highlighting" that require a full repaint of the board.\r
2896 \r
2897     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
2898     user interaction, when speed is not so important) but especially to avoid errors\r
2899     in the displayed graphics.\r
2900 \r
2901     In such patched places, I always try refer to this function so there is a single\r
2902     place to maintain knowledge.\r
2903     \r
2904     To restore the original behavior, just return FALSE unconditionally.\r
2905 */\r
2906 BOOL IsFullRepaintPreferrable()\r
2907 {\r
2908     BOOL result = FALSE;\r
2909 \r
2910     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
2911         /* Arrow may appear on the board */\r
2912         result = TRUE;\r
2913     }\r
2914 \r
2915     return result;\r
2916 }\r
2917 \r
2918 /* \r
2919     This function is called by DrawPosition to know whether a full repaint must\r
2920     be forced or not.\r
2921 \r
2922     Only DrawPosition may directly call this function, which makes use of \r
2923     some state information. Other function should call DrawPosition specifying \r
2924     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
2925 */\r
2926 BOOL DrawPositionNeedsFullRepaint()\r
2927 {\r
2928     BOOL result = FALSE;\r
2929 \r
2930     /* \r
2931         Probably a slightly better policy would be to trigger a full repaint\r
2932         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
2933         but animation is fast enough that it's difficult to notice.\r
2934     */\r
2935     if( animInfo.piece == EmptySquare ) {\r
2936         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
2937             result = TRUE;\r
2938         }\r
2939     }\r
2940 \r
2941     return result;\r
2942 }\r
2943 \r
2944 VOID\r
2945 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2946 {\r
2947   int row, column, x, y, square_color, piece_color;\r
2948   ChessSquare piece;\r
2949   HBRUSH oldBrush;\r
2950   HDC texture_hdc = NULL;\r
2951 \r
2952   /* [AS] Initialize background textures if needed */\r
2953   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
2954       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
2955       if( backTextureSquareSize != squareSize \r
2956        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
2957           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
2958           backTextureSquareSize = squareSize;\r
2959           RebuildTextureSquareInfo();\r
2960       }\r
2961 \r
2962       texture_hdc = CreateCompatibleDC( hdc );\r
2963   }\r
2964 \r
2965   for (row = 0; row < BOARD_HEIGHT; row++) {\r
2966     for (column = 0; column < BOARD_WIDTH; column++) {\r
2967   \r
2968       SquareToPos(row, column, &x, &y);\r
2969 \r
2970       piece = board[row][column];\r
2971 \r
2972       square_color = ((column + row) % 2) == 1;\r
2973       if( gameInfo.variant == VariantXiangqi ) {\r
2974           square_color = !InPalace(row, column);\r
2975           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
2976           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
2977       }\r
2978       piece_color = (int) piece < (int) BlackPawn;\r
2979 \r
2980 \r
2981       /* [HGM] holdings file: light square or black */\r
2982       if(column == BOARD_LEFT-2) {\r
2983             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
2984                 square_color = 1;\r
2985             else {\r
2986                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
2987                 continue;\r
2988             }\r
2989       } else\r
2990       if(column == BOARD_RGHT + 1 ) {\r
2991             if( row < gameInfo.holdingsSize )\r
2992                 square_color = 1;\r
2993             else {\r
2994                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
2995                 continue;\r
2996             }\r
2997       }\r
2998       if(column == BOARD_LEFT-1 ) /* left align */\r
2999             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3000       else if( column == BOARD_RGHT) /* right align */\r
3001             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3002       else\r
3003       if (appData.monoMode) {\r
3004         if (piece == EmptySquare) {\r
3005           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3006                  square_color ? WHITENESS : BLACKNESS);\r
3007         } else {\r
3008           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3009         }\r
3010       } \r
3011       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3012           /* [AS] Draw the square using a texture bitmap */\r
3013           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3014           int r = row, c = column; // [HGM] do not flip board in flipView\r
3015           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3016 \r
3017           DrawTile( x, y, \r
3018               squareSize, squareSize, \r
3019               hdc, \r
3020               texture_hdc,\r
3021               backTextureSquareInfo[r][c].mode,\r
3022               backTextureSquareInfo[r][c].x,\r
3023               backTextureSquareInfo[r][c].y );\r
3024 \r
3025           SelectObject( texture_hdc, hbm );\r
3026 \r
3027           if (piece != EmptySquare) {\r
3028               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3029           }\r
3030       }\r
3031       else {\r
3032         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3033 \r
3034         oldBrush = SelectObject(hdc, brush );\r
3035         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3036         SelectObject(hdc, oldBrush);\r
3037         if (piece != EmptySquare)\r
3038           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3039       }\r
3040     }\r
3041   }\r
3042 \r
3043   if( texture_hdc != NULL ) {\r
3044     DeleteDC( texture_hdc );\r
3045   }\r
3046 }\r
3047 \r
3048 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3049 void fputDW(FILE *f, int x)\r
3050 {\r
3051         fputc(x     & 255, f);\r
3052         fputc(x>>8  & 255, f);\r
3053         fputc(x>>16 & 255, f);\r
3054         fputc(x>>24 & 255, f);\r
3055 }\r
3056 \r
3057 #define MAX_CLIPS 200   /* more than enough */\r
3058 \r
3059 VOID\r
3060 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3061 {\r
3062 //  HBITMAP bufferBitmap;\r
3063   BITMAP bi;\r
3064 //  RECT Rect;\r
3065   HDC tmphdc;\r
3066   HBITMAP hbm;\r
3067   int w = 100, h = 50;\r
3068 \r
3069   if(logo == NULL) return;\r
3070 //  GetClientRect(hwndMain, &Rect);\r
3071 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3072 //                                      Rect.bottom-Rect.top+1);\r
3073   tmphdc = CreateCompatibleDC(hdc);\r
3074   hbm = SelectObject(tmphdc, logo);\r
3075   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3076             w = bi.bmWidth;\r
3077             h = bi.bmHeight;\r
3078   }\r
3079   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3080                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3081   SelectObject(tmphdc, hbm);\r
3082   DeleteDC(tmphdc);\r
3083 }\r
3084 \r
3085 VOID\r
3086 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3087 {\r
3088   static Board lastReq, lastDrawn;\r
3089   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3090   static int lastDrawnFlipView = 0;\r
3091   static int lastReqValid = 0, lastDrawnValid = 0;\r
3092   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3093   HDC tmphdc;\r
3094   HDC hdcmem;\r
3095   HBITMAP bufferBitmap;\r
3096   HBITMAP oldBitmap;\r
3097   RECT Rect;\r
3098   HRGN clips[MAX_CLIPS];\r
3099   ChessSquare dragged_piece = EmptySquare;\r
3100 \r
3101   /* I'm undecided on this - this function figures out whether a full\r
3102    * repaint is necessary on its own, so there's no real reason to have the\r
3103    * caller tell it that.  I think this can safely be set to FALSE - but\r
3104    * if we trust the callers not to request full repaints unnessesarily, then\r
3105    * we could skip some clipping work.  In other words, only request a full\r
3106    * redraw when the majority of pieces have changed positions (ie. flip, \r
3107    * gamestart and similar)  --Hawk\r
3108    */\r
3109   Boolean fullrepaint = repaint;\r
3110 \r
3111   if( DrawPositionNeedsFullRepaint() ) {\r
3112       fullrepaint = TRUE;\r
3113   }\r
3114 \r
3115   if (board == NULL) {\r
3116     if (!lastReqValid) {\r
3117       return;\r
3118     }\r
3119     board = lastReq;\r
3120   } else {\r
3121     CopyBoard(lastReq, board);\r
3122     lastReqValid = 1;\r
3123   }\r
3124 \r
3125   if (doingSizing) {\r
3126     return;\r
3127   }\r
3128 \r
3129   if (IsIconic(hwndMain)) {\r
3130     return;\r
3131   }\r
3132 \r
3133   if (hdc == NULL) {\r
3134     hdc = GetDC(hwndMain);\r
3135     if (!appData.monoMode) {\r
3136       SelectPalette(hdc, hPal, FALSE);\r
3137       RealizePalette(hdc);\r
3138     }\r
3139     releaseDC = TRUE;\r
3140   } else {\r
3141     releaseDC = FALSE;\r
3142   }\r
3143 \r
3144   /* Create some work-DCs */\r
3145   hdcmem = CreateCompatibleDC(hdc);\r
3146   tmphdc = CreateCompatibleDC(hdc);\r
3147 \r
3148   /* If dragging is in progress, we temporarely remove the piece */\r
3149   /* [HGM] or temporarily decrease count if stacked              */\r
3150   /*       !! Moved to before board compare !!                   */\r
3151   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3152     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3153     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3154             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3155         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3156     } else \r
3157     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3158             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3159         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3160     } else \r
3161         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3162   }\r
3163 \r
3164   /* Figure out which squares need updating by comparing the \r
3165    * newest board with the last drawn board and checking if\r
3166    * flipping has changed.\r
3167    */\r
3168   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3169     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3170       for (column = 0; column < BOARD_WIDTH; column++) {\r
3171         if (lastDrawn[row][column] != board[row][column]) {\r
3172           SquareToPos(row, column, &x, &y);\r
3173           clips[num_clips++] =\r
3174             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3175         }\r
3176       }\r
3177     }\r
3178     for (i=0; i<2; i++) {\r
3179       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3180           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3181         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3182             lastDrawnHighlight.sq[i].y >= 0) {\r
3183           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3184                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3185           clips[num_clips++] =\r
3186             CreateRectRgn(x - lineGap, y - lineGap, \r
3187                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3188         }\r
3189         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3190           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3191           clips[num_clips++] =\r
3192             CreateRectRgn(x - lineGap, y - lineGap, \r
3193                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3194         }\r
3195       }\r
3196     }\r
3197     for (i=0; i<2; i++) {\r
3198       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3199           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3200         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3201             lastDrawnPremove.sq[i].y >= 0) {\r
3202           SquareToPos(lastDrawnPremove.sq[i].y,\r
3203                       lastDrawnPremove.sq[i].x, &x, &y);\r
3204           clips[num_clips++] =\r
3205             CreateRectRgn(x - lineGap, y - lineGap, \r
3206                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3207         }\r
3208         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3209             premoveHighlightInfo.sq[i].y >= 0) {\r
3210           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3211                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3212           clips[num_clips++] =\r
3213             CreateRectRgn(x - lineGap, y - lineGap, \r
3214                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3215         }\r
3216       }\r
3217     }\r
3218   } else {\r
3219     fullrepaint = TRUE;\r
3220   }\r
3221 \r