e4e8db961a111e5e868d1cae9a879b50bbd2ee25
[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];\r
1226   if(name[0] == '%' && strchr(name+1, '%')) { // [HGM] recognize %*% as environment variable\r
1227     strcpy(buf, name+1);\r
1228     *strchr(buf, '%') = 0;\r
1229     installDir = getenv(buf);\r
1230     sprintf(fullname, "%s\\%s", installDir, strchr(name+1, '%')+1);\r
1231     return strlen(fullname);\r
1232   }\r
1233   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1234 }\r
1235 \r
1236 int\r
1237 MyGetFullPathName(char *name, char *fullname)\r
1238 {\r
1239   char *dummy;\r
1240   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1241 }\r
1242 \r
1243 int\r
1244 MainWindowUp()\r
1245 { // [HGM] args: allows testing if main window is realized from back-end\r
1246   return hwndMain != NULL;\r
1247 }\r
1248 \r
1249 void\r
1250 PopUpStartupDialog()\r
1251 {\r
1252     FARPROC lpProc;\r
1253     \r
1254     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1255     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1256     FreeProcInstance(lpProc);\r
1257 }\r
1258 \r
1259 /*---------------------------------------------------------------------------*\\r
1260  *\r
1261  * GDI board drawing routines\r
1262  *\r
1263 \*---------------------------------------------------------------------------*/\r
1264 \r
1265 /* [AS] Draw square using background texture */\r
1266 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1267 {\r
1268     XFORM   x;\r
1269 \r
1270     if( mode == 0 ) {\r
1271         return; /* Should never happen! */\r
1272     }\r
1273 \r
1274     SetGraphicsMode( dst, GM_ADVANCED );\r
1275 \r
1276     switch( mode ) {\r
1277     case 1:\r
1278         /* Identity */\r
1279         break;\r
1280     case 2:\r
1281         /* X reflection */\r
1282         x.eM11 = -1.0;\r
1283         x.eM12 = 0;\r
1284         x.eM21 = 0;\r
1285         x.eM22 = 1.0;\r
1286         x.eDx = (FLOAT) dw + dx - 1;\r
1287         x.eDy = 0;\r
1288         dx = 0;\r
1289         SetWorldTransform( dst, &x );\r
1290         break;\r
1291     case 3:\r
1292         /* Y reflection */\r
1293         x.eM11 = 1.0;\r
1294         x.eM12 = 0;\r
1295         x.eM21 = 0;\r
1296         x.eM22 = -1.0;\r
1297         x.eDx = 0;\r
1298         x.eDy = (FLOAT) dh + dy - 1;\r
1299         dy = 0;\r
1300         SetWorldTransform( dst, &x );\r
1301         break;\r
1302     case 4:\r
1303         /* X/Y flip */\r
1304         x.eM11 = 0;\r
1305         x.eM12 = 1.0;\r
1306         x.eM21 = 1.0;\r
1307         x.eM22 = 0;\r
1308         x.eDx = (FLOAT) dx;\r
1309         x.eDy = (FLOAT) dy;\r
1310         dx = 0;\r
1311         dy = 0;\r
1312         SetWorldTransform( dst, &x );\r
1313         break;\r
1314     }\r
1315 \r
1316     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1317 \r
1318     x.eM11 = 1.0;\r
1319     x.eM12 = 0;\r
1320     x.eM21 = 0;\r
1321     x.eM22 = 1.0;\r
1322     x.eDx = 0;\r
1323     x.eDy = 0;\r
1324     SetWorldTransform( dst, &x );\r
1325 \r
1326     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1327 }\r
1328 \r
1329 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1330 enum {\r
1331     PM_WP = (int) WhitePawn, \r
1332     PM_WN = (int) WhiteKnight, \r
1333     PM_WB = (int) WhiteBishop, \r
1334     PM_WR = (int) WhiteRook, \r
1335     PM_WQ = (int) WhiteQueen, \r
1336     PM_WF = (int) WhiteFerz, \r
1337     PM_WW = (int) WhiteWazir, \r
1338     PM_WE = (int) WhiteAlfil, \r
1339     PM_WM = (int) WhiteMan, \r
1340     PM_WO = (int) WhiteCannon, \r
1341     PM_WU = (int) WhiteUnicorn, \r
1342     PM_WH = (int) WhiteNightrider, \r
1343     PM_WA = (int) WhiteAngel, \r
1344     PM_WC = (int) WhiteMarshall, \r
1345     PM_WAB = (int) WhiteCardinal, \r
1346     PM_WD = (int) WhiteDragon, \r
1347     PM_WL = (int) WhiteLance, \r
1348     PM_WS = (int) WhiteCobra, \r
1349     PM_WV = (int) WhiteFalcon, \r
1350     PM_WSG = (int) WhiteSilver, \r
1351     PM_WG = (int) WhiteGrasshopper, \r
1352     PM_WK = (int) WhiteKing,\r
1353     PM_BP = (int) BlackPawn, \r
1354     PM_BN = (int) BlackKnight, \r
1355     PM_BB = (int) BlackBishop, \r
1356     PM_BR = (int) BlackRook, \r
1357     PM_BQ = (int) BlackQueen, \r
1358     PM_BF = (int) BlackFerz, \r
1359     PM_BW = (int) BlackWazir, \r
1360     PM_BE = (int) BlackAlfil, \r
1361     PM_BM = (int) BlackMan,\r
1362     PM_BO = (int) BlackCannon, \r
1363     PM_BU = (int) BlackUnicorn, \r
1364     PM_BH = (int) BlackNightrider, \r
1365     PM_BA = (int) BlackAngel, \r
1366     PM_BC = (int) BlackMarshall, \r
1367     PM_BG = (int) BlackGrasshopper, \r
1368     PM_BAB = (int) BlackCardinal,\r
1369     PM_BD = (int) BlackDragon,\r
1370     PM_BL = (int) BlackLance,\r
1371     PM_BS = (int) BlackCobra,\r
1372     PM_BV = (int) BlackFalcon,\r
1373     PM_BSG = (int) BlackSilver,\r
1374     PM_BK = (int) BlackKing\r
1375 };\r
1376 \r
1377 static HFONT hPieceFont = NULL;\r
1378 static HBITMAP hPieceMask[(int) EmptySquare];\r
1379 static HBITMAP hPieceFace[(int) EmptySquare];\r
1380 static int fontBitmapSquareSize = 0;\r
1381 static char pieceToFontChar[(int) EmptySquare] =\r
1382                               { 'p', 'n', 'b', 'r', 'q', \r
1383                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1384                       'k', 'o', 'm', 'v', 't', 'w', \r
1385                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1386                                                               'l' };\r
1387 \r
1388 extern BOOL SetCharTable( char *table, const char * map );\r
1389 /* [HGM] moved to backend.c */\r
1390 \r
1391 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1392 {\r
1393     HBRUSH hbrush;\r
1394     BYTE r1 = GetRValue( color );\r
1395     BYTE g1 = GetGValue( color );\r
1396     BYTE b1 = GetBValue( color );\r
1397     BYTE r2 = r1 / 2;\r
1398     BYTE g2 = g1 / 2;\r
1399     BYTE b2 = b1 / 2;\r
1400     RECT rc;\r
1401 \r
1402     /* Create a uniform background first */\r
1403     hbrush = CreateSolidBrush( color );\r
1404     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1405     FillRect( hdc, &rc, hbrush );\r
1406     DeleteObject( hbrush );\r
1407     \r
1408     if( mode == 1 ) {\r
1409         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1410         int steps = squareSize / 2;\r
1411         int i;\r
1412 \r
1413         for( i=0; i<steps; i++ ) {\r
1414             BYTE r = r1 - (r1-r2) * i / steps;\r
1415             BYTE g = g1 - (g1-g2) * i / steps;\r
1416             BYTE b = b1 - (b1-b2) * i / steps;\r
1417 \r
1418             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1419             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1420             FillRect( hdc, &rc, hbrush );\r
1421             DeleteObject(hbrush);\r
1422         }\r
1423     }\r
1424     else if( mode == 2 ) {\r
1425         /* Diagonal gradient, good more or less for every piece */\r
1426         POINT triangle[3];\r
1427         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1428         HBRUSH hbrush_old;\r
1429         int steps = squareSize;\r
1430         int i;\r
1431 \r
1432         triangle[0].x = squareSize - steps;\r
1433         triangle[0].y = squareSize;\r
1434         triangle[1].x = squareSize;\r
1435         triangle[1].y = squareSize;\r
1436         triangle[2].x = squareSize;\r
1437         triangle[2].y = squareSize - steps;\r
1438 \r
1439         for( i=0; i<steps; i++ ) {\r
1440             BYTE r = r1 - (r1-r2) * i / steps;\r
1441             BYTE g = g1 - (g1-g2) * i / steps;\r
1442             BYTE b = b1 - (b1-b2) * i / steps;\r
1443 \r
1444             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1445             hbrush_old = SelectObject( hdc, hbrush );\r
1446             Polygon( hdc, triangle, 3 );\r
1447             SelectObject( hdc, hbrush_old );\r
1448             DeleteObject(hbrush);\r
1449             triangle[0].x++;\r
1450             triangle[2].y++;\r
1451         }\r
1452 \r
1453         SelectObject( hdc, hpen );\r
1454     }\r
1455 }\r
1456 \r
1457 /*\r
1458     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1459     seems to work ok. The main problem here is to find the "inside" of a chess\r
1460     piece: follow the steps as explained below.\r
1461 */\r
1462 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1463 {\r
1464     HBITMAP hbm;\r
1465     HBITMAP hbm_old;\r
1466     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1467     RECT rc;\r
1468     SIZE sz;\r
1469     POINT pt;\r
1470     int backColor = whitePieceColor; \r
1471     int foreColor = blackPieceColor;\r
1472     \r
1473     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1474         backColor = appData.fontBackColorWhite;\r
1475         foreColor = appData.fontForeColorWhite;\r
1476     }\r
1477     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1478         backColor = appData.fontBackColorBlack;\r
1479         foreColor = appData.fontForeColorBlack;\r
1480     }\r
1481 \r
1482     /* Mask */\r
1483     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1484 \r
1485     hbm_old = SelectObject( hdc, hbm );\r
1486 \r
1487     rc.left = 0;\r
1488     rc.top = 0;\r
1489     rc.right = squareSize;\r
1490     rc.bottom = squareSize;\r
1491 \r
1492     /* Step 1: background is now black */\r
1493     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1494 \r
1495     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1496 \r
1497     pt.x = (squareSize - sz.cx) / 2;\r
1498     pt.y = (squareSize - sz.cy) / 2;\r
1499 \r
1500     SetBkMode( hdc, TRANSPARENT );\r
1501     SetTextColor( hdc, chroma );\r
1502     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1503     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1504 \r
1505     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1506     /* Step 3: the area outside the piece is filled with white */\r
1507 //    FloodFill( hdc, 0, 0, chroma );\r
1508     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1509     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1510     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1511     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1512     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1513     /* \r
1514         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1515         but if the start point is not inside the piece we're lost!\r
1516         There should be a better way to do this... if we could create a region or path\r
1517         from the fill operation we would be fine for example.\r
1518     */\r
1519 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1520     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1521 \r
1522     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1523         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1524         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1525 \r
1526         SelectObject( dc2, bm2 );\r
1527         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1528         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1529         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1530         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1531         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1532 \r
1533         DeleteDC( dc2 );\r
1534         DeleteObject( bm2 );\r
1535     }\r
1536 \r
1537     SetTextColor( hdc, 0 );\r
1538     /* \r
1539         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1540         draw the piece again in black for safety.\r
1541     */\r
1542     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1543 \r
1544     SelectObject( hdc, hbm_old );\r
1545 \r
1546     if( hPieceMask[index] != NULL ) {\r
1547         DeleteObject( hPieceMask[index] );\r
1548     }\r
1549 \r
1550     hPieceMask[index] = hbm;\r
1551 \r
1552     /* Face */\r
1553     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1554 \r
1555     SelectObject( hdc, hbm );\r
1556 \r
1557     {\r
1558         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1559         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1560         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1561 \r
1562         SelectObject( dc1, hPieceMask[index] );\r
1563         SelectObject( dc2, bm2 );\r
1564         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1565         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1566         \r
1567         /* \r
1568             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1569             the piece background and deletes (makes transparent) the rest.\r
1570             Thanks to that mask, we are free to paint the background with the greates\r
1571             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1572             We use this, to make gradients and give the pieces a "roundish" look.\r
1573         */\r
1574         SetPieceBackground( hdc, backColor, 2 );\r
1575         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1576 \r
1577         DeleteDC( dc2 );\r
1578         DeleteDC( dc1 );\r
1579         DeleteObject( bm2 );\r
1580     }\r
1581 \r
1582     SetTextColor( hdc, foreColor );\r
1583     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1584 \r
1585     SelectObject( hdc, hbm_old );\r
1586 \r
1587     if( hPieceFace[index] != NULL ) {\r
1588         DeleteObject( hPieceFace[index] );\r
1589     }\r
1590 \r
1591     hPieceFace[index] = hbm;\r
1592 }\r
1593 \r
1594 static int TranslatePieceToFontPiece( int piece )\r
1595 {\r
1596     switch( piece ) {\r
1597     case BlackPawn:\r
1598         return PM_BP;\r
1599     case BlackKnight:\r
1600         return PM_BN;\r
1601     case BlackBishop:\r
1602         return PM_BB;\r
1603     case BlackRook:\r
1604         return PM_BR;\r
1605     case BlackQueen:\r
1606         return PM_BQ;\r
1607     case BlackKing:\r
1608         return PM_BK;\r
1609     case WhitePawn:\r
1610         return PM_WP;\r
1611     case WhiteKnight:\r
1612         return PM_WN;\r
1613     case WhiteBishop:\r
1614         return PM_WB;\r
1615     case WhiteRook:\r
1616         return PM_WR;\r
1617     case WhiteQueen:\r
1618         return PM_WQ;\r
1619     case WhiteKing:\r
1620         return PM_WK;\r
1621 \r
1622     case BlackAngel:\r
1623         return PM_BA;\r
1624     case BlackMarshall:\r
1625         return PM_BC;\r
1626     case BlackFerz:\r
1627         return PM_BF;\r
1628     case BlackNightrider:\r
1629         return PM_BH;\r
1630     case BlackAlfil:\r
1631         return PM_BE;\r
1632     case BlackWazir:\r
1633         return PM_BW;\r
1634     case BlackUnicorn:\r
1635         return PM_BU;\r
1636     case BlackCannon:\r
1637         return PM_BO;\r
1638     case BlackGrasshopper:\r
1639         return PM_BG;\r
1640     case BlackMan:\r
1641         return PM_BM;\r
1642     case BlackSilver:\r
1643         return PM_BSG;\r
1644     case BlackLance:\r
1645         return PM_BL;\r
1646     case BlackFalcon:\r
1647         return PM_BV;\r
1648     case BlackCobra:\r
1649         return PM_BS;\r
1650     case BlackCardinal:\r
1651         return PM_BAB;\r
1652     case BlackDragon:\r
1653         return PM_BD;\r
1654 \r
1655     case WhiteAngel:\r
1656         return PM_WA;\r
1657     case WhiteMarshall:\r
1658         return PM_WC;\r
1659     case WhiteFerz:\r
1660         return PM_WF;\r
1661     case WhiteNightrider:\r
1662         return PM_WH;\r
1663     case WhiteAlfil:\r
1664         return PM_WE;\r
1665     case WhiteWazir:\r
1666         return PM_WW;\r
1667     case WhiteUnicorn:\r
1668         return PM_WU;\r
1669     case WhiteCannon:\r
1670         return PM_WO;\r
1671     case WhiteGrasshopper:\r
1672         return PM_WG;\r
1673     case WhiteMan:\r
1674         return PM_WM;\r
1675     case WhiteSilver:\r
1676         return PM_WSG;\r
1677     case WhiteLance:\r
1678         return PM_WL;\r
1679     case WhiteFalcon:\r
1680         return PM_WV;\r
1681     case WhiteCobra:\r
1682         return PM_WS;\r
1683     case WhiteCardinal:\r
1684         return PM_WAB;\r
1685     case WhiteDragon:\r
1686         return PM_WD;\r
1687     }\r
1688 \r
1689     return 0;\r
1690 }\r
1691 \r
1692 void CreatePiecesFromFont()\r
1693 {\r
1694     LOGFONT lf;\r
1695     HDC hdc_window = NULL;\r
1696     HDC hdc = NULL;\r
1697     HFONT hfont_old;\r
1698     int fontHeight;\r
1699     int i;\r
1700 \r
1701     if( fontBitmapSquareSize < 0 ) {\r
1702         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
1703         return;\r
1704     }\r
1705 \r
1706     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
1707         fontBitmapSquareSize = -1;\r
1708         return;\r
1709     }\r
1710 \r
1711     if( fontBitmapSquareSize != squareSize ) {\r
1712         hdc_window = GetDC( hwndMain );\r
1713         hdc = CreateCompatibleDC( hdc_window );\r
1714 \r
1715         if( hPieceFont != NULL ) {\r
1716             DeleteObject( hPieceFont );\r
1717         }\r
1718         else {\r
1719             for( i=0; i<=(int)BlackKing; i++ ) {\r
1720                 hPieceMask[i] = NULL;\r
1721                 hPieceFace[i] = NULL;\r
1722             }\r
1723         }\r
1724 \r
1725         fontHeight = 75;\r
1726 \r
1727         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
1728             fontHeight = appData.fontPieceSize;\r
1729         }\r
1730 \r
1731         fontHeight = (fontHeight * squareSize) / 100;\r
1732 \r
1733         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
1734         lf.lfWidth = 0;\r
1735         lf.lfEscapement = 0;\r
1736         lf.lfOrientation = 0;\r
1737         lf.lfWeight = FW_NORMAL;\r
1738         lf.lfItalic = 0;\r
1739         lf.lfUnderline = 0;\r
1740         lf.lfStrikeOut = 0;\r
1741         lf.lfCharSet = DEFAULT_CHARSET;\r
1742         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1743         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1744         lf.lfQuality = PROOF_QUALITY;\r
1745         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
1746         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
1747         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
1748 \r
1749         hPieceFont = CreateFontIndirect( &lf );\r
1750 \r
1751         if( hPieceFont == NULL ) {\r
1752             fontBitmapSquareSize = -2;\r
1753         }\r
1754         else {\r
1755             /* Setup font-to-piece character table */\r
1756             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
1757                 /* No (or wrong) global settings, try to detect the font */\r
1758                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
1759                     /* Alpha */\r
1760                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
1761                 }\r
1762                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
1763                     /* DiagramTT* family */\r
1764                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
1765                 }\r
1766                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
1767                     /* Fairy symbols */\r
1768                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
1769                 }\r
1770                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
1771                     /* Good Companion (Some characters get warped as literal :-( */\r
1772                     char s[] = "1cmWG0??S??oYI23wgQU";\r
1773                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
1774                     SetCharTable(pieceToFontChar, s);\r
1775                 }\r
1776                 else {\r
1777                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
1778                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
1779                 }\r
1780             }\r
1781 \r
1782             /* Create bitmaps */\r
1783             hfont_old = SelectObject( hdc, hPieceFont );\r
1784             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
1785                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
1786                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
1787 \r
1788             SelectObject( hdc, hfont_old );\r
1789 \r
1790             fontBitmapSquareSize = squareSize;\r
1791         }\r
1792     }\r
1793 \r
1794     if( hdc != NULL ) {\r
1795         DeleteDC( hdc );\r
1796     }\r
1797 \r
1798     if( hdc_window != NULL ) {\r
1799         ReleaseDC( hwndMain, hdc_window );\r
1800     }\r
1801 }\r
1802 \r
1803 HBITMAP\r
1804 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
1805 {\r
1806   char name[128];\r
1807 \r
1808   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
1809   if (gameInfo.event &&\r
1810       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
1811       strcmp(name, "k80s") == 0) {\r
1812     strcpy(name, "tim");\r
1813   }\r
1814   return LoadBitmap(hinst, name);\r
1815 }\r
1816 \r
1817 \r
1818 /* Insert a color into the program's logical palette\r
1819    structure.  This code assumes the given color is\r
1820    the result of the RGB or PALETTERGB macro, and it\r
1821    knows how those macros work (which is documented).\r
1822 */\r
1823 VOID\r
1824 InsertInPalette(COLORREF color)\r
1825 {\r
1826   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
1827 \r
1828   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
1829     DisplayFatalError("Too many colors", 0, 1);\r
1830     pLogPal->palNumEntries--;\r
1831     return;\r
1832   }\r
1833 \r
1834   pe->peFlags = (char) 0;\r
1835   pe->peRed = (char) (0xFF & color);\r
1836   pe->peGreen = (char) (0xFF & (color >> 8));\r
1837   pe->peBlue = (char) (0xFF & (color >> 16));\r
1838   return;\r
1839 }\r
1840 \r
1841 \r
1842 VOID\r
1843 InitDrawingColors()\r
1844 {\r
1845   if (pLogPal == NULL) {\r
1846     /* Allocate enough memory for a logical palette with\r
1847      * PALETTESIZE entries and set the size and version fields\r
1848      * of the logical palette structure.\r
1849      */\r
1850     pLogPal = (NPLOGPALETTE)\r
1851       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
1852                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
1853     pLogPal->palVersion    = 0x300;\r
1854   }\r
1855   pLogPal->palNumEntries = 0;\r
1856 \r
1857   InsertInPalette(lightSquareColor);\r
1858   InsertInPalette(darkSquareColor);\r
1859   InsertInPalette(whitePieceColor);\r
1860   InsertInPalette(blackPieceColor);\r
1861   InsertInPalette(highlightSquareColor);\r
1862   InsertInPalette(premoveHighlightColor);\r
1863 \r
1864   /*  create a logical color palette according the information\r
1865    *  in the LOGPALETTE structure.\r
1866    */\r
1867   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
1868 \r
1869   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
1870   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
1871   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
1872   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
1873   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
1874   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
1875   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
1876   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
1877   /* [AS] Force rendering of the font-based pieces */\r
1878   if( fontBitmapSquareSize > 0 ) {\r
1879     fontBitmapSquareSize = 0;\r
1880   }\r
1881 }\r
1882 \r
1883 \r
1884 int\r
1885 BoardWidth(int boardSize, int n)\r
1886 { /* [HGM] argument n added to allow different width and height */\r
1887   int lineGap = sizeInfo[boardSize].lineGap;\r
1888 \r
1889   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1890       lineGap = appData.overrideLineGap;\r
1891   }\r
1892 \r
1893   return (n + 1) * lineGap +\r
1894           n * sizeInfo[boardSize].squareSize;\r
1895 }\r
1896 \r
1897 /* Respond to board resize by dragging edge */\r
1898 VOID\r
1899 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
1900 {\r
1901   BoardSize newSize = NUM_SIZES - 1;\r
1902   static int recurse = 0;\r
1903   if (IsIconic(hwndMain)) return;\r
1904   if (recurse > 0) return;\r
1905   recurse++;\r
1906   while (newSize > 0) {\r
1907         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
1908         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
1909            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
1910     newSize--;\r
1911   } \r
1912   boardSize = newSize;\r
1913   InitDrawingSizes(boardSize, flags);\r
1914   recurse--;\r
1915 }\r
1916 \r
1917 \r
1918 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
1919 \r
1920 VOID\r
1921 InitDrawingSizes(BoardSize boardSize, int flags)\r
1922 {\r
1923   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
1924   ChessSquare piece;\r
1925   static int oldBoardSize = -1, oldTinyLayout = 0;\r
1926   HDC hdc;\r
1927   SIZE clockSize, messageSize;\r
1928   HFONT oldFont;\r
1929   char buf[MSG_SIZ];\r
1930   char *str;\r
1931   HMENU hmenu = GetMenu(hwndMain);\r
1932   RECT crect, wrect, oldRect;\r
1933   int offby;\r
1934   LOGBRUSH logbrush;\r
1935 \r
1936   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
1937   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
1938 \r
1939   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
1940   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
1941 \r
1942   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
1943   oldRect.top = wpMain.y;\r
1944   oldRect.right = wpMain.x + wpMain.width;\r
1945   oldRect.bottom = wpMain.y + wpMain.height;\r
1946 \r
1947   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
1948   smallLayout = sizeInfo[boardSize].smallLayout;\r
1949   squareSize = sizeInfo[boardSize].squareSize;\r
1950   lineGap = sizeInfo[boardSize].lineGap;\r
1951   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
1952 \r
1953   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
1954       lineGap = appData.overrideLineGap;\r
1955   }\r
1956 \r
1957   if (tinyLayout != oldTinyLayout) {\r
1958     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
1959     if (tinyLayout) {\r
1960       style &= ~WS_SYSMENU;\r
1961       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
1962                  "&Minimize\tCtrl+F4");\r
1963     } else {\r
1964       style |= WS_SYSMENU;\r
1965       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
1966     }\r
1967     SetWindowLong(hwndMain, GWL_STYLE, style);\r
1968 \r
1969     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
1970       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
1971         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
1972     }\r
1973     DrawMenuBar(hwndMain);\r
1974   }\r
1975 \r
1976   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
1977   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
1978 \r
1979   /* Get text area sizes */\r
1980   hdc = GetDC(hwndMain);\r
1981   if (appData.clockMode) {\r
1982     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
1983   } else {\r
1984     sprintf(buf, "White");\r
1985   }\r
1986   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
1987   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
1988   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
1989   str = "We only care about the height here";\r
1990   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
1991   SelectObject(hdc, oldFont);\r
1992   ReleaseDC(hwndMain, hdc);\r
1993 \r
1994   /* Compute where everything goes */\r
1995   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
1996         /* [HGM] logo: if either logo is on, reserve space for it */\r
1997         logoHeight =  2*clockSize.cy;\r
1998         leftLogoRect.left   = OUTER_MARGIN;\r
1999         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2000         leftLogoRect.top    = OUTER_MARGIN;\r
2001         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2002 \r
2003         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2004         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2005         rightLogoRect.top    = OUTER_MARGIN;\r
2006         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2007 \r
2008 \r
2009     whiteRect.left = leftLogoRect.right;\r
2010     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2011     whiteRect.top = OUTER_MARGIN;\r
2012     whiteRect.bottom = whiteRect.top + logoHeight;\r
2013 \r
2014     blackRect.right = rightLogoRect.left;\r
2015     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2016     blackRect.top = whiteRect.top;\r
2017     blackRect.bottom = whiteRect.bottom;\r
2018   } else {\r
2019     whiteRect.left = OUTER_MARGIN;\r
2020     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2021     whiteRect.top = OUTER_MARGIN;\r
2022     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2023 \r
2024     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2025     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2026     blackRect.top = whiteRect.top;\r
2027     blackRect.bottom = whiteRect.bottom;\r
2028 \r
2029     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2030   }\r
2031 \r
2032   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2033   if (appData.showButtonBar) {\r
2034     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2035       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2036   } else {\r
2037     messageRect.right = OUTER_MARGIN + boardWidth;\r
2038   }\r
2039   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2040   messageRect.bottom = messageRect.top + messageSize.cy;\r
2041 \r
2042   boardRect.left = OUTER_MARGIN;\r
2043   boardRect.right = boardRect.left + boardWidth;\r
2044   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2045   boardRect.bottom = boardRect.top + boardHeight;\r
2046 \r
2047   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2048   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2049   oldBoardSize = boardSize;\r
2050   oldTinyLayout = tinyLayout;\r
2051   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2052   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2053     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2054   winW *= 1 + twoBoards;\r
2055   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2056   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2057   wpMain.height = winH; //       without disturbing window attachments\r
2058   GetWindowRect(hwndMain, &wrect);\r
2059   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2060                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2061 \r
2062   // [HGM] placement: let attached windows follow size change.\r
2063   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2064   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2065   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2066   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2067   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2068 \r
2069   /* compensate if menu bar wrapped */\r
2070   GetClientRect(hwndMain, &crect);\r
2071   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2072   wpMain.height += offby;\r
2073   switch (flags) {\r
2074   case WMSZ_TOPLEFT:\r
2075     SetWindowPos(hwndMain, NULL, \r
2076                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2077                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2078     break;\r
2079 \r
2080   case WMSZ_TOPRIGHT:\r
2081   case WMSZ_TOP:\r
2082     SetWindowPos(hwndMain, NULL, \r
2083                  wrect.left, wrect.bottom - wpMain.height, \r
2084                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2085     break;\r
2086 \r
2087   case WMSZ_BOTTOMLEFT:\r
2088   case WMSZ_LEFT:\r
2089     SetWindowPos(hwndMain, NULL, \r
2090                  wrect.right - wpMain.width, wrect.top, \r
2091                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2092     break;\r
2093 \r
2094   case WMSZ_BOTTOMRIGHT:\r
2095   case WMSZ_BOTTOM:\r
2096   case WMSZ_RIGHT:\r
2097   default:\r
2098     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2099                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2100     break;\r
2101   }\r
2102 \r
2103   hwndPause = NULL;\r
2104   for (i = 0; i < N_BUTTONS; i++) {\r
2105     if (buttonDesc[i].hwnd != NULL) {\r
2106       DestroyWindow(buttonDesc[i].hwnd);\r
2107       buttonDesc[i].hwnd = NULL;\r
2108     }\r
2109     if (appData.showButtonBar) {\r
2110       buttonDesc[i].hwnd =\r
2111         CreateWindow("BUTTON", buttonDesc[i].label,\r
2112                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2113                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2114                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2115                      (HMENU) buttonDesc[i].id,\r
2116                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2117       if (tinyLayout) {\r
2118         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2119                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2120                     MAKELPARAM(FALSE, 0));\r
2121       }\r
2122       if (buttonDesc[i].id == IDM_Pause)\r
2123         hwndPause = buttonDesc[i].hwnd;\r
2124       buttonDesc[i].wndproc = (WNDPROC)\r
2125         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2126     }\r
2127   }\r
2128   if (gridPen != NULL) DeleteObject(gridPen);\r
2129   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2130   if (premovePen != NULL) DeleteObject(premovePen);\r
2131   if (lineGap != 0) {\r
2132     logbrush.lbStyle = BS_SOLID;\r
2133     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2134     gridPen =\r
2135       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2136                    lineGap, &logbrush, 0, NULL);\r
2137     logbrush.lbColor = highlightSquareColor;\r
2138     highlightPen =\r
2139       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2140                    lineGap, &logbrush, 0, NULL);\r
2141 \r
2142     logbrush.lbColor = premoveHighlightColor; \r
2143     premovePen =\r
2144       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2145                    lineGap, &logbrush, 0, NULL);\r
2146 \r
2147     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2148     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2149       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2150       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2151         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2152       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2153         BOARD_WIDTH * (squareSize + lineGap);\r
2154       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2155     }\r
2156     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2157       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2158       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2159         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2160         lineGap / 2 + (i * (squareSize + lineGap));\r
2161       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2162         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2163       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2164     }\r
2165   }\r
2166 \r
2167   /* [HGM] Licensing requirement */\r
2168 #ifdef GOTHIC\r
2169   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2170 #endif\r
2171 #ifdef FALCON\r
2172   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2173 #endif\r
2174   GothicPopUp( "", VariantNormal);\r
2175 \r
2176 \r
2177 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2178 \r
2179   /* Load piece bitmaps for this board size */\r
2180   for (i=0; i<=2; i++) {\r
2181     for (piece = WhitePawn;\r
2182          (int) piece < (int) BlackPawn;\r
2183          piece = (ChessSquare) ((int) piece + 1)) {\r
2184       if (pieceBitmap[i][piece] != NULL)\r
2185         DeleteObject(pieceBitmap[i][piece]);\r
2186     }\r
2187   }\r
2188 \r
2189   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2190   // Orthodox Chess pieces\r
2191   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2192   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2193   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2194   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2195   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2196   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2197   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2198   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2199   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2200   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2201   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2202   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2203   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2204   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2205   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2206   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
2207     // in Shogi, Hijack the unused Queen for Lance\r
2208     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2209     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2210     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2211   } else {\r
2212     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2213     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2214     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2215   }\r
2216 \r
2217   if(squareSize <= 72 && squareSize >= 33) { \r
2218     /* A & C are available in most sizes now */\r
2219     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2220       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2221       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2222       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2223       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2224       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2225       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2226       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2227       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2228       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2229       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2230       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2231       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2232     } else { // Smirf-like\r
2233       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2234       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2235       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2236     }\r
2237     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2238       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2239       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2240       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2241     } else { // WinBoard standard\r
2242       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2243       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2244       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2245     }\r
2246   }\r
2247 \r
2248 \r
2249   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2250     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2251     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2252     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2253     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2254     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2255     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2256     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2257     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2258     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2259     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2260     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2261     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2262     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2263     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2264     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2265     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2266     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2267     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2268     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2269     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2270     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2271     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2272     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2273     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2274     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2275     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2276     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2277     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2278     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2279     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2280 \r
2281     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2282       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2283       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2284       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2285       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2286       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2287       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2288       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2289       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2290       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2291       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2292       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2293       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2294     } else {\r
2295       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2296       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2297       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2298       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2299       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2300       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2301       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2302       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2303       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2304       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2305       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2306       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2307     }\r
2308 \r
2309   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2310     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2311     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2312     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2313     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2314     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2315     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2316     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2317     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2318     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2319     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2320     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2321     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2322     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2323     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2324   }\r
2325 \r
2326 \r
2327   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2328   /* special Shogi support in this size */\r
2329   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2330       for (piece = WhitePawn;\r
2331            (int) piece < (int) BlackPawn;\r
2332            piece = (ChessSquare) ((int) piece + 1)) {\r
2333         if (pieceBitmap[i][piece] != NULL)\r
2334           DeleteObject(pieceBitmap[i][piece]);\r
2335       }\r
2336     }\r
2337   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2338   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2339   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2340   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2341   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2342   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2343   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2344   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2345   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2346   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2347   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2348   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2349   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2350   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2351   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2352   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2353   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2354   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2355   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2356   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2357   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2358   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2359   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2360   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2361   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2362   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2363   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2364   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2365   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2366   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2367   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2368   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2369   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2370   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2371   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2372   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2373   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2374   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2375   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2376   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2377   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2378   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2379   minorSize = 0;\r
2380   }\r
2381 }\r
2382 \r
2383 HBITMAP\r
2384 PieceBitmap(ChessSquare p, int kind)\r
2385 {\r
2386   if ((int) p >= (int) BlackPawn)\r
2387     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2388 \r
2389   return pieceBitmap[kind][(int) p];\r
2390 }\r
2391 \r
2392 /***************************************************************/\r
2393 \r
2394 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2395 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2396 /*\r
2397 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2398 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2399 */\r
2400 \r
2401 VOID\r
2402 SquareToPos(int row, int column, int * x, int * y)\r
2403 {\r
2404   if (flipView) {\r
2405     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2406     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2407   } else {\r
2408     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2409     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2410   }\r
2411 }\r
2412 \r
2413 VOID\r
2414 DrawCoordsOnDC(HDC hdc)\r
2415 {\r
2416   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
2417   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
2418   char str[2] = { NULLCHAR, NULLCHAR };\r
2419   int oldMode, oldAlign, x, y, start, i;\r
2420   HFONT oldFont;\r
2421   HBRUSH oldBrush;\r
2422 \r
2423   if (!appData.showCoords)\r
2424     return;\r
2425 \r
2426   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
2427 \r
2428   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2429   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2430   oldAlign = GetTextAlign(hdc);\r
2431   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2432 \r
2433   y = boardRect.top + lineGap;\r
2434   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2435 \r
2436   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2437   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2438     str[0] = files[start + i];\r
2439     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2440     y += squareSize + lineGap;\r
2441   }\r
2442 \r
2443   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
2444 \r
2445   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2446   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2447     str[0] = ranks[start + i];\r
2448     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2449     x += squareSize + lineGap;\r
2450   }    \r
2451 \r
2452   SelectObject(hdc, oldBrush);\r
2453   SetBkMode(hdc, oldMode);\r
2454   SetTextAlign(hdc, oldAlign);\r
2455   SelectObject(hdc, oldFont);\r
2456 }\r
2457 \r
2458 VOID\r
2459 DrawGridOnDC(HDC hdc)\r
2460 {\r
2461   HPEN oldPen;\r
2462  \r
2463   if (lineGap != 0) {\r
2464     oldPen = SelectObject(hdc, gridPen);\r
2465     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2466     SelectObject(hdc, oldPen);\r
2467   }\r
2468 }\r
2469 \r
2470 #define HIGHLIGHT_PEN 0\r
2471 #define PREMOVE_PEN   1\r
2472 \r
2473 VOID\r
2474 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2475 {\r
2476   int x1, y1;\r
2477   HPEN oldPen, hPen;\r
2478   if (lineGap == 0) return;\r
2479   if (flipView) {\r
2480     x1 = boardRect.left +\r
2481       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2482     y1 = boardRect.top +\r
2483       lineGap/2 + y * (squareSize + lineGap);\r
2484   } else {\r
2485     x1 = boardRect.left +\r
2486       lineGap/2 + x * (squareSize + lineGap);\r
2487     y1 = boardRect.top +\r
2488       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2489   }\r
2490   hPen = pen ? premovePen : highlightPen;\r
2491   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2492   MoveToEx(hdc, x1, y1, NULL);\r
2493   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2494   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2495   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2496   LineTo(hdc, x1, y1);\r
2497   SelectObject(hdc, oldPen);\r
2498 }\r
2499 \r
2500 VOID\r
2501 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2502 {\r
2503   int i;\r
2504   for (i=0; i<2; i++) {\r
2505     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2506       DrawHighlightOnDC(hdc, TRUE,\r
2507                         h->sq[i].x, h->sq[i].y,\r
2508                         pen);\r
2509   }\r
2510 }\r
2511 \r
2512 /* Note: sqcolor is used only in monoMode */\r
2513 /* Note that this code is largely duplicated in woptions.c,\r
2514    function DrawSampleSquare, so that needs to be updated too */\r
2515 VOID\r
2516 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2517 {\r
2518   HBITMAP oldBitmap;\r
2519   HBRUSH oldBrush;\r
2520   int tmpSize;\r
2521 \r
2522   if (appData.blindfold) return;\r
2523 \r
2524   /* [AS] Use font-based pieces if needed */\r
2525   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2526     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2527     CreatePiecesFromFont();\r
2528 \r
2529     if( fontBitmapSquareSize == squareSize ) {\r
2530         int index = TranslatePieceToFontPiece(piece);\r
2531 \r
2532         SelectObject( tmphdc, hPieceMask[ index ] );\r
2533 \r
2534       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2535         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2536       else\r
2537         BitBlt( hdc,\r
2538             x, y,\r
2539             squareSize, squareSize,\r
2540             tmphdc,\r
2541             0, 0,\r
2542             SRCAND );\r
2543 \r
2544         SelectObject( tmphdc, hPieceFace[ index ] );\r
2545 \r
2546       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2547         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2548       else\r
2549         BitBlt( hdc,\r
2550             x, y,\r
2551             squareSize, squareSize,\r
2552             tmphdc,\r
2553             0, 0,\r
2554             SRCPAINT );\r
2555 \r
2556         return;\r
2557     }\r
2558   }\r
2559 \r
2560   if (appData.monoMode) {\r
2561     SelectObject(tmphdc, PieceBitmap(piece, \r
2562       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2563     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2564            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2565   } else {\r
2566     tmpSize = squareSize;\r
2567     if(minorSize &&\r
2568         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2569          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2570       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2571       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2572       x += (squareSize - minorSize)>>1;\r
2573       y += squareSize - minorSize - 2;\r
2574       tmpSize = minorSize;\r
2575     }\r
2576     if (color || appData.allWhite ) {\r
2577       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2578       if( color )\r
2579               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2580       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2581       if(appData.upsideDown && color==flipView)\r
2582         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2583       else\r
2584         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2585       /* Use black for outline of white pieces */\r
2586       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2587       if(appData.upsideDown && color==flipView)\r
2588         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2589       else\r
2590         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2591     } else {\r
2592       /* Use square color for details of black pieces */\r
2593       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2594       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2595       if(appData.upsideDown && !flipView)\r
2596         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2597       else\r
2598         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2599     }\r
2600     SelectObject(hdc, oldBrush);\r
2601     SelectObject(tmphdc, oldBitmap);\r
2602   }\r
2603 }\r
2604 \r
2605 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2606 int GetBackTextureMode( int algo )\r
2607 {\r
2608     int result = BACK_TEXTURE_MODE_DISABLED;\r
2609 \r
2610     switch( algo ) \r
2611     {\r
2612         case BACK_TEXTURE_MODE_PLAIN:\r
2613             result = 1; /* Always use identity map */\r
2614             break;\r
2615         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2616             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2617             break;\r
2618     }\r
2619 \r
2620     return result;\r
2621 }\r
2622 \r
2623 /* \r
2624     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2625     to handle redraws cleanly (as random numbers would always be different).\r
2626 */\r
2627 VOID RebuildTextureSquareInfo()\r
2628 {\r
2629     BITMAP bi;\r
2630     int lite_w = 0;\r
2631     int lite_h = 0;\r
2632     int dark_w = 0;\r
2633     int dark_h = 0;\r
2634     int row;\r
2635     int col;\r
2636 \r
2637     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2638 \r
2639     if( liteBackTexture != NULL ) {\r
2640         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2641             lite_w = bi.bmWidth;\r
2642             lite_h = bi.bmHeight;\r
2643         }\r
2644     }\r
2645 \r
2646     if( darkBackTexture != NULL ) {\r
2647         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2648             dark_w = bi.bmWidth;\r
2649             dark_h = bi.bmHeight;\r
2650         }\r
2651     }\r
2652 \r
2653     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2654         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2655             if( (col + row) & 1 ) {\r
2656                 /* Lite square */\r
2657                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2658                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2659                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2660                   else\r
2661                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2662                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2663                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2664                   else\r
2665                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2666                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2667                 }\r
2668             }\r
2669             else {\r
2670                 /* Dark square */\r
2671                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
2672                   if( dark_w >= squareSize*BOARD_WIDTH )\r
2673                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
2674                   else\r
2675                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
2676                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
2677                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
2678                   else\r
2679                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
2680                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
2681                 }\r
2682             }\r
2683         }\r
2684     }\r
2685 }\r
2686 \r
2687 /* [AS] Arrow highlighting support */\r
2688 \r
2689 static int A_WIDTH = 5; /* Width of arrow body */\r
2690 \r
2691 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
2692 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
2693 \r
2694 static double Sqr( double x )\r
2695 {\r
2696     return x*x;\r
2697 }\r
2698 \r
2699 static int Round( double x )\r
2700 {\r
2701     return (int) (x + 0.5);\r
2702 }\r
2703 \r
2704 /* Draw an arrow between two points using current settings */\r
2705 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
2706 {\r
2707     POINT arrow[7];\r
2708     double dx, dy, j, k, x, y;\r
2709 \r
2710     if( d_x == s_x ) {\r
2711         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2712 \r
2713         arrow[0].x = s_x + A_WIDTH;\r
2714         arrow[0].y = s_y;\r
2715 \r
2716         arrow[1].x = s_x + A_WIDTH;\r
2717         arrow[1].y = d_y - h;\r
2718 \r
2719         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
2720         arrow[2].y = d_y - h;\r
2721 \r
2722         arrow[3].x = d_x;\r
2723         arrow[3].y = d_y;\r
2724 \r
2725         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
2726         arrow[4].y = d_y - h;\r
2727 \r
2728         arrow[5].x = s_x - A_WIDTH;\r
2729         arrow[5].y = d_y - h;\r
2730 \r
2731         arrow[6].x = s_x - A_WIDTH;\r
2732         arrow[6].y = s_y;\r
2733     }\r
2734     else if( d_y == s_y ) {\r
2735         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
2736 \r
2737         arrow[0].x = s_x;\r
2738         arrow[0].y = s_y + A_WIDTH;\r
2739 \r
2740         arrow[1].x = d_x - w;\r
2741         arrow[1].y = s_y + A_WIDTH;\r
2742 \r
2743         arrow[2].x = d_x - w;\r
2744         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
2745 \r
2746         arrow[3].x = d_x;\r
2747         arrow[3].y = d_y;\r
2748 \r
2749         arrow[4].x = d_x - w;\r
2750         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
2751 \r
2752         arrow[5].x = d_x - w;\r
2753         arrow[5].y = s_y - A_WIDTH;\r
2754 \r
2755         arrow[6].x = s_x;\r
2756         arrow[6].y = s_y - A_WIDTH;\r
2757     }\r
2758     else {\r
2759         /* [AS] Needed a lot of paper for this! :-) */\r
2760         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
2761         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
2762   \r
2763         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
2764 \r
2765         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
2766 \r
2767         x = s_x;\r
2768         y = s_y;\r
2769 \r
2770         arrow[0].x = Round(x - j);\r
2771         arrow[0].y = Round(y + j*dx);\r
2772 \r
2773         arrow[1].x = Round(x + j);\r
2774         arrow[1].y = Round(y - j*dx);\r
2775 \r
2776         if( d_x > s_x ) {\r
2777             x = (double) d_x - k;\r
2778             y = (double) d_y - k*dy;\r
2779         }\r
2780         else {\r
2781             x = (double) d_x + k;\r
2782             y = (double) d_y + k*dy;\r
2783         }\r
2784 \r
2785         arrow[2].x = Round(x + j);\r
2786         arrow[2].y = Round(y - j*dx);\r
2787 \r
2788         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
2789         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
2790 \r
2791         arrow[4].x = d_x;\r
2792         arrow[4].y = d_y;\r
2793 \r
2794         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
2795         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
2796 \r
2797         arrow[6].x = Round(x - j);\r
2798         arrow[6].y = Round(y + j*dx);\r
2799     }\r
2800 \r
2801     Polygon( hdc, arrow, 7 );\r
2802 }\r
2803 \r
2804 /* [AS] Draw an arrow between two squares */\r
2805 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
2806 {\r
2807     int s_x, s_y, d_x, d_y;\r
2808     HPEN hpen;\r
2809     HPEN holdpen;\r
2810     HBRUSH hbrush;\r
2811     HBRUSH holdbrush;\r
2812     LOGBRUSH stLB;\r
2813 \r
2814     if( s_col == d_col && s_row == d_row ) {\r
2815         return;\r
2816     }\r
2817 \r
2818     /* Get source and destination points */\r
2819     SquareToPos( s_row, s_col, &s_x, &s_y);\r
2820     SquareToPos( d_row, d_col, &d_x, &d_y);\r
2821 \r
2822     if( d_y > s_y ) {\r
2823         d_y += squareSize / 4;\r
2824     }\r
2825     else if( d_y < s_y ) {\r
2826         d_y += 3 * squareSize / 4;\r
2827     }\r
2828     else {\r
2829         d_y += squareSize / 2;\r
2830     }\r
2831 \r
2832     if( d_x > s_x ) {\r
2833         d_x += squareSize / 4;\r
2834     }\r
2835     else if( d_x < s_x ) {\r
2836         d_x += 3 * squareSize / 4;\r
2837     }\r
2838     else {\r
2839         d_x += squareSize / 2;\r
2840     }\r
2841 \r
2842     s_x += squareSize / 2;\r
2843     s_y += squareSize / 2;\r
2844 \r
2845     /* Adjust width */\r
2846     A_WIDTH = squareSize / 14;\r
2847 \r
2848     /* Draw */\r
2849     stLB.lbStyle = BS_SOLID;\r
2850     stLB.lbColor = appData.highlightArrowColor;\r
2851     stLB.lbHatch = 0;\r
2852 \r
2853     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
2854     holdpen = SelectObject( hdc, hpen );\r
2855     hbrush = CreateBrushIndirect( &stLB );\r
2856     holdbrush = SelectObject( hdc, hbrush );\r
2857 \r
2858     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
2859 \r
2860     SelectObject( hdc, holdpen );\r
2861     SelectObject( hdc, holdbrush );\r
2862     DeleteObject( hpen );\r
2863     DeleteObject( hbrush );\r
2864 }\r
2865 \r
2866 BOOL HasHighlightInfo()\r
2867 {\r
2868     BOOL result = FALSE;\r
2869 \r
2870     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
2871         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
2872     {\r
2873         result = TRUE;\r
2874     }\r
2875 \r
2876     return result;\r
2877 }\r
2878 \r
2879 BOOL IsDrawArrowEnabled()\r
2880 {\r
2881     BOOL result = FALSE;\r
2882 \r
2883     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
2884         result = TRUE;\r
2885     }\r
2886 \r
2887     return result;\r
2888 }\r
2889 \r
2890 VOID DrawArrowHighlight( HDC hdc )\r
2891 {\r
2892     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
2893         DrawArrowBetweenSquares( hdc,\r
2894             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
2895             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
2896     }\r
2897 }\r
2898 \r
2899 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
2900 {\r
2901     HRGN result = NULL;\r
2902 \r
2903     if( HasHighlightInfo() ) {\r
2904         int x1, y1, x2, y2;\r
2905         int sx, sy, dx, dy;\r
2906 \r
2907         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
2908         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
2909 \r
2910         sx = MIN( x1, x2 );\r
2911         sy = MIN( y1, y2 );\r
2912         dx = MAX( x1, x2 ) + squareSize;\r
2913         dy = MAX( y1, y2 ) + squareSize;\r
2914 \r
2915         result = CreateRectRgn( sx, sy, dx, dy );\r
2916     }\r
2917 \r
2918     return result;\r
2919 }\r
2920 \r
2921 /*\r
2922     Warning: this function modifies the behavior of several other functions. \r
2923     \r
2924     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
2925     needed. Unfortunately, the decision whether or not to perform a full or partial\r
2926     repaint is scattered all over the place, which is not good for features such as\r
2927     "arrow highlighting" that require a full repaint of the board.\r
2928 \r
2929     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
2930     user interaction, when speed is not so important) but especially to avoid errors\r
2931     in the displayed graphics.\r
2932 \r
2933     In such patched places, I always try refer to this function so there is a single\r
2934     place to maintain knowledge.\r
2935     \r
2936     To restore the original behavior, just return FALSE unconditionally.\r
2937 */\r
2938 BOOL IsFullRepaintPreferrable()\r
2939 {\r
2940     BOOL result = FALSE;\r
2941 \r
2942     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
2943         /* Arrow may appear on the board */\r
2944         result = TRUE;\r
2945     }\r
2946 \r
2947     return result;\r
2948 }\r
2949 \r
2950 /* \r
2951     This function is called by DrawPosition to know whether a full repaint must\r
2952     be forced or not.\r
2953 \r
2954     Only DrawPosition may directly call this function, which makes use of \r
2955     some state information. Other function should call DrawPosition specifying \r
2956     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
2957 */\r
2958 BOOL DrawPositionNeedsFullRepaint()\r
2959 {\r
2960     BOOL result = FALSE;\r
2961 \r
2962     /* \r
2963         Probably a slightly better policy would be to trigger a full repaint\r
2964         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
2965         but animation is fast enough that it's difficult to notice.\r
2966     */\r
2967     if( animInfo.piece == EmptySquare ) {\r
2968         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
2969             result = TRUE;\r
2970         }\r
2971     }\r
2972 \r
2973     return result;\r
2974 }\r
2975 \r
2976 VOID\r
2977 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2978 {\r
2979   int row, column, x, y, square_color, piece_color;\r
2980   ChessSquare piece;\r
2981   HBRUSH oldBrush;\r
2982   HDC texture_hdc = NULL;\r
2983 \r
2984   /* [AS] Initialize background textures if needed */\r
2985   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
2986       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
2987       if( backTextureSquareSize != squareSize \r
2988        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
2989           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
2990           backTextureSquareSize = squareSize;\r
2991           RebuildTextureSquareInfo();\r
2992       }\r
2993 \r
2994       texture_hdc = CreateCompatibleDC( hdc );\r
2995   }\r
2996 \r
2997   for (row = 0; row < BOARD_HEIGHT; row++) {\r
2998     for (column = 0; column < BOARD_WIDTH; column++) {\r
2999   \r
3000       SquareToPos(row, column, &x, &y);\r
3001 \r
3002       piece = board[row][column];\r
3003 \r
3004       square_color = ((column + row) % 2) == 1;\r
3005       if( gameInfo.variant == VariantXiangqi ) {\r
3006           square_color = !InPalace(row, column);\r
3007           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3008           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3009       }\r
3010       piece_color = (int) piece < (int) BlackPawn;\r
3011 \r
3012 \r
3013       /* [HGM] holdings file: light square or black */\r
3014       if(column == BOARD_LEFT-2) {\r
3015             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3016                 square_color = 1;\r
3017             else {\r
3018                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3019                 continue;\r
3020             }\r
3021       } else\r
3022       if(column == BOARD_RGHT + 1 ) {\r
3023             if( row < gameInfo.holdingsSize )\r
3024                 square_color = 1;\r
3025             else {\r
3026                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3027                 continue;\r
3028             }\r
3029       }\r
3030       if(column == BOARD_LEFT-1 ) /* left align */\r
3031             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3032       else if( column == BOARD_RGHT) /* right align */\r
3033             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3034       else\r
3035       if (appData.monoMode) {\r
3036         if (piece == EmptySquare) {\r
3037           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3038                  square_color ? WHITENESS : BLACKNESS);\r
3039         } else {\r
3040           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3041         }\r
3042       } \r
3043       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3044           /* [AS] Draw the square using a texture bitmap */\r
3045           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3046           int r = row, c = column; // [HGM] do not flip board in flipView\r
3047           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3048 \r
3049           DrawTile( x, y, \r
3050               squareSize, squareSize, \r
3051               hdc, \r
3052               texture_hdc,\r
3053               backTextureSquareInfo[r][c].mode,\r
3054               backTextureSquareInfo[r][c].x,\r
3055               backTextureSquareInfo[r][c].y );\r
3056 \r
3057           SelectObject( texture_hdc, hbm );\r
3058 \r
3059           if (piece != EmptySquare) {\r
3060               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3061           }\r
3062       }\r
3063       else {\r
3064         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3065 \r
3066         oldBrush = SelectObject(hdc, brush );\r
3067         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3068         SelectObject(hdc, oldBrush);\r
3069         if (piece != EmptySquare)\r
3070           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3071       }\r
3072     }\r
3073   }\r
3074 \r
3075   if( texture_hdc != NULL ) {\r
3076     DeleteDC( texture_hdc );\r
3077   }\r
3078 }\r
3079 \r
3080 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3081 void fputDW(FILE *f, int x)\r
3082 {\r
3083         fputc(x     & 255, f);\r
3084         fputc(x>>8  & 255, f);\r
3085         fputc(x>>16 & 255, f);\r
3086         fputc(x>>24 & 255, f);\r
3087 }\r
3088 \r
3089 #define MAX_CLIPS 200   /* more than enough */\r
3090 \r
3091 VOID\r
3092 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3093 {\r
3094 //  HBITMAP bufferBitmap;\r
3095   BITMAP bi;\r
3096 //  RECT Rect;\r
3097   HDC tmphdc;\r
3098   HBITMAP hbm;\r
3099   int w = 100, h = 50;\r
3100 \r
3101   if(logo == NULL) return;\r
3102 //  GetClientRect(hwndMain, &Rect);\r
3103 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3104 //                                      Rect.bottom-Rect.top+1);\r
3105   tmphdc = CreateCompatibleDC(hdc);\r
3106   hbm = SelectObject(tmphdc, logo);\r
3107   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3108             w = bi.bmWidth;\r
3109             h = bi.bmHeight;\r
3110   }\r
3111   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3112                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3113   SelectObject(tmphdc, hbm);\r
3114   DeleteDC(tmphdc);\r
3115 }\r
3116 \r
3117 static HDC hdcSeek;\r
3118 \r
3119 // [HGM] seekgraph\r
3120 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3121 {\r
3122     POINT stPt;\r
3123     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3124     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3125     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3126     SelectObject( hdcSeek, hp );\r
3127 }\r
3128 \r
3129 // front-end wrapper for drawing functions to do rectangles\r
3130 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3131 {\r
3132     HPEN hp;\r
3133     RECT rc;\r
3134 \r
3135     if (hdcSeek == NULL) {\r
3136     hdcSeek = GetDC(hwndMain);\r
3137       if (!appData.monoMode) {\r
3138         SelectPalette(hdcSeek, hPal, FALSE);\r
3139         RealizePalette(hdcSeek);\r
3140       }\r
3141     }\r
3142     hp = SelectObject( hdcSeek, gridPen );\r
3143     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3144     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3145     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3146     SelectObject( hdcSeek, hp );\r
3147 }\r
3148 \r
3149 // front-end wrapper for putting text in graph\r
3150 void DrawSeekText(char *buf, int x, int y)\r
3151 {\r
3152         SIZE stSize;\r
3153         SetBkMode( hdcSeek, TRANSPARENT );\r
3154         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3155         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3156 }\r
3157 \r
3158 void DrawSeekDot(int x, int y, int color)\r
3159 {\r
3160         int square = color & 0x80;\r
3161         color &= 0x7F;\r
3162             HBRUSH oldBrush = SelectObject(hdcSeek, \r
3163                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3164         if(square)\r
3165             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3166                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3167         else\r
3168             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3169                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3170             SelectObject(hdcSeek, oldBrush);\r
3171 }\r
3172 \r
3173 VOID\r
3174 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3175 {\r
3176   static Board lastReq[2], lastDrawn[2];\r
3177   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3178   static int lastDrawnFlipView = 0;\r
3179   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3180   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3181   HDC tmphdc;\r
3182   HDC hdcmem;\r
3183   HBITMAP bufferBitmap;\r
3184   HBITMAP oldBitmap;\r
3185   RECT Rect;\r
3186   HRGN clips[MAX_CLIPS];\r
3187   ChessSquare dragged_piece = EmptySquare;\r
3188   int nr = twoBoards*partnerUp;\r
3189 \r
3190   /* I'm undecided on this - this function figures out whether a full\r
3191    * repaint is necessary on its own, so there's no real reason to have the\r
3192    * caller tell it that.  I think this can safely be set to FALSE - but\r
3193    * if we trust the callers not to request full repaints unnessesarily, then\r
3194    * we could skip some clipping work.  In other words, only request a full\r
3195    * redraw when the majority of pieces have changed positions (ie. flip, \r
3196    * gamestart and similar)  --Hawk\r
3197    */\r
3198   Boolean fullrepaint = repaint;\r
3199 \r
3200   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3201 \r
3202   if( DrawPositionNeedsFullRepaint() ) {\r
3203       fullrepaint = TRUE;\r
3204   }\r
3205 \r
3206   if (board == NULL) {\r
3207     if (!lastReqValid[nr]) {\r
3208       return;\r
3209     }\r
3210     board = lastReq[nr];\r
3211   } else {\r
3212     CopyBoard(lastReq[nr], board);\r
3213     lastReqValid[nr] = 1;\r
3214   }\r
3215 \r
3216   if (doingSizing) {\r
3217     return;\r
3218   }\r
3219 \r
3220   if (IsIconic(hwndMain)) {\r
3221     return;\r
3222   }\r
3223 \r
3224   if (hdc == NULL) {\r
3225     hdc = GetDC(hwndMain);\r
3226     if (!appData.monoMode) {\r
3227       SelectPalette(hdc, hPal, FALSE);\r
3228       RealizePalette(hdc);\r
3229     }\r
3230     releaseDC = TRUE;\r
3231   } else {\r
3232     releaseDC = FALSE;\r
3233   }\r
3234 \r
3235   /* Create some work-DCs */\r
3236   hdcmem = CreateCompatibleDC(hdc);\r
3237   tmphdc = CreateCompatibleDC(hdc);\r
3238 \r
3239   /* If dragging is in progress, we temporarely remove the piece */\r
3240   /* [HGM] or temporarily decrease count if stacked              */\r
3241   /*       !! Moved to before board compare !!                   */\r
3242   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3243     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3244     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3245             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3246         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3247     } else \r
3248     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3249             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3250         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3251     } else \r
3252         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3253   }\r
3254 \r
3255   /* Figure out which squares need updating by comparing the \r
3256    * newest board with the last drawn board and checking if\r
3257    * flipping has changed.\r
3258    */\r
3259   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3260     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3261       for (column = 0; column < BOARD_WIDTH; column++) {\r
3262         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3263           SquareToPos(row, column, &x, &y);\r
3264           clips[num_clips++] =\r
3265             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3266         }\r
3267       }\r
3268     }\r
3269    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3270     for (i=0; i<2; i++) {\r
3271       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3272           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3273         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3274             lastDrawnHighlight.sq[i].y >= 0) {\r
3275           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3276                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3277           clips[num_clips++] =\r
3278             CreateRectRgn(x - lineGap, y - lineGap, \r
3279                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3280         }\r
3281         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3282           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3283           clips[num_clips++] =\r
3284             CreateRectRgn(x - lineGap, y - lineGap, \r
3285                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3286         }\r
3287       }\r
3288     }\r
3289     for (i=0; i<2; i++) {\r
3290       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3291           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3292         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3293             lastDrawnPremove.sq[i].y >= 0) {\r
3294           SquareToPos(lastDrawnPremove.sq[i].y,\r
3295                       lastDrawnPremove.sq[i].x, &x, &y);\r
3296           clips[num_clips++] =\r
3297             CreateRectRgn(x - lineGap, y - lineGap, \r
3298                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3299         }\r
3300         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3301             premoveHighlightInfo.sq[i].y >= 0) {\r
3302           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3303                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3304           clips[num_clips++] =\r
3305             CreateRectRgn(x - lineGap, y - lineGap, \r
3306                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3307         }\r
3308       }\r
3309     }\r
3310    } else { // nr == 1\r
3311         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3312         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3313         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3314         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3315       for (i=0; i<2; i++) {\r
3316         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3317             partnerHighlightInfo.sq[i].y >= 0) {\r
3318           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3319                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3320           clips[num_clips++] =\r
3321             CreateRectRgn(x - lineGap, y - lineGap, \r
3322                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3323         }\r
3324         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3325             oldPartnerHighlight.sq[i].y >= 0) {\r
3326           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3327                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3328           clips[num_clips++] =\r
3329             CreateRectRgn(x - lineGap, y - lineGap, \r
3330                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3331         }\r
3332       }\r
3333    }\r
3334   } else {\r
3335     fullrepaint = TRUE;\r
3336   }\r
3337 \r
3338   /* Create a buffer bitmap - this is the actual bitmap\r
3339    * being written to.  When all the work is done, we can\r
3340    * copy it to the real DC (the screen).  This avoids\r
3341    * the problems with flickering.\r
3342    */\r
3343   GetClientRect(hwndMain, &Rect);\r
3344   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3345                                         Rect.bottom-Rect.top+1);\r
3346   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3347   if (!appData.monoMode) {\r
3348     SelectPalette(hdcmem, hPal, FALSE);\r
3349   }\r
3350 \r
3351   /* Create clips for dragging */\r
3352   if (!fullrepaint) {\r
3353     if (dragInfo.from.x >= 0) {\r
3354       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3355       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3356     }\r
3357     if (dragInfo.start.x >= 0) {\r
3358       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3359       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3360     }\r
3361     if (dragInfo.pos.x >= 0) {\r
3362       x = dragInfo.pos.x - squareSize / 2;\r
3363       y = dragInfo.pos.y - squareSize / 2;\r
3364       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3365     }\r
3366     if (dragInfo.lastpos.x >= 0) {\r
3367       x = dragInfo.lastpos.x - squareSize / 2;\r
3368       y = dragInfo.lastpos.y - squareSize / 2;\r
3369       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3370     }\r
3371   }\r
3372 \r
3373   /* Are we animating a move?  \r
3374    * If so, \r
3375    *   - remove the piece from the board (temporarely)\r
3376    *   - calculate the clipping region\r
3377    */\r
3378   if (!fullrepaint) {\r
3379     if (animInfo.piece != EmptySquare) {\r
3380       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3381       x = boardRect.left + animInfo.lastpos.x;\r
3382       y = boardRect.top + animInfo.lastpos.y;\r
3383       x2 = boardRect.left + animInfo.pos.x;\r
3384       y2 = boardRect.top + animInfo.pos.y;\r
3385       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3386       /* Slight kludge.  The real problem is that after AnimateMove is\r
3387          done, the position on the screen does not match lastDrawn.\r
3388          This currently causes trouble only on e.p. captures in\r
3389          atomic, where the piece moves to an empty square and then\r
3390          explodes.  The old and new positions both had an empty square\r
3391          at the destination, but animation has drawn a piece there and\r
3392          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3393       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3394     }\r
3395   }\r
3396 \r
3397   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3398   if (num_clips == 0)\r
3399     fullrepaint = TRUE;\r
3400 \r
3401   /* Set clipping on the memory DC */\r
3402   if (!fullrepaint) {\r
3403     SelectClipRgn(hdcmem, clips[0]);\r
3404     for (x = 1; x < num_clips; x++) {\r
3405       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3406         abort();  // this should never ever happen!\r
3407     }\r
3408   }\r
3409 \r
3410   /* Do all the drawing to the memory DC */\r
3411   if(explodeInfo.radius) { // [HGM] atomic\r
3412         HBRUSH oldBrush;\r
3413         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3414         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3415         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3416         x += squareSize/2;\r
3417         y += squareSize/2;\r
3418         if(!fullrepaint) {\r
3419           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3420           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3421         }\r
3422         DrawGridOnDC(hdcmem);\r
3423         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3424         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3425         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3426         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3427         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3428         SelectObject(hdcmem, oldBrush);\r
3429   } else {\r
3430     DrawGridOnDC(hdcmem);\r
3431     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3432         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3433         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3434     } else {\r
3435         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3436         oldPartnerHighlight = partnerHighlightInfo;\r
3437     }\r
3438     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3439   }\r
3440   if(nr == 0) // [HGM] dual: markers only on left board\r
3441   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3442     for (column = 0; column < BOARD_WIDTH; column++) {\r
3443         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3444             HBRUSH oldBrush = SelectObject(hdcmem, \r
3445                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3446             SquareToPos(row, column, &x, &y);\r
3447             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3448                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3449             SelectObject(hdcmem, oldBrush);\r
3450         }\r
3451     }\r
3452   }\r
3453   if(logoHeight) {\r
3454         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3455         if(appData.autoLogo) {\r
3456           \r
3457           switch(gameMode) { // pick logos based on game mode\r
3458             case IcsObserving:\r
3459                 whiteLogo = second.programLogo; // ICS logo\r
3460                 blackLogo = second.programLogo;\r
3461             default:\r
3462                 break;\r
3463             case IcsPlayingWhite:\r
3464                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3465                 blackLogo = second.programLogo; // ICS logo\r
3466                 break;\r
3467             case IcsPlayingBlack:\r
3468                 whiteLogo = second.programLogo; // ICS logo\r
3469                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3470                 break;\r
3471             case TwoMachinesPlay:\r
3472                 if(first.twoMachinesColor[0] == 'b') {\r
3473                     whiteLogo = second.programLogo;\r
3474                     blackLogo = first.programLogo;\r
3475                 }\r
3476                 break;\r
3477             case MachinePlaysWhite:\r
3478                 blackLogo = userLogo;\r
3479                 break;\r
3480             case MachinePlaysBlack:\r
3481                 whiteLogo = userLogo;\r
3482                 blackLogo = first.programLogo;\r
3483           }\r
3484         }\r
3485         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3486         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3487   }\r
3488 \r
3489   if( appData.highlightMoveWithArrow ) {\r
3490     DrawArrowHighlight(hdcmem);\r
3491   }\r
3492 \r
3493   DrawCoordsOnDC(hdcmem);\r
3494 \r
3495   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3496                  /* to make sure lastDrawn contains what is actually drawn */\r
3497 \r
3498   /* Put the dragged piece back into place and draw it (out of place!) */\r
3499     if (dragged_piece != EmptySquare) {\r
3500     /* [HGM] or restack */\r
3501     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3502                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3503     else\r
3504     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3505                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3506     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3507     x = dragInfo.pos.x - squareSize / 2;\r
3508     y = dragInfo.pos.y - squareSize / 2;\r
3509     DrawPieceOnDC(hdcmem, dragged_piece,\r
3510                   ((int) dragged_piece < (int) BlackPawn), \r
3511                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3512   }   \r
3513   \r
3514   /* Put the animated piece back into place and draw it */\r
3515   if (animInfo.piece != EmptySquare) {\r
3516     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3517     x = boardRect.left + animInfo.pos.x;\r
3518     y = boardRect.top + animInfo.pos.y;\r
3519     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3520                   ((int) animInfo.piece < (int) BlackPawn),\r
3521                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3522   }\r
3523 \r
3524   /* Release the bufferBitmap by selecting in the old bitmap \r
3525    * and delete the memory DC\r
3526    */\r
3527   SelectObject(hdcmem, oldBitmap);\r
3528   DeleteDC(hdcmem);\r
3529 \r
3530   /* Set clipping on the target DC */\r
3531   if (!fullrepaint) {\r
3532     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3533         RECT rect;\r
3534         GetRgnBox(clips[x], &rect);\r
3535         DeleteObject(clips[x]);\r
3536         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3537                           rect.right + wpMain.width/2, rect.bottom);\r
3538     }\r
3539     SelectClipRgn(hdc, clips[0]);\r
3540     for (x = 1; x < num_clips; x++) {\r
3541       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3542         abort();   // this should never ever happen!\r
3543     } \r
3544   }\r
3545 \r
3546   /* Copy the new bitmap onto the screen in one go.\r
3547    * This way we avoid any flickering\r
3548    */\r
3549   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3550   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3551          boardRect.right - boardRect.left,\r
3552          boardRect.bottom - boardRect.top,\r
3553          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3554   if(saveDiagFlag) { \r
3555     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
3556     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3557 \r
3558     GetObject(bufferBitmap, sizeof(b), &b);\r
3559     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
3560         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3561         bih.biWidth = b.bmWidth;\r
3562         bih.biHeight = b.bmHeight;\r
3563         bih.biPlanes = 1;\r
3564         bih.biBitCount = b.bmBitsPixel;\r
3565         bih.biCompression = 0;\r
3566         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3567         bih.biXPelsPerMeter = 0;\r
3568         bih.biYPelsPerMeter = 0;\r
3569         bih.biClrUsed = 0;\r
3570         bih.biClrImportant = 0;\r
3571 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3572 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3573         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3574 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3575 \r
3576         wb = b.bmWidthBytes;\r
3577         // count colors\r
3578         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3579                 int k = ((int*) pData)[i];\r
3580                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3581                 if(j >= 16) break;\r
3582                 color[j] = k;\r
3583                 if(j >= nrColors) nrColors = j+1;\r
3584         }\r
3585         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3586                 INT p = 0;\r
3587                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3588                     for(w=0; w<(wb>>2); w+=2) {\r
3589                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3590                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3591                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3592                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3593                         pData[p++] = m | j<<4;\r
3594                     }\r
3595                     while(p&3) pData[p++] = 0;\r
3596                 }\r
3597                 fac = 3;\r
3598                 wb = ((wb+31)>>5)<<2;\r
3599         }\r
3600         // write BITMAPFILEHEADER\r
3601         fprintf(diagFile, "BM");\r
3602         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3603         fputDW(diagFile, 0);\r
3604         fputDW(diagFile, 0x36 + (fac?64:0));\r
3605         // write BITMAPINFOHEADER\r
3606         fputDW(diagFile, 40);\r
3607         fputDW(diagFile, b.bmWidth);\r
3608         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3609         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3610         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3611         fputDW(diagFile, 0);\r
3612         fputDW(diagFile, 0);\r
3613         fputDW(diagFile, 0);\r
3614         fputDW(diagFile, 0);\r
3615         fputDW(diagFile, 0);\r
3616         fputDW(diagFile, 0);\r
3617         // write color table\r
3618         if(fac)\r
3619         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3620         // write bitmap data\r
3621         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3622                 fputc(pData[i], diagFile);\r
3623      }\r
3624   }\r
3625 \r
3626   SelectObject(tmphdc, oldBitmap);\r
3627 \r
3628   /* Massive cleanup */\r
3629   for (x = 0; x < num_clips; x++)\r
3630     DeleteObject(clips[x]);\r
3631 \r
3632   DeleteDC(tmphdc);\r
3633   DeleteObject(bufferBitmap);\r
3634 \r
3635   if (releaseDC) \r
3636     ReleaseDC(hwndMain, hdc);\r
3637   \r
3638   if (lastDrawnFlipView != flipView && nr == 0) {\r
3639     if (flipView)\r
3640       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3641     else\r
3642       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3643   }\r
3644 \r
3645 /*  CopyBoard(lastDrawn, board);*/\r
3646   lastDrawnHighlight = highlightInfo;\r
3647   lastDrawnPremove   = premoveHighlightInfo;\r
3648   lastDrawnFlipView = flipView;\r
3649   lastDrawnValid[nr] = 1;\r
3650 }\r
3651 \r
3652 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
3653 int\r
3654 SaveDiagram(f)\r
3655      FILE *f;\r
3656 {\r
3657     saveDiagFlag = 1; diagFile = f;\r
3658     HDCDrawPosition(NULL, TRUE, NULL);\r
3659 \r
3660     saveDiagFlag = 0;\r
3661 \r
3662 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
3663     \r
3664     fclose(f);\r
3665     return TRUE;\r
3666 }\r
3667 \r
3668 \r
3669 /*---------------------------------------------------------------------------*\\r
3670 | CLIENT PAINT PROCEDURE\r
3671 |   This is the main event-handler for the WM_PAINT message.\r
3672 |\r
3673 \*---------------------------------------------------------------------------*/\r
3674 VOID\r
3675 PaintProc(HWND hwnd)\r
3676 {\r
3677   HDC         hdc;\r
3678   PAINTSTRUCT ps;\r
3679   HFONT       oldFont;\r
3680 \r
3681   if((hdc = BeginPaint(hwnd, &ps))) {\r
3682     if (IsIconic(hwnd)) {\r
3683       DrawIcon(hdc, 2, 2, iconCurrent);\r
3684     } else {\r
3685       if (!appData.monoMode) {\r
3686         SelectPalette(hdc, hPal, FALSE);\r
3687         RealizePalette(hdc);\r
3688       }\r
3689       HDCDrawPosition(hdc, 1, NULL);\r
3690       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
3691         flipView = !flipView; partnerUp = !partnerUp;\r
3692         HDCDrawPosition(hdc, 1, NULL);\r
3693         flipView = !flipView; partnerUp = !partnerUp;\r
3694       }\r
3695       oldFont =\r
3696         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3697       ExtTextOut(hdc, messageRect.left, messageRect.top,