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