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