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