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