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