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