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