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