added a dialog for engine-specific option settings
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 \r
137 typedef struct { // [HGM] atomic\r
138   int fromX, fromY, toX, toY, radius;\r
139 } ExplodeInfo;\r
140 \r
141 static ExplodeInfo explodeInfo;\r
142 \r
143 /* Window class names */\r
144 char szAppName[] = "WinBoard";\r
145 char szConsoleName[] = "WBConsole";\r
146 \r
147 /* Title bar text */\r
148 char szTitle[] = "WinBoard";\r
149 char szConsoleTitle[] = "I C S Interaction";\r
150 \r
151 char *programName;\r
152 char *settingsFileName;\r
153 BOOLEAN saveSettingsOnExit;\r
154 char installDir[MSG_SIZ];\r
155 \r
156 BoardSize boardSize;\r
157 BOOLEAN chessProgram;\r
158 static int boardX, boardY;\r
159 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
160 static int squareSize, lineGap, minorSize;\r
161 static int winWidth, winHeight, winW, winH;\r
162 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
163 static int logoHeight = 0;\r
164 static char messageText[MESSAGE_TEXT_MAX];\r
165 static int clockTimerEvent = 0;\r
166 static int loadGameTimerEvent = 0;\r
167 static int analysisTimerEvent = 0;\r
168 static DelayedEventCallback delayedTimerCallback;\r
169 static int delayedTimerEvent = 0;\r
170 static int buttonCount = 2;\r
171 char *icsTextMenuString;\r
172 char *icsNames;\r
173 char *firstChessProgramNames;\r
174 char *secondChessProgramNames;\r
175 \r
176 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 HWND hwndMain = NULL;        /* root window*/\r
182 HWND hwndConsole = NULL;\r
183 BOOLEAN alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
197 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
198 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
199 static HPEN gridPen = NULL;\r
200 static HPEN highlightPen = NULL;\r
201 static HPEN premovePen = NULL;\r
202 static NPLOGPALETTE pLogPal;\r
203 static BOOL paletteChanged = FALSE;\r
204 static HICON iconWhite, iconBlack, iconCurrent;\r
205 static int doingSizing = FALSE;\r
206 static int lastSizing = 0;\r
207 static int prevStderrPort;\r
208 static HBITMAP userLogo;\r
209 \r
210 /* [AS] Support for background textures */\r
211 #define BACK_TEXTURE_MODE_DISABLED      0\r
212 #define BACK_TEXTURE_MODE_PLAIN         1\r
213 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #define oldDialog (_winmajor < 4)\r
226 #endif\r
227 \r
228 char *defaultTextAttribs[] = \r
229 {\r
230   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
231   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
232   COLOR_NONE\r
233 };\r
234 \r
235 typedef struct {\r
236   char *name;\r
237   int squareSize;\r
238   int lineGap;\r
239   int smallLayout;\r
240   int tinyLayout;\r
241   int cliWidth, cliHeight;\r
242 } SizeInfo;\r
243 \r
244 SizeInfo sizeInfo[] = \r
245 {\r
246   { "tiny",     21, 0, 1, 1, 0, 0 },\r
247   { "teeny",    25, 1, 1, 1, 0, 0 },\r
248   { "dinky",    29, 1, 1, 1, 0, 0 },\r
249   { "petite",   33, 1, 1, 1, 0, 0 },\r
250   { "slim",     37, 2, 1, 0, 0, 0 },\r
251   { "small",    40, 2, 1, 0, 0, 0 },\r
252   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
253   { "middling", 49, 2, 0, 0, 0, 0 },\r
254   { "average",  54, 2, 0, 0, 0, 0 },\r
255   { "moderate", 58, 3, 0, 0, 0, 0 },\r
256   { "medium",   64, 3, 0, 0, 0, 0 },\r
257   { "bulky",    72, 3, 0, 0, 0, 0 },\r
258   { "large",    80, 3, 0, 0, 0, 0 },\r
259   { "big",      87, 3, 0, 0, 0, 0 },\r
260   { "huge",     95, 3, 0, 0, 0, 0 },\r
261   { "giant",    108, 3, 0, 0, 0, 0 },\r
262   { "colossal", 116, 4, 0, 0, 0, 0 },\r
263   { "titanic",  129, 4, 0, 0, 0, 0 },\r
264   { NULL, 0, 0, 0, 0, 0, 0 }\r
265 };\r
266 \r
267 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
268 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
269 {\r
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288 };\r
289 \r
290 MyFont *font[NUM_SIZES][NUM_FONTS];\r
291 \r
292 typedef struct {\r
293   char *label;\r
294   int id;\r
295   HWND hwnd;\r
296   WNDPROC wndproc;\r
297 } MyButtonDesc;\r
298 \r
299 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
300 #define N_BUTTONS 5\r
301 \r
302 MyButtonDesc buttonDesc[N_BUTTONS] =\r
303 {\r
304   {"<<", IDM_ToStart, NULL, NULL},\r
305   {"<", IDM_Backward, NULL, NULL},\r
306   {"P", IDM_Pause, NULL, NULL},\r
307   {">", IDM_Forward, NULL, NULL},\r
308   {">>", IDM_ToEnd, NULL, NULL},\r
309 };\r
310 \r
311 int tinyLayout = 0, smallLayout = 0;\r
312 #define MENU_BAR_ITEMS 7\r
313 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
314   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
315   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
316 };\r
317 \r
318 \r
319 MySound sounds[(int)NSoundClasses];\r
320 MyTextAttribs textAttribs[(int)NColorClasses];\r
321 \r
322 MyColorizeAttribs colorizeAttribs[] = {\r
323   { (COLORREF)0, 0, "Shout Text" },\r
324   { (COLORREF)0, 0, "SShout/CShout" },\r
325   { (COLORREF)0, 0, "Channel 1 Text" },\r
326   { (COLORREF)0, 0, "Channel Text" },\r
327   { (COLORREF)0, 0, "Kibitz Text" },\r
328   { (COLORREF)0, 0, "Tell Text" },\r
329   { (COLORREF)0, 0, "Challenge Text" },\r
330   { (COLORREF)0, 0, "Request Text" },\r
331   { (COLORREF)0, 0, "Seek Text" },\r
332   { (COLORREF)0, 0, "Normal Text" },\r
333   { (COLORREF)0, 0, "None" }\r
334 };\r
335 \r
336 \r
337 \r
338 static char *commentTitle;\r
339 static char *commentText;\r
340 static int commentIndex;\r
341 static Boolean editComment = FALSE;\r
342 HWND commentDialog = NULL;\r
343 BOOLEAN commentDialogUp = FALSE;\r
344 static int commentX, commentY, commentH, commentW;\r
345 \r
346 static char *analysisTitle;\r
347 static char *analysisText;\r
348 HWND analysisDialog = NULL;\r
349 BOOLEAN analysisDialogUp = FALSE;\r
350 static int analysisX, analysisY, analysisH, analysisW;\r
351 \r
352 char errorTitle[MSG_SIZ];\r
353 char errorMessage[2*MSG_SIZ];\r
354 HWND errorDialog = NULL;\r
355 BOOLEAN moveErrorMessageUp = FALSE;\r
356 BOOLEAN consoleEcho = TRUE;\r
357 CHARFORMAT consoleCF;\r
358 COLORREF consoleBackgroundColor;\r
359 \r
360 char *programVersion;\r
361 \r
362 #define CPReal 1\r
363 #define CPComm 2\r
364 #define CPSock 3\r
365 #define CPRcmd 4\r
366 typedef int CPKind;\r
367 \r
368 typedef struct {\r
369   CPKind kind;\r
370   HANDLE hProcess;\r
371   DWORD pid;\r
372   HANDLE hTo;\r
373   HANDLE hFrom;\r
374   SOCKET sock;\r
375   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
376 } ChildProc;\r
377 \r
378 #define INPUT_SOURCE_BUF_SIZE 4096\r
379 \r
380 typedef struct _InputSource {\r
381   CPKind kind;\r
382   HANDLE hFile;\r
383   SOCKET sock;\r
384   int lineByLine;\r
385   HANDLE hThread;\r
386   DWORD id;\r
387   char buf[INPUT_SOURCE_BUF_SIZE];\r
388   char *next;\r
389   DWORD count;\r
390   int error;\r
391   InputCallback func;\r
392   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
393   VOIDSTAR closure;\r
394 } InputSource;\r
395 \r
396 InputSource *consoleInputSource;\r
397 \r
398 DCB dcb;\r
399 \r
400 /* forward */\r
401 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
402 VOID ConsoleCreate();\r
403 LRESULT CALLBACK\r
404   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
406 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
407 VOID ParseCommSettings(char *arg, DCB *dcb);\r
408 LRESULT CALLBACK\r
409   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
410 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
411 void ParseIcsTextMenu(char *icsTextMenuString);\r
412 VOID PopUpMoveDialog(char firstchar);\r
413 VOID PopUpNameDialog(char firstchar);\r
414 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
415 \r
416 /* [AS] */\r
417 int NewGameFRC();\r
418 int GameListOptions();\r
419 \r
420 HWND moveHistoryDialog = NULL;\r
421 BOOLEAN moveHistoryDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpMoveHistory;\r
424 \r
425 HWND evalGraphDialog = NULL;\r
426 BOOLEAN evalGraphDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEvalGraph;\r
429 \r
430 HWND engineOutputDialog = NULL;\r
431 BOOLEAN engineOutputDialogUp = FALSE;\r
432 \r
433 WindowPlacement wpEngineOutput;\r
434 WindowPlacement wpGameList;\r
435 WindowPlacement wpConsole;\r
436 \r
437 VOID MoveHistoryPopUp();\r
438 VOID MoveHistoryPopDown();\r
439 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 BOOL MoveHistoryIsUp();\r
441 \r
442 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 VOID EvalGraphPopUp();\r
444 VOID EvalGraphPopDown();\r
445 BOOL EvalGraphIsUp();\r
446 \r
447 VOID EngineOutputPopUp();\r
448 VOID EngineOutputPopDown();\r
449 BOOL EngineOutputIsUp();\r
450 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
451 \r
452 VOID GothicPopUp(char *title, VariantClass variant);\r
453 /*\r
454  * Setting "frozen" should disable all user input other than deleting\r
455  * the window.  We do this while engines are initializing themselves.\r
456  */\r
457 static int frozen = 0;\r
458 static int oldMenuItemState[MENU_BAR_ITEMS];\r
459 void FreezeUI()\r
460 {\r
461   HMENU hmenu;\r
462   int i;\r
463 \r
464   if (frozen) return;\r
465   frozen = 1;\r
466   hmenu = GetMenu(hwndMain);\r
467   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
468     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
469   }\r
470   DrawMenuBar(hwndMain);\r
471 }\r
472 \r
473 /* Undo a FreezeUI */\r
474 void ThawUI()\r
475 {\r
476   HMENU hmenu;\r
477   int i;\r
478 \r
479   if (!frozen) return;\r
480   frozen = 0;\r
481   hmenu = GetMenu(hwndMain);\r
482   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
483     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
484   }\r
485   DrawMenuBar(hwndMain);\r
486 }\r
487 \r
488 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
489 \r
490 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
491 #ifdef JAWS\r
492 #include "jaws.c"\r
493 #else\r
494 #define JAWS_INIT\r
495 #define JAWS_ALT_INTERCEPT\r
496 #define JAWS_KB_NAVIGATION\r
497 #define JAWS_MENU_ITEMS\r
498 #define JAWS_SILENCE\r
499 #define JAWS_REPLAY\r
500 #define JAWS_ACCEL\r
501 #define JAWS_COPYRIGHT\r
502 #define JAWS_DELETE(X) X\r
503 #define SAYMACHINEMOVE()\r
504 #define SAY(X)\r
505 #endif\r
506 \r
507 /*---------------------------------------------------------------------------*\\r
508  *\r
509  * WinMain\r
510  *\r
511 \*---------------------------------------------------------------------------*/\r
512 \r
513 int APIENTRY\r
514 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
515         LPSTR lpCmdLine, int nCmdShow)\r
516 {\r
517   MSG msg;\r
518   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
519 //  INITCOMMONCONTROLSEX ex;\r
520 \r
521   debugFP = stderr;\r
522 \r
523   LoadLibrary("RICHED32.DLL");\r
524   consoleCF.cbSize = sizeof(CHARFORMAT);\r
525 \r
526   if (!InitApplication(hInstance)) {\r
527     return (FALSE);\r
528   }\r
529   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
530     return (FALSE);\r
531   }\r
532 \r
533   JAWS_INIT\r
534 \r
535 //  InitCommonControlsEx(&ex);\r
536   InitCommonControls();\r
537 \r
538   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
539   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
540   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
541 \r
542   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
543 \r
544   while (GetMessage(&msg, /* message structure */\r
545                     NULL, /* handle of window receiving the message */\r
546                     0,    /* lowest message to examine */\r
547                     0))   /* highest message to examine */\r
548     {\r
549 \r
550       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
551         // [HGM] navigate: switch between all windows with tab\r
552         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
553         int i, currentElement = 0;\r
554 \r
555         // first determine what element of the chain we come from (if any)\r
556         if(appData.icsActive) {\r
557             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
558             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
559         }\r
560         if(engineOutputDialog && EngineOutputIsUp()) {\r
561             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
562             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
563         }\r
564         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
565             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
566         }\r
567         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
568         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
569         if(msg.hwnd == e1)                 currentElement = 2; else\r
570         if(msg.hwnd == e2)                 currentElement = 3; else\r
571         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
572         if(msg.hwnd == mh)                currentElement = 4; else\r
573         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
574         if(msg.hwnd == hText)  currentElement = 5; else\r
575         if(msg.hwnd == hInput) currentElement = 6; else\r
576         for (i = 0; i < N_BUTTONS; i++) {\r
577             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
578         }\r
579 \r
580         // determine where to go to\r
581         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
582           do {\r
583             currentElement = (currentElement + direction) % 7;\r
584             switch(currentElement) {\r
585                 case 0:\r
586                   h = hwndMain; break; // passing this case always makes the loop exit\r
587                 case 1:\r
588                   h = buttonDesc[0].hwnd; break; // could be NULL\r
589                 case 2:\r
590                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
591                   h = e1; break;\r
592                 case 3:\r
593                   if(!EngineOutputIsUp()) continue;\r
594                   h = e2; break;\r
595                 case 4:\r
596                   if(!MoveHistoryIsUp()) continue;\r
597                   h = mh; break;\r
598 //              case 5: // input to eval graph does not seem to get here!\r
599 //                if(!EvalGraphIsUp()) continue;\r
600 //                h = evalGraphDialog; break;\r
601                 case 5:\r
602                   if(!appData.icsActive) continue;\r
603                   SAY("display");\r
604                   h = hText; break;\r
605                 case 6:\r
606                   if(!appData.icsActive) continue;\r
607                   SAY("input");\r
608                   h = hInput; break;\r
609             }\r
610           } while(h == 0);\r
611 \r
612           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
613           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
614           SetFocus(h);\r
615 \r
616           continue; // this message now has been processed\r
617         }\r
618       }\r
619 \r
620       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
621           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
622           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
623           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
624           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
625           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
626           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
627           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
628           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
629           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
630         TranslateMessage(&msg); /* Translates virtual key codes */\r
631         DispatchMessage(&msg);  /* Dispatches message to window */\r
632       }\r
633     }\r
634 \r
635 \r
636   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
637 }\r
638 \r
639 /*---------------------------------------------------------------------------*\\r
640  *\r
641  * Initialization functions\r
642  *\r
643 \*---------------------------------------------------------------------------*/\r
644 \r
645 void\r
646 SetUserLogo()\r
647 {   // update user logo if necessary\r
648     static char oldUserName[MSG_SIZ], *curName;\r
649 \r
650     if(appData.autoLogo) {\r
651           curName = UserName();\r
652           if(strcmp(curName, oldUserName)) {\r
653                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
654                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
655                 strcpy(oldUserName, curName);\r
656           }\r
657     }\r
658 }\r
659 \r
660 BOOL\r
661 InitApplication(HINSTANCE hInstance)\r
662 {\r
663   WNDCLASS wc;\r
664 \r
665   /* Fill in window class structure with parameters that describe the */\r
666   /* main window. */\r
667 \r
668   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
669   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
670   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
671   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
672   wc.hInstance     = hInstance;         /* Owner of this class */\r
673   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
674   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
675   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
676   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
677   wc.lpszClassName = szAppName;                 /* Name to register as */\r
678 \r
679   /* Register the window class and return success/failure code. */\r
680   if (!RegisterClass(&wc)) return FALSE;\r
681 \r
682   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
683   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
684   wc.cbClsExtra    = 0;\r
685   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
686   wc.hInstance     = hInstance;\r
687   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
688   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
689   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
690   wc.lpszMenuName  = NULL;\r
691   wc.lpszClassName = szConsoleName;\r
692 \r
693   if (!RegisterClass(&wc)) return FALSE;\r
694   return TRUE;\r
695 }\r
696 \r
697 \r
698 /* Set by InitInstance, used by EnsureOnScreen */\r
699 int screenHeight, screenWidth;\r
700 \r
701 void\r
702 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
703 {\r
704 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
705   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
706   if (*x > screenWidth - 32) *x = 0;\r
707   if (*y > screenHeight - 32) *y = 0;\r
708   if (*x < minX) *x = minX;\r
709   if (*y < minY) *y = minY;\r
710 }\r
711 \r
712 BOOL\r
713 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
714 {\r
715   HWND hwnd; /* Main window handle. */\r
716   int ibs;\r
717   WINDOWPLACEMENT wp;\r
718   char *filepart;\r
719 \r
720   hInst = hInstance;    /* Store instance handle in our global variable */\r
721 \r
722   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
723     *filepart = NULLCHAR;\r
724   } else {\r
725     GetCurrentDirectory(MSG_SIZ, installDir);\r
726   }\r
727   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
728   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
729   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
730   if (appData.debugMode) {\r
731     debugFP = fopen(appData.nameOfDebugFile, "w");\r
732     setbuf(debugFP, NULL);\r
733   }\r
734 \r
735   InitBackEnd1();\r
736 \r
737 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
738 //  InitEngineUCI( installDir, &second );\r
739 \r
740   /* Create a main window for this application instance. */\r
741   hwnd = CreateWindow(szAppName, szTitle,\r
742                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
743                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
744                       NULL, NULL, hInstance, NULL);\r
745   hwndMain = hwnd;\r
746 \r
747   /* If window could not be created, return "failure" */\r
748   if (!hwnd) {\r
749     return (FALSE);\r
750   }\r
751 \r
752   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
753   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
754       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
755 \r
756       if (first.programLogo == NULL && appData.debugMode) {\r
757           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
758       }\r
759   } else if(appData.autoLogo) {\r
760       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
761         char buf[MSG_SIZ];\r
762         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
763         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
764       }\r
765   }\r
766 \r
767   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
768       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
769 \r
770       if (second.programLogo == NULL && appData.debugMode) {\r
771           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
772       }\r
773   } else if(appData.autoLogo) {\r
774       char buf[MSG_SIZ];\r
775       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
776         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
777         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
778       } else\r
779       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
780         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
781         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
782       }\r
783   }\r
784 \r
785   SetUserLogo();\r
786 \r
787   iconWhite = LoadIcon(hInstance, "icon_white");\r
788   iconBlack = LoadIcon(hInstance, "icon_black");\r
789   iconCurrent = iconWhite;\r
790   InitDrawingColors();\r
791   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
792   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
793   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
794     /* Compute window size for each board size, and use the largest\r
795        size that fits on this screen as the default. */\r
796     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
797     if (boardSize == (BoardSize)-1 &&\r
798         winH <= screenHeight\r
799            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
800         && winW <= screenWidth) {\r
801       boardSize = (BoardSize)ibs;\r
802     }\r
803   }\r
804 \r
805   InitDrawingSizes(boardSize, 0);\r
806   InitMenuChecks();\r
807   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
808 \r
809   /* [AS] Load textures if specified */\r
810   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
811   \r
812   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
813       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
814       liteBackTextureMode = appData.liteBackTextureMode;\r
815 \r
816       if (liteBackTexture == NULL && appData.debugMode) {\r
817           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
818       }\r
819   }\r
820   \r
821   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
822       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
823       darkBackTextureMode = appData.darkBackTextureMode;\r
824 \r
825       if (darkBackTexture == NULL && appData.debugMode) {\r
826           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
827       }\r
828   }\r
829 \r
830   mysrandom( (unsigned) time(NULL) );\r
831 \r
832   /* [AS] Restore layout */\r
833   if( wpMoveHistory.visible ) {\r
834       MoveHistoryPopUp();\r
835   }\r
836 \r
837   if( wpEvalGraph.visible ) {\r
838       EvalGraphPopUp();\r
839   }\r
840 \r
841   if( wpEngineOutput.visible ) {\r
842       EngineOutputPopUp();\r
843   }\r
844 \r
845   InitBackEnd2();\r
846 \r
847   /* Make the window visible; update its client area; and return "success" */\r
848   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
849   wp.length = sizeof(WINDOWPLACEMENT);\r
850   wp.flags = 0;\r
851   wp.showCmd = nCmdShow;\r
852   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
853   wp.rcNormalPosition.left = boardX;\r
854   wp.rcNormalPosition.right = boardX + winWidth;\r
855   wp.rcNormalPosition.top = boardY;\r
856   wp.rcNormalPosition.bottom = boardY + winHeight;\r
857   SetWindowPlacement(hwndMain, &wp);\r
858 \r
859   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
860                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
861 \r
862   if (hwndConsole) {\r
863 #if AOT_CONSOLE\r
864     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
865                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
866 #endif\r
867     ShowWindow(hwndConsole, nCmdShow);\r
868   }\r
869   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
870 \r
871   return TRUE;\r
872 \r
873 }\r
874 \r
875 \r
876 typedef enum {\r
877   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
878   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
879   ArgSettingsFilename,\r
880   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
881 } ArgType;\r
882 \r
883 typedef struct {\r
884   char *argName;\r
885   ArgType argType;\r
886   /***\r
887   union {\r
888     String *pString;       // ArgString\r
889     int *pInt;             // ArgInt\r
890     float *pFloat;         // ArgFloat\r
891     Boolean *pBoolean;     // ArgBoolean\r
892     COLORREF *pColor;      // ArgColor\r
893     ColorClass cc;         // ArgAttribs\r
894     String *pFilename;     // ArgFilename\r
895     BoardSize *pBoardSize; // ArgBoardSize\r
896     int whichFont;         // ArgFont\r
897     DCB *pDCB;             // ArgCommSettings\r
898     String *pFilename;     // ArgSettingsFilename\r
899   } argLoc;\r
900   ***/\r
901   LPVOID argLoc;\r
902   BOOL save;\r
903 } ArgDescriptor;\r
904 \r
905 int junk;\r
906 ArgDescriptor argDescriptors[] = {\r
907   /* positional arguments */\r
908   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
909   { "", ArgNone, NULL },\r
910   /* keyword arguments */\r
911   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
912   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
913   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
914   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
915   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
916   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
917   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
918   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
919   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
920   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
921   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
922   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
923   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
924   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
925   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
926   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
927   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
928   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
929     FALSE },\r
930   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
931     FALSE },\r
932   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
933     FALSE },\r
934   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
935   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
936     FALSE },\r
937   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
938   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
939   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
940   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
941   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
942   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
943   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
944   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
945   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
946   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
947   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
948   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
949   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
950   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
951   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
952   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
953   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
954   /*!!bitmapDirectory?*/\r
955   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
956   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
957   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
958   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
959   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
960   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
961   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
962   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
963   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
964   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
965   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
966   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
967   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
968   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
969   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
970   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
971   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
972   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
973   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
974   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
975   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
976   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
977   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
978   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
979   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
980   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
981   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
982   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
983   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
984   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
985   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
986   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
987   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
988   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
989   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
990   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
991   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
992   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
993   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
994   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
995   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
996   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
997   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
998   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
999   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1000   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1001   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1002   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1003   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1004   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1005   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1006   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1007   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1008   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1009   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1010   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1011   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1012   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1013   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1014   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1015   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1016   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1017   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1018   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1019   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1020   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1021   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1022   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1023   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1024   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1025   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1026   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1027   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1028   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1029   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1030   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1031   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1032   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1033   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1034   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1035   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1036   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1037   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1038   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1039   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1040   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1041   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1042   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1043   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1044   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1045   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1046   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1047   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1048   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1049     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1050   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1051   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1052   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1053   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1054   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1055   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1056   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1057   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1058     TRUE }, /* must come after all fonts */\r
1059   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1060   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1061     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1062   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1063   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1064   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1065   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1066   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1067   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1068   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1069   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1070   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1071   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1072   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1073   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1074   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1075   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1076   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1077   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1078   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1079   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1080   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1081   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1082   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1083   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1084   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1085   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1086   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1087   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1088   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1089   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1090 #if 0\r
1091   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1092   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1093 #endif\r
1094   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1095   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1096   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1097   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1098   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1099   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1100   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1101   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1102   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1103   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1104   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1105   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1106   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1107   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1108   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1109   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1110   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1111   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1112   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1113   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1114   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1115   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1116   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1117   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1118   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1119   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1120   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1121   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1122   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1123   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1124   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1125   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1126   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1127   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1128   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1129   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1130   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1131   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1132   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1133   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1134   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1135   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1136   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1137   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1138   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1139   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1140   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1141   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1142   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1143   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1144   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1145   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1146   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1147   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1148   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1149   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1150   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1151   { "highlightLastMove", ArgBoolean,\r
1152     (LPVOID) &appData.highlightLastMove, TRUE },\r
1153   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1154   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1155   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1156   { "highlightDragging", ArgBoolean,\r
1157     (LPVOID) &appData.highlightDragging, TRUE },\r
1158   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1159   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1160   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1161   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1162   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1163   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1164   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1165   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1166   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1167   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1168   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1169   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1170   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1171   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1172   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1173   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1174   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1175   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1176   { "soundShout", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1178   { "soundSShout", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1180   { "soundChannel1", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1182   { "soundChannel", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1184   { "soundKibitz", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1186   { "soundTell", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1188   { "soundChallenge", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1190   { "soundRequest", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1192   { "soundSeek", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1194   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1195   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1196   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1197   { "soundIcsLoss", ArgFilename, \r
1198     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1199   { "soundIcsDraw", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1201   { "soundIcsUnfinished", ArgFilename, \r
1202     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1203   { "soundIcsAlarm", ArgFilename, \r
1204     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1205   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1206   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1207   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1208   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1209   { "reuseChessPrograms", ArgBoolean,\r
1210     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1211   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1212   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1213   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1214   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1215   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1216   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1217   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1218   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1219   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1220   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1221   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1222   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1223   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1224   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1225   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1226     TRUE },\r
1227   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1228     TRUE },\r
1229   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1230   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1231   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1232   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1233   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1234   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1235   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1236   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1237   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1238   /* [AS] New features */\r
1239   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1240   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1241   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1242   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1243   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1244   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1245   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1246   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1247   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1248   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1249   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1250   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1251   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1252   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1253   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1254   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1255   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1256   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1257   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1258   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1259   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1260   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1261   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1262   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1263   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1264   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1265   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1266   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1267   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1268   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1269   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1270   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1271   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1272   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1273   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1274   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1275   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1276   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1277   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1278   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1279   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1280   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1281   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1282   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1283   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1284   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1285   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1286   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1287   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1288   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1289 \r
1290   /* [HGM] board-size, adjudication and misc. options */\r
1291   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1292   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1293   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1294   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1295   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1296   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1297   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1298   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1299   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1300   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1301   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1302   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1303   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1304   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1305   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1306   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1307   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1308   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1309   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1310   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1311   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1312   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1313   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1314   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1315   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1316   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1317   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1318   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1319   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1320   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1321   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1322 \r
1323 #ifdef ZIPPY\r
1324   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1325   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1326   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1327   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1328   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1329   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1330   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1331   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1332   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1333   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1334   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1335   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1336   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1337     FALSE },\r
1338   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1339   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1340   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1341   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1342   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1343   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1344   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1345     FALSE },\r
1346   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1347   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1348   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1349   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1350   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1351   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1352   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1353   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1354   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1355   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1356   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1357   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1358   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1359   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1360   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1361   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1362   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1363   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1364   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1365 #endif\r
1366   /* [HGM] options for broadcasting and time odds */\r
1367   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1368   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1369   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1370   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1371   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1372   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1373   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1374   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1375   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1376   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1377   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1378 \r
1379   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1380   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1381   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1382   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1383   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1384   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1385   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1386   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1387   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1388   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1389   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1390   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1391   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1392   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1393   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1394   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1395   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1396   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1397   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1398   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1399   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1400   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1401   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1402   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1403   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1404   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1405   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1406   /* [AS] Layout stuff */\r
1407   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1408   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1409   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1410   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1411   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1412 \r
1413   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1414   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1415   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1416   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1417   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1418 \r
1419   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1420   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1421   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1422   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1423   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1424 \r
1425   { NULL, ArgNone, NULL, FALSE }\r
1426 };\r
1427 \r
1428 \r
1429 /* Kludge for indirection files on command line */\r
1430 char* lastIndirectionFilename;\r
1431 ArgDescriptor argDescriptorIndirection =\r
1432 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1433 \r
1434 \r
1435 VOID\r
1436 ExitArgError(char *msg, char *badArg)\r
1437 {\r
1438   char buf[MSG_SIZ];\r
1439 \r
1440   sprintf(buf, "%s %s", msg, badArg);\r
1441   DisplayFatalError(buf, 0, 2);\r
1442   exit(2);\r
1443 }\r
1444 \r
1445 /* Command line font name parser.  NULL name means do nothing.\r
1446    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1447    For backward compatibility, syntax without the colon is also\r
1448    accepted, but font names with digits in them won't work in that case.\r
1449 */\r
1450 VOID\r
1451 ParseFontName(char *name, MyFontParams *mfp)\r
1452 {\r
1453   char *p, *q;\r
1454   if (name == NULL) return;\r
1455   p = name;\r
1456   q = strchr(p, ':');\r
1457   if (q) {\r
1458     if (q - p >= sizeof(mfp->faceName))\r
1459       ExitArgError("Font name too long:", name);\r
1460     memcpy(mfp->faceName, p, q - p);\r
1461     mfp->faceName[q - p] = NULLCHAR;\r
1462     p = q + 1;\r
1463   } else {\r
1464     q = mfp->faceName;\r
1465     while (*p && !isdigit(*p)) {\r
1466       *q++ = *p++;\r
1467       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1468         ExitArgError("Font name too long:", name);\r
1469     }\r
1470     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1471     *q = NULLCHAR;\r
1472   }\r
1473   if (!*p) ExitArgError("Font point size missing:", name);\r
1474   mfp->pointSize = (float) atof(p);\r
1475   mfp->bold = (strchr(p, 'b') != NULL);\r
1476   mfp->italic = (strchr(p, 'i') != NULL);\r
1477   mfp->underline = (strchr(p, 'u') != NULL);\r
1478   mfp->strikeout = (strchr(p, 's') != NULL);\r
1479 }\r
1480 \r
1481 /* Color name parser.\r
1482    X version accepts X color names, but this one\r
1483    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1484 COLORREF\r
1485 ParseColorName(char *name)\r
1486 {\r
1487   int red, green, blue, count;\r
1488   char buf[MSG_SIZ];\r
1489 \r
1490   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1491   if (count != 3) {\r
1492     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1493       &red, &green, &blue);\r
1494   }\r
1495   if (count != 3) {\r
1496     sprintf(buf, "Can't parse color name %s", name);\r
1497     DisplayError(buf, 0);\r
1498     return RGB(0, 0, 0);\r
1499   }\r
1500   return PALETTERGB(red, green, blue);\r
1501 }\r
1502 \r
1503 \r
1504 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1505 {\r
1506   char *e = argValue;\r
1507   int eff = 0;\r
1508 \r
1509   while (*e) {\r
1510     if (*e == 'b')      eff |= CFE_BOLD;\r
1511     else if (*e == 'i') eff |= CFE_ITALIC;\r
1512     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1513     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1514     else if (*e == '#' || isdigit(*e)) break;\r
1515     e++;\r
1516   }\r
1517   *effects = eff;\r
1518   *color   = ParseColorName(e);\r
1519 }\r
1520 \r
1521 \r
1522 BoardSize\r
1523 ParseBoardSize(char *name)\r
1524 {\r
1525   BoardSize bs = SizeTiny;\r
1526   while (sizeInfo[bs].name != NULL) {\r
1527     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1528     bs++;\r
1529   }\r
1530   ExitArgError("Unrecognized board size value", name);\r
1531   return bs; /* not reached */\r
1532 }\r
1533 \r
1534 \r
1535 char\r
1536 StringGet(void *getClosure)\r
1537 {\r
1538   char **p = (char **) getClosure;\r
1539   return *((*p)++);\r
1540 }\r
1541 \r
1542 char\r
1543 FileGet(void *getClosure)\r
1544 {\r
1545   int c;\r
1546   FILE* f = (FILE*) getClosure;\r
1547 \r
1548   c = getc(f);\r
1549   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1550   if (c == EOF)\r
1551     return NULLCHAR;\r
1552   else\r
1553     return (char) c;\r
1554 }\r
1555 \r
1556 /* Parse settings file named "name". If file found, return the\r
1557    full name in fullname and return TRUE; else return FALSE */\r
1558 BOOLEAN\r
1559 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1560 {\r
1561   char *dummy;\r
1562   FILE *f;\r
1563   int ok; char buf[MSG_SIZ];\r
1564 \r
1565   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1566   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1567     sprintf(buf, "%s.ini", name);\r
1568     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1569   }\r
1570   if (ok) {\r
1571     f = fopen(fullname, "r");\r
1572     if (f != NULL) {\r
1573       ParseArgs(FileGet, f);\r
1574       fclose(f);\r
1575       return TRUE;\r
1576     }\r
1577   }\r
1578   return FALSE;\r
1579 }\r
1580 \r
1581 VOID\r
1582 ParseArgs(GetFunc get, void *cl)\r
1583 {\r
1584   char argName[ARG_MAX];\r
1585   char argValue[ARG_MAX];\r
1586   ArgDescriptor *ad;\r
1587   char start;\r
1588   char *q;\r
1589   int i, octval;\r
1590   char ch;\r
1591   int posarg = 0;\r
1592 \r
1593   ch = get(cl);\r
1594   for (;;) {\r
1595     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1596     if (ch == NULLCHAR) break;\r
1597     if (ch == ';') {\r
1598       /* Comment to end of line */\r
1599       ch = get(cl);\r
1600       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1601       continue;\r
1602     } else if (ch == '/' || ch == '-') {\r
1603       /* Switch */\r
1604       q = argName;\r
1605       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1606              ch != '\n' && ch != '\t') {\r
1607         *q++ = ch;\r
1608         ch = get(cl);\r
1609       }\r
1610       *q = NULLCHAR;\r
1611 \r
1612       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1613         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1614 \r
1615       if (ad->argName == NULL)\r
1616         ExitArgError("Unrecognized argument", argName);\r
1617 \r
1618     } else if (ch == '@') {\r
1619       /* Indirection file */\r
1620       ad = &argDescriptorIndirection;\r
1621       ch = get(cl);\r
1622     } else {\r
1623       /* Positional argument */\r
1624       ad = &argDescriptors[posarg++];\r
1625       strcpy(argName, ad->argName);\r
1626     }\r
1627 \r
1628     if (ad->argType == ArgTrue) {\r
1629       *(Boolean *) ad->argLoc = TRUE;\r
1630       continue;\r
1631     }\r
1632     if (ad->argType == ArgFalse) {\r
1633       *(Boolean *) ad->argLoc = FALSE;\r
1634       continue;\r
1635     }\r
1636 \r
1637     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1638     if (ch == NULLCHAR || ch == '\n') {\r
1639       ExitArgError("No value provided for argument", argName);\r
1640     }\r
1641     q = argValue;\r
1642     if (ch == '{') {\r
1643       // Quoting with { }.  No characters have to (or can) be escaped.\r
1644       // Thus the string cannot contain a '}' character.\r
1645       start = ch;\r
1646       ch = get(cl);\r
1647       while (start) {\r
1648         switch (ch) {\r
1649         case NULLCHAR:\r
1650           start = NULLCHAR;\r
1651           break;\r
1652           \r
1653         case '}':\r
1654           ch = get(cl);\r
1655           start = NULLCHAR;\r
1656           break;\r
1657 \r
1658         default:\r
1659           *q++ = ch;\r
1660           ch = get(cl);\r
1661           break;\r
1662         }\r
1663       }   \r
1664     } else if (ch == '\'' || ch == '"') {\r
1665       // Quoting with ' ' or " ", with \ as escape character.\r
1666       // Inconvenient for long strings that may contain Windows filenames.\r
1667       start = ch;\r
1668       ch = get(cl);\r
1669       while (start) {\r
1670         switch (ch) {\r
1671         case NULLCHAR:\r
1672           start = NULLCHAR;\r
1673           break;\r
1674 \r
1675         default:\r
1676         not_special:\r
1677           *q++ = ch;\r
1678           ch = get(cl);\r
1679           break;\r
1680 \r
1681         case '\'':\r
1682         case '\"':\r
1683           if (ch == start) {\r
1684             ch = get(cl);\r
1685             start = NULLCHAR;\r
1686             break;\r
1687           } else {\r
1688             goto not_special;\r
1689           }\r
1690 \r
1691         case '\\':\r
1692           if (ad->argType == ArgFilename\r
1693               || ad->argType == ArgSettingsFilename) {\r
1694               goto not_special;\r
1695           }\r
1696           ch = get(cl);\r
1697           switch (ch) {\r
1698           case NULLCHAR:\r
1699             ExitArgError("Incomplete \\ escape in value for", argName);\r
1700             break;\r
1701           case 'n':\r
1702             *q++ = '\n';\r
1703             ch = get(cl);\r
1704             break;\r
1705           case 'r':\r
1706             *q++ = '\r';\r
1707             ch = get(cl);\r
1708             break;\r
1709           case 't':\r
1710             *q++ = '\t';\r
1711             ch = get(cl);\r
1712             break;\r
1713           case 'b':\r
1714             *q++ = '\b';\r
1715             ch = get(cl);\r
1716             break;\r
1717           case 'f':\r
1718             *q++ = '\f';\r
1719             ch = get(cl);\r
1720             break;\r
1721           default:\r
1722             octval = 0;\r
1723             for (i = 0; i < 3; i++) {\r
1724               if (ch >= '0' && ch <= '7') {\r
1725                 octval = octval*8 + (ch - '0');\r
1726                 ch = get(cl);\r
1727               } else {\r
1728                 break;\r
1729               }\r
1730             }\r
1731             if (i > 0) {\r
1732               *q++ = (char) octval;\r
1733             } else {\r
1734               *q++ = ch;\r
1735               ch = get(cl);\r
1736             }\r
1737             break;\r
1738           }\r
1739           break;\r
1740         }\r
1741       }\r
1742     } else {\r
1743       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1744         *q++ = ch;\r
1745         ch = get(cl);\r
1746       }\r
1747     }\r
1748     *q = NULLCHAR;\r
1749 \r
1750     switch (ad->argType) {\r
1751     case ArgInt:\r
1752       *(int *) ad->argLoc = atoi(argValue);\r
1753       break;\r
1754 \r
1755     case ArgX:\r
1756       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1757       break;\r
1758 \r
1759     case ArgY:\r
1760       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1761       break;\r
1762 \r
1763     case ArgZ:\r
1764       *(int *) ad->argLoc = atoi(argValue);\r
1765       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1766       break;\r
1767 \r
1768     case ArgFloat:\r
1769       *(float *) ad->argLoc = (float) atof(argValue);\r
1770       break;\r
1771 \r
1772     case ArgString:\r
1773     case ArgFilename:\r
1774       *(char **) ad->argLoc = strdup(argValue);\r
1775       break;\r
1776 \r
1777     case ArgSettingsFilename:\r
1778       {\r
1779         char fullname[MSG_SIZ];\r
1780         if (ParseSettingsFile(argValue, fullname)) {\r
1781           if (ad->argLoc != NULL) {\r
1782             *(char **) ad->argLoc = strdup(fullname);\r
1783           }\r
1784         } else {\r
1785           if (ad->argLoc != NULL) {\r
1786           } else {\r
1787             ExitArgError("Failed to open indirection file", argValue);\r
1788           }\r
1789         }\r
1790       }\r
1791       break;\r
1792 \r
1793     case ArgBoolean:\r
1794       switch (argValue[0]) {\r
1795       case 't':\r
1796       case 'T':\r
1797         *(Boolean *) ad->argLoc = TRUE;\r
1798         break;\r
1799       case 'f':\r
1800       case 'F':\r
1801         *(Boolean *) ad->argLoc = FALSE;\r
1802         break;\r
1803       default:\r
1804         ExitArgError("Unrecognized boolean argument value", argValue);\r
1805         break;\r
1806       }\r
1807       break;\r
1808 \r
1809     case ArgColor:\r
1810       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1811       break;\r
1812 \r
1813     case ArgAttribs: {\r
1814       ColorClass cc = (ColorClass)ad->argLoc;\r
1815       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1816       }\r
1817       break;\r
1818       \r
1819     case ArgBoardSize:\r
1820       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1821       break;\r
1822 \r
1823     case ArgFont:\r
1824       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1825       break;\r
1826 \r
1827     case ArgCommSettings:\r
1828       ParseCommSettings(argValue, &dcb);\r
1829       break;\r
1830 \r
1831     case ArgNone:\r
1832       ExitArgError("Unrecognized argument", argValue);\r
1833       break;\r
1834     case ArgTrue:\r
1835     case ArgFalse: ;\r
1836     }\r
1837   }\r
1838 }\r
1839 \r
1840 VOID\r
1841 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1842 {\r
1843   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1844   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1845   DeleteDC(hdc);\r
1846   lf->lfWidth = 0;\r
1847   lf->lfEscapement = 0;\r
1848   lf->lfOrientation = 0;\r
1849   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1850   lf->lfItalic = mfp->italic;\r
1851   lf->lfUnderline = mfp->underline;\r
1852   lf->lfStrikeOut = mfp->strikeout;\r
1853   lf->lfCharSet = DEFAULT_CHARSET;\r
1854   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1855   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1856   lf->lfQuality = DEFAULT_QUALITY;\r
1857   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1858   strcpy(lf->lfFaceName, mfp->faceName);\r
1859 }\r
1860 \r
1861 VOID\r
1862 CreateFontInMF(MyFont *mf)\r
1863 {\r
1864   LFfromMFP(&mf->lf, &mf->mfp);\r
1865   if (mf->hf) DeleteObject(mf->hf);\r
1866   mf->hf = CreateFontIndirect(&mf->lf);\r
1867 }\r
1868 \r
1869 VOID\r
1870 SetDefaultTextAttribs()\r
1871 {\r
1872   ColorClass cc;\r
1873   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1874     ParseAttribs(&textAttribs[cc].color, \r
1875                  &textAttribs[cc].effects, \r
1876                  defaultTextAttribs[cc]);\r
1877   }\r
1878 }\r
1879 \r
1880 VOID\r
1881 SetDefaultSounds()\r
1882 {\r
1883   ColorClass cc;\r
1884   SoundClass sc;\r
1885   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1886     textAttribs[cc].sound.name = strdup("");\r
1887     textAttribs[cc].sound.data = NULL;\r
1888   }\r
1889   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1890     sounds[sc].name = strdup("");\r
1891     sounds[sc].data = NULL;\r
1892   }\r
1893   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1894 }\r
1895 \r
1896 VOID\r
1897 LoadAllSounds()\r
1898 {\r
1899   ColorClass cc;\r
1900   SoundClass sc;\r
1901   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1902     MyLoadSound(&textAttribs[cc].sound);\r
1903   }\r
1904   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1905     MyLoadSound(&sounds[sc]);\r
1906   }\r
1907 }\r
1908 \r
1909 VOID\r
1910 InitAppData(LPSTR lpCmdLine)\r
1911 {\r
1912   int i, j;\r
1913   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1914   char *dummy, *p;\r
1915 \r
1916   programName = szAppName;\r
1917 \r
1918   /* Initialize to defaults */\r
1919   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1920   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1921   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1922   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1923   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1924   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1925   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1926   SetDefaultTextAttribs();\r
1927   SetDefaultSounds();\r
1928   appData.movesPerSession = MOVES_PER_SESSION;\r
1929   appData.initString = INIT_STRING;\r
1930   appData.secondInitString = INIT_STRING;\r
1931   appData.firstComputerString = COMPUTER_STRING;\r
1932   appData.secondComputerString = COMPUTER_STRING;\r
1933   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1934   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1935   appData.firstPlaysBlack = FALSE;\r
1936   appData.noChessProgram = FALSE;\r
1937   chessProgram = FALSE;\r
1938   appData.firstHost = FIRST_HOST;\r
1939   appData.secondHost = SECOND_HOST;\r
1940   appData.firstDirectory = FIRST_DIRECTORY;\r
1941   appData.secondDirectory = SECOND_DIRECTORY;\r
1942   appData.bitmapDirectory = "";\r
1943   appData.remoteShell = REMOTE_SHELL;\r
1944   appData.remoteUser = "";\r
1945   appData.timeDelay = TIME_DELAY;\r
1946   appData.timeControl = TIME_CONTROL;\r
1947   appData.timeIncrement = TIME_INCREMENT;\r
1948   appData.icsActive = FALSE;\r
1949   appData.icsHost = "";\r
1950   appData.icsPort = ICS_PORT;\r
1951   appData.icsCommPort = ICS_COMM_PORT;\r
1952   appData.icsLogon = ICS_LOGON;\r
1953   appData.icsHelper = "";\r
1954   appData.useTelnet = FALSE;\r
1955   appData.telnetProgram = TELNET_PROGRAM;\r
1956   appData.gateway = "";\r
1957   appData.loadGameFile = "";\r
1958   appData.loadGameIndex = 0;\r
1959   appData.saveGameFile = "";\r
1960   appData.autoSaveGames = FALSE;\r
1961   appData.loadPositionFile = "";\r
1962   appData.loadPositionIndex = 1;\r
1963   appData.savePositionFile = "";\r
1964   appData.matchMode = FALSE;\r
1965   appData.matchGames = 0;\r
1966   appData.monoMode = FALSE;\r
1967   appData.debugMode = FALSE;\r
1968   appData.clockMode = TRUE;\r
1969   boardSize = (BoardSize) -1; /* determine by screen size */\r
1970   appData.Iconic = FALSE; /*unused*/\r
1971   appData.searchTime = "";\r
1972   appData.searchDepth = 0;\r
1973   appData.showCoords = FALSE;\r
1974   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1975   appData.autoCallFlag = FALSE;\r
1976   appData.flipView = FALSE;\r
1977   appData.autoFlipView = TRUE;\r
1978   appData.cmailGameName = "";\r
1979   appData.alwaysPromoteToQueen = FALSE;\r
1980   appData.oldSaveStyle = FALSE;\r
1981   appData.quietPlay = FALSE;\r
1982   appData.showThinking = FALSE;\r
1983   appData.ponderNextMove = TRUE;\r
1984   appData.periodicUpdates = TRUE;\r
1985   appData.popupExitMessage = TRUE;\r
1986   appData.popupMoveErrors = FALSE;\r
1987   appData.autoObserve = FALSE;\r
1988   appData.autoComment = FALSE;\r
1989   appData.animate = TRUE;\r
1990   appData.animSpeed = 10;\r
1991   appData.animateDragging = TRUE;\r
1992   appData.highlightLastMove = TRUE;\r
1993   appData.getMoveList = TRUE;\r
1994   appData.testLegality = TRUE;\r
1995   appData.premove = TRUE;\r
1996   appData.premoveWhite = FALSE;\r
1997   appData.premoveWhiteText = "";\r
1998   appData.premoveBlack = FALSE;\r
1999   appData.premoveBlackText = "";\r
2000   appData.icsAlarm = TRUE;\r
2001   appData.icsAlarmTime = 5000;\r
2002   appData.autoRaiseBoard = TRUE;\r
2003   appData.localLineEditing = TRUE;\r
2004   appData.colorize = TRUE;\r
2005   appData.reuseFirst = TRUE;\r
2006   appData.reuseSecond = TRUE;\r
2007   appData.blindfold = FALSE;\r
2008   appData.icsEngineAnalyze = FALSE;\r
2009   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2010   dcb.DCBlength = sizeof(DCB);\r
2011   dcb.BaudRate = 9600;\r
2012   dcb.fBinary = TRUE;\r
2013   dcb.fParity = FALSE;\r
2014   dcb.fOutxCtsFlow = FALSE;\r
2015   dcb.fOutxDsrFlow = FALSE;\r
2016   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2017   dcb.fDsrSensitivity = FALSE;\r
2018   dcb.fTXContinueOnXoff = TRUE;\r
2019   dcb.fOutX = FALSE;\r
2020   dcb.fInX = FALSE;\r
2021   dcb.fNull = FALSE;\r
2022   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2023   dcb.fAbortOnError = FALSE;\r
2024   dcb.ByteSize = 7;\r
2025   dcb.Parity = SPACEPARITY;\r
2026   dcb.StopBits = ONESTOPBIT;\r
2027   settingsFileName = SETTINGS_FILE;\r
2028   saveSettingsOnExit = TRUE;\r
2029   boardX = CW_USEDEFAULT;\r
2030   boardY = CW_USEDEFAULT;\r
2031   analysisX = CW_USEDEFAULT; \r
2032   analysisY = CW_USEDEFAULT; \r
2033   analysisW = CW_USEDEFAULT;\r
2034   analysisH = CW_USEDEFAULT;\r
2035   commentX = CW_USEDEFAULT; \r
2036   commentY = CW_USEDEFAULT; \r
2037   commentW = CW_USEDEFAULT;\r
2038   commentH = CW_USEDEFAULT;\r
2039   editTagsX = CW_USEDEFAULT; \r
2040   editTagsY = CW_USEDEFAULT; \r
2041   editTagsW = CW_USEDEFAULT;\r
2042   editTagsH = CW_USEDEFAULT;\r
2043   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2044   icsNames = ICS_NAMES;\r
2045   firstChessProgramNames = FCP_NAMES;\r
2046   secondChessProgramNames = SCP_NAMES;\r
2047   appData.initialMode = "";\r
2048   appData.variant = "normal";\r
2049   appData.firstProtocolVersion = PROTOVER;\r
2050   appData.secondProtocolVersion = PROTOVER;\r
2051   appData.showButtonBar = TRUE;\r
2052 \r
2053    /* [AS] New properties (see comments in header file) */\r
2054   appData.firstScoreIsAbsolute = FALSE;\r
2055   appData.secondScoreIsAbsolute = FALSE;\r
2056   appData.saveExtendedInfoInPGN = FALSE;\r
2057   appData.hideThinkingFromHuman = FALSE;\r
2058   appData.liteBackTextureFile = "";\r
2059   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2060   appData.darkBackTextureFile = "";\r
2061   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2062   appData.renderPiecesWithFont = "";\r
2063   appData.fontToPieceTable = "";\r
2064   appData.fontBackColorWhite = 0;\r
2065   appData.fontForeColorWhite = 0;\r
2066   appData.fontBackColorBlack = 0;\r
2067   appData.fontForeColorBlack = 0;\r
2068   appData.fontPieceSize = 80;\r
2069   appData.overrideLineGap = 1;\r
2070   appData.adjudicateLossThreshold = 0;\r
2071   appData.delayBeforeQuit = 0;\r
2072   appData.delayAfterQuit = 0;\r
2073   appData.nameOfDebugFile = "winboard.debug";\r
2074   appData.pgnEventHeader = "Computer Chess Game";\r
2075   appData.defaultFrcPosition = -1;\r
2076   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2077   appData.saveOutOfBookInfo = TRUE;\r
2078   appData.showEvalInMoveHistory = TRUE;\r
2079   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2080   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2081   appData.highlightMoveWithArrow = FALSE;\r
2082   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2083   appData.useStickyWindows = TRUE;\r
2084   appData.adjudicateDrawMoves = 0;\r
2085   appData.autoDisplayComment = TRUE;\r
2086   appData.autoDisplayTags = TRUE;\r
2087   appData.firstIsUCI = FALSE;\r
2088   appData.secondIsUCI = FALSE;\r
2089   appData.firstHasOwnBookUCI = TRUE;\r
2090   appData.secondHasOwnBookUCI = TRUE;\r
2091   appData.polyglotDir = "";\r
2092   appData.usePolyglotBook = FALSE;\r
2093   appData.polyglotBook = "";\r
2094   appData.defaultHashSize = 64;\r
2095   appData.defaultCacheSizeEGTB = 4;\r
2096   appData.defaultPathEGTB = "c:\\egtb";\r
2097   appData.firstOptions = "";\r
2098   appData.secondOptions = "";\r
2099 \r
2100   InitWindowPlacement( &wpGameList );\r
2101   InitWindowPlacement( &wpMoveHistory );\r
2102   InitWindowPlacement( &wpEvalGraph );\r
2103   InitWindowPlacement( &wpEngineOutput );\r
2104   InitWindowPlacement( &wpConsole );\r
2105 \r
2106   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2107   appData.NrFiles      = -1;\r
2108   appData.NrRanks      = -1;\r
2109   appData.holdingsSize = -1;\r
2110   appData.testClaims   = FALSE;\r
2111   appData.checkMates   = FALSE;\r
2112   appData.materialDraws= FALSE;\r
2113   appData.trivialDraws = FALSE;\r
2114   appData.ruleMoves    = 51;\r
2115   appData.drawRepeats  = 6;\r
2116   appData.matchPause   = 10000;\r
2117   appData.alphaRank    = FALSE;\r
2118   appData.allWhite     = FALSE;\r
2119   appData.upsideDown   = FALSE;\r
2120   appData.serverPause  = 15;\r
2121   appData.serverMovesName   = NULL;\r
2122   appData.suppressLoadMoves = FALSE;\r
2123   appData.firstTimeOdds  = 1;\r
2124   appData.secondTimeOdds = 1;\r
2125   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2126   appData.secondAccumulateTC = 1;\r
2127   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2128   appData.secondNPS = -1;\r
2129   appData.engineComments = 1;\r
2130   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2131   appData.egtFormats = "";\r
2132 \r
2133 #ifdef ZIPPY\r
2134   appData.zippyTalk = ZIPPY_TALK;\r
2135   appData.zippyPlay = ZIPPY_PLAY;\r
2136   appData.zippyLines = ZIPPY_LINES;\r
2137   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2138   appData.zippyPassword = ZIPPY_PASSWORD;\r
2139   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2140   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2141   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2142   appData.zippyUseI = ZIPPY_USE_I;\r
2143   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2144   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2145   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2146   appData.zippyGameStart = ZIPPY_GAME_START;\r
2147   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2148   appData.zippyAbort = ZIPPY_ABORT;\r
2149   appData.zippyVariants = ZIPPY_VARIANTS;\r
2150   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2151   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2152 #endif\r
2153 \r
2154   /* Point font array elements to structures and\r
2155      parse default font names */\r
2156   for (i=0; i<NUM_FONTS; i++) {\r
2157     for (j=0; j<NUM_SIZES; j++) {\r
2158       font[j][i] = &fontRec[j][i];\r
2159       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2160     }\r
2161   }\r
2162   \r
2163   /* Parse default settings file if any */\r
2164   if (ParseSettingsFile(settingsFileName, buf)) {\r
2165     settingsFileName = strdup(buf);\r
2166   }\r
2167 \r
2168   /* Parse command line */\r
2169   ParseArgs(StringGet, &lpCmdLine);\r
2170 \r
2171   /* [HGM] make sure board size is acceptable */\r
2172   if(appData.NrFiles > BOARD_SIZE ||\r
2173      appData.NrRanks > BOARD_SIZE   )\r
2174       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2175 \r
2176   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2177    * with options from the command line, we now make an even higher priority\r
2178    * overrule by WB options attached to the engine command line. This so that\r
2179    * tournament managers can use WB options (such as /timeOdds) that follow\r
2180    * the engines.\r
2181    */\r
2182   if(appData.firstChessProgram != NULL) {\r
2183       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2184       static char *f = "first";\r
2185       char buf[MSG_SIZ], *q = buf;\r
2186       if(p != NULL) { // engine command line contains WinBoard options\r
2187           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2188           ParseArgs(StringGet, &q);\r
2189           p[-1] = 0; // cut them offengine command line\r
2190       }\r
2191   }\r
2192   // now do same for second chess program\r
2193   if(appData.secondChessProgram != NULL) {\r
2194       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2195       static char *s = "second";\r
2196       char buf[MSG_SIZ], *q = buf;\r
2197       if(p != NULL) { // engine command line contains WinBoard options\r
2198           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2199           ParseArgs(StringGet, &q);\r
2200           p[-1] = 0; // cut them offengine command line\r
2201       }\r
2202   }\r
2203 \r
2204 \r
2205   /* Propagate options that affect others */\r
2206   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2207   if (appData.icsActive || appData.noChessProgram) {\r
2208      chessProgram = FALSE;  /* not local chess program mode */\r
2209   }\r
2210 \r
2211   /* Open startup dialog if needed */\r
2212   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2213       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2214       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2215                         *appData.secondChessProgram == NULLCHAR))) {\r
2216     FARPROC lpProc;\r
2217     \r
2218     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2219     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2220     FreeProcInstance(lpProc);\r
2221   }\r
2222 \r
2223   /* Make sure save files land in the right (?) directory */\r
2224   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2225     appData.saveGameFile = strdup(buf);\r
2226   }\r
2227   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2228     appData.savePositionFile = strdup(buf);\r
2229   }\r
2230 \r
2231   /* Finish initialization for fonts and sounds */\r
2232   for (i=0; i<NUM_FONTS; i++) {\r
2233     for (j=0; j<NUM_SIZES; j++) {\r
2234       CreateFontInMF(font[j][i]);\r
2235     }\r
2236   }\r
2237   /* xboard, and older WinBoards, controlled the move sound with the\r
2238      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2239      always turn the option on (so that the backend will call us),\r
2240      then let the user turn the sound off by setting it to silence if\r
2241      desired.  To accommodate old winboard.ini files saved by old\r
2242      versions of WinBoard, we also turn off the sound if the option\r
2243      was initially set to false. */\r
2244   if (!appData.ringBellAfterMoves) {\r
2245     sounds[(int)SoundMove].name = strdup("");\r
2246     appData.ringBellAfterMoves = TRUE;\r
2247   }\r
2248   GetCurrentDirectory(MSG_SIZ, currDir);\r
2249   SetCurrentDirectory(installDir);\r
2250   LoadAllSounds();\r
2251   SetCurrentDirectory(currDir);\r
2252 \r
2253   p = icsTextMenuString;\r
2254   if (p[0] == '@') {\r
2255     FILE* f = fopen(p + 1, "r");\r
2256     if (f == NULL) {\r
2257       DisplayFatalError(p + 1, errno, 2);\r
2258       return;\r
2259     }\r
2260     i = fread(buf, 1, sizeof(buf)-1, f);\r
2261     fclose(f);\r
2262     buf[i] = NULLCHAR;\r
2263     p = buf;\r
2264   }\r
2265   ParseIcsTextMenu(strdup(p));\r
2266 }\r
2267 \r
2268 \r
2269 VOID\r
2270 InitMenuChecks()\r
2271 {\r
2272   HMENU hmenu = GetMenu(hwndMain);\r
2273 \r
2274   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2275                         MF_BYCOMMAND|((appData.icsActive &&\r
2276                                        *appData.icsCommPort != NULLCHAR) ?\r
2277                                       MF_ENABLED : MF_GRAYED));\r
2278   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2279                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2280                                      MF_CHECKED : MF_UNCHECKED));\r
2281 }\r
2282 \r
2283 \r
2284 VOID\r
2285 SaveSettings(char* name)\r
2286 {\r
2287   FILE *f;\r
2288   ArgDescriptor *ad;\r
2289   WINDOWPLACEMENT wp;\r
2290   char dir[MSG_SIZ];\r
2291 \r
2292   if (!hwndMain) return;\r
2293 \r
2294   GetCurrentDirectory(MSG_SIZ, dir);\r
2295   SetCurrentDirectory(installDir);\r
2296   f = fopen(name, "w");\r
2297   SetCurrentDirectory(dir);\r
2298   if (f == NULL) {\r
2299     DisplayError(name, errno);\r
2300     return;\r
2301   }\r
2302   fprintf(f, ";\n");\r
2303   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2304   fprintf(f, ";\n");\r
2305   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2306   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2307   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2308   fprintf(f, ";\n");\r
2309 \r
2310   wp.length = sizeof(WINDOWPLACEMENT);\r
2311   GetWindowPlacement(hwndMain, &wp);\r
2312   boardX = wp.rcNormalPosition.left;\r
2313   boardY = wp.rcNormalPosition.top;\r
2314 \r
2315   if (hwndConsole) {\r
2316     GetWindowPlacement(hwndConsole, &wp);\r
2317     wpConsole.x = wp.rcNormalPosition.left;\r
2318     wpConsole.y = wp.rcNormalPosition.top;\r
2319     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2320     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2321   }\r
2322 \r
2323   if (analysisDialog) {\r
2324     GetWindowPlacement(analysisDialog, &wp);\r
2325     analysisX = wp.rcNormalPosition.left;\r
2326     analysisY = wp.rcNormalPosition.top;\r
2327     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2328     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2329   }\r
2330 \r
2331   if (commentDialog) {\r
2332     GetWindowPlacement(commentDialog, &wp);\r
2333     commentX = wp.rcNormalPosition.left;\r
2334     commentY = wp.rcNormalPosition.top;\r
2335     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2336     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2337   }\r
2338 \r
2339   if (editTagsDialog) {\r
2340     GetWindowPlacement(editTagsDialog, &wp);\r
2341     editTagsX = wp.rcNormalPosition.left;\r
2342     editTagsY = wp.rcNormalPosition.top;\r
2343     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2344     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2345   }\r
2346 \r
2347   if (gameListDialog) {\r
2348     GetWindowPlacement(gameListDialog, &wp);\r
2349     wpGameList.x = wp.rcNormalPosition.left;\r
2350     wpGameList.y = wp.rcNormalPosition.top;\r
2351     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2352     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2353   }\r
2354 \r
2355   /* [AS] Move history */\r
2356   wpMoveHistory.visible = MoveHistoryIsUp();\r
2357   \r
2358   if( moveHistoryDialog ) {\r
2359     GetWindowPlacement(moveHistoryDialog, &wp);\r
2360     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2361     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2362     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2363     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2364   }\r
2365 \r
2366   /* [AS] Eval graph */\r
2367   wpEvalGraph.visible = EvalGraphIsUp();\r
2368 \r
2369   if( evalGraphDialog ) {\r
2370     GetWindowPlacement(evalGraphDialog, &wp);\r
2371     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2372     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2373     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2374     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2375   }\r
2376 \r
2377   /* [AS] Engine output */\r
2378   wpEngineOutput.visible = EngineOutputIsUp();\r
2379 \r
2380   if( engineOutputDialog ) {\r
2381     GetWindowPlacement(engineOutputDialog, &wp);\r
2382     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2383     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2384     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2385     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2386   }\r
2387 \r
2388   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2389     if (!ad->save) continue;\r
2390     switch (ad->argType) {\r
2391     case ArgString:\r
2392       {\r
2393         char *p = *(char **)ad->argLoc;\r
2394         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2395           /* Quote multiline values or \-containing values\r
2396              with { } if possible */\r
2397           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2398         } else {\r
2399           /* Else quote with " " */\r
2400           fprintf(f, "/%s=\"", ad->argName);\r
2401           while (*p) {\r
2402             if (*p == '\n') fprintf(f, "\n");\r
2403             else if (*p == '\r') fprintf(f, "\\r");\r
2404             else if (*p == '\t') fprintf(f, "\\t");\r
2405             else if (*p == '\b') fprintf(f, "\\b");\r
2406             else if (*p == '\f') fprintf(f, "\\f");\r
2407             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2408             else if (*p == '\"') fprintf(f, "\\\"");\r
2409             else if (*p == '\\') fprintf(f, "\\\\");\r
2410             else putc(*p, f);\r
2411             p++;\r
2412           }\r
2413           fprintf(f, "\"\n");\r
2414         }\r
2415       }\r
2416       break;\r
2417     case ArgInt:\r
2418     case ArgZ:\r
2419       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2420       break;\r
2421     case ArgX:\r
2422       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2423       break;\r
2424     case ArgY:\r
2425       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2426       break;\r
2427     case ArgFloat:\r
2428       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2429       break;\r
2430     case ArgBoolean:\r
2431       fprintf(f, "/%s=%s\n", ad->argName, \r
2432         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2433       break;\r
2434     case ArgTrue:\r
2435       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2436       break;\r
2437     case ArgFalse:\r
2438       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2439       break;\r
2440     case ArgColor:\r
2441       {\r
2442         COLORREF color = *(COLORREF *)ad->argLoc;\r
2443         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2444           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2445       }\r
2446       break;\r
2447     case ArgAttribs:\r
2448       {\r
2449         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2450         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2451           (ta->effects & CFE_BOLD) ? "b" : "",\r
2452           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2453           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2454           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2455           (ta->effects) ? " " : "",\r
2456           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2457       }\r
2458       break;\r
2459     case ArgFilename:\r
2460       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2461         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2462       } else {\r
2463         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2464       }\r
2465       break;\r
2466     case ArgBoardSize:\r
2467       fprintf(f, "/%s=%s\n", ad->argName,\r
2468               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2469       break;\r
2470     case ArgFont:\r
2471       {\r
2472         int bs;\r
2473         for (bs=0; bs<NUM_SIZES; bs++) {\r
2474           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2475           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2476           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2477             ad->argName, mfp->faceName, mfp->pointSize,\r
2478             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2479             mfp->bold ? "b" : "",\r
2480             mfp->italic ? "i" : "",\r
2481             mfp->underline ? "u" : "",\r
2482             mfp->strikeout ? "s" : "");\r
2483         }\r
2484       }\r
2485       break;\r
2486     case ArgCommSettings:\r
2487       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2488     case ArgNone:\r
2489     case ArgSettingsFilename: ;\r
2490     }\r
2491   }\r
2492   fclose(f);\r
2493 }\r
2494 \r
2495 \r
2496 \r
2497 /*---------------------------------------------------------------------------*\\r
2498  *\r
2499  * GDI board drawing routines\r
2500  *\r
2501 \*---------------------------------------------------------------------------*/\r
2502 \r
2503 /* [AS] Draw square using background texture */\r
2504 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2505 {\r
2506     XFORM   x;\r
2507 \r
2508     if( mode == 0 ) {\r
2509         return; /* Should never happen! */\r
2510     }\r
2511 \r
2512     SetGraphicsMode( dst, GM_ADVANCED );\r
2513 \r
2514     switch( mode ) {\r
2515     case 1:\r
2516         /* Identity */\r
2517         break;\r
2518     case 2:\r
2519         /* X reflection */\r
2520         x.eM11 = -1.0;\r
2521         x.eM12 = 0;\r
2522         x.eM21 = 0;\r
2523         x.eM22 = 1.0;\r
2524         x.eDx = (FLOAT) dw + dx - 1;\r
2525         x.eDy = 0;\r
2526         dx = 0;\r
2527         SetWorldTransform( dst, &x );\r
2528         break;\r
2529     case 3:\r
2530         /* Y reflection */\r
2531         x.eM11 = 1.0;\r
2532         x.eM12 = 0;\r
2533         x.eM21 = 0;\r
2534         x.eM22 = -1.0;\r
2535         x.eDx = 0;\r
2536         x.eDy = (FLOAT) dh + dy - 1;\r
2537         dy = 0;\r
2538         SetWorldTransform( dst, &x );\r
2539         break;\r
2540     case 4:\r
2541         /* X/Y flip */\r
2542         x.eM11 = 0;\r
2543         x.eM12 = 1.0;\r
2544         x.eM21 = 1.0;\r
2545         x.eM22 = 0;\r
2546         x.eDx = (FLOAT) dx;\r
2547         x.eDy = (FLOAT) dy;\r
2548         dx = 0;\r
2549         dy = 0;\r
2550         SetWorldTransform( dst, &x );\r
2551         break;\r
2552     }\r
2553 \r
2554     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2555 \r
2556     x.eM11 = 1.0;\r
2557     x.eM12 = 0;\r
2558     x.eM21 = 0;\r
2559     x.eM22 = 1.0;\r
2560     x.eDx = 0;\r
2561     x.eDy = 0;\r
2562     SetWorldTransform( dst, &x );\r
2563 \r
2564     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2565 }\r
2566 \r
2567 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2568 enum {\r
2569     PM_WP = (int) WhitePawn, \r
2570     PM_WN = (int) WhiteKnight, \r
2571     PM_WB = (int) WhiteBishop, \r
2572     PM_WR = (int) WhiteRook, \r
2573     PM_WQ = (int) WhiteQueen, \r
2574     PM_WF = (int) WhiteFerz, \r
2575     PM_WW = (int) WhiteWazir, \r
2576     PM_WE = (int) WhiteAlfil, \r
2577     PM_WM = (int) WhiteMan, \r
2578     PM_WO = (int) WhiteCannon, \r
2579     PM_WU = (int) WhiteUnicorn, \r
2580     PM_WH = (int) WhiteNightrider, \r
2581     PM_WA = (int) WhiteAngel, \r
2582     PM_WC = (int) WhiteMarshall, \r
2583     PM_WAB = (int) WhiteCardinal, \r
2584     PM_WD = (int) WhiteDragon, \r
2585     PM_WL = (int) WhiteLance, \r
2586     PM_WS = (int) WhiteCobra, \r
2587     PM_WV = (int) WhiteFalcon, \r
2588     PM_WSG = (int) WhiteSilver, \r
2589     PM_WG = (int) WhiteGrasshopper, \r
2590     PM_WK = (int) WhiteKing,\r
2591     PM_BP = (int) BlackPawn, \r
2592     PM_BN = (int) BlackKnight, \r
2593     PM_BB = (int) BlackBishop, \r
2594     PM_BR = (int) BlackRook, \r
2595     PM_BQ = (int) BlackQueen, \r
2596     PM_BF = (int) BlackFerz, \r
2597     PM_BW = (int) BlackWazir, \r
2598     PM_BE = (int) BlackAlfil, \r
2599     PM_BM = (int) BlackMan,\r
2600     PM_BO = (int) BlackCannon, \r
2601     PM_BU = (int) BlackUnicorn, \r
2602     PM_BH = (int) BlackNightrider, \r
2603     PM_BA = (int) BlackAngel, \r
2604     PM_BC = (int) BlackMarshall, \r
2605     PM_BG = (int) BlackGrasshopper, \r
2606     PM_BAB = (int) BlackCardinal,\r
2607     PM_BD = (int) BlackDragon,\r
2608     PM_BL = (int) BlackLance,\r
2609     PM_BS = (int) BlackCobra,\r
2610     PM_BV = (int) BlackFalcon,\r
2611     PM_BSG = (int) BlackSilver,\r
2612     PM_BK = (int) BlackKing\r
2613 };\r
2614 \r
2615 static HFONT hPieceFont = NULL;\r
2616 static HBITMAP hPieceMask[(int) EmptySquare];\r
2617 static HBITMAP hPieceFace[(int) EmptySquare];\r
2618 static int fontBitmapSquareSize = 0;\r
2619 static char pieceToFontChar[(int) EmptySquare] =\r
2620                               { 'p', 'n', 'b', 'r', 'q', \r
2621                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2622                       'k', 'o', 'm', 'v', 't', 'w', \r
2623                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2624                                                               'l' };\r
2625 \r
2626 extern BOOL SetCharTable( char *table, const char * map );\r
2627 /* [HGM] moved to backend.c */\r
2628 \r
2629 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2630 {\r
2631     HBRUSH hbrush;\r
2632     BYTE r1 = GetRValue( color );\r
2633     BYTE g1 = GetGValue( color );\r
2634     BYTE b1 = GetBValue( color );\r
2635     BYTE r2 = r1 / 2;\r
2636     BYTE g2 = g1 / 2;\r
2637     BYTE b2 = b1 / 2;\r
2638     RECT rc;\r
2639 \r
2640     /* Create a uniform background first */\r
2641     hbrush = CreateSolidBrush( color );\r
2642     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2643     FillRect( hdc, &rc, hbrush );\r
2644     DeleteObject( hbrush );\r
2645     \r
2646     if( mode == 1 ) {\r
2647         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2648         int steps = squareSize / 2;\r
2649         int i;\r
2650 \r
2651         for( i=0; i<steps; i++ ) {\r
2652             BYTE r = r1 - (r1-r2) * i / steps;\r
2653             BYTE g = g1 - (g1-g2) * i / steps;\r
2654             BYTE b = b1 - (b1-b2) * i / steps;\r
2655 \r
2656             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2657             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2658             FillRect( hdc, &rc, hbrush );\r
2659             DeleteObject(hbrush);\r
2660         }\r
2661     }\r
2662     else if( mode == 2 ) {\r
2663         /* Diagonal gradient, good more or less for every piece */\r
2664         POINT triangle[3];\r
2665         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2666         HBRUSH hbrush_old;\r
2667         int steps = squareSize;\r
2668         int i;\r
2669 \r
2670         triangle[0].x = squareSize - steps;\r
2671         triangle[0].y = squareSize;\r
2672         triangle[1].x = squareSize;\r
2673         triangle[1].y = squareSize;\r
2674         triangle[2].x = squareSize;\r
2675         triangle[2].y = squareSize - steps;\r
2676 \r
2677         for( i=0; i<steps; i++ ) {\r
2678             BYTE r = r1 - (r1-r2) * i / steps;\r
2679             BYTE g = g1 - (g1-g2) * i / steps;\r
2680             BYTE b = b1 - (b1-b2) * i / steps;\r
2681 \r
2682             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2683             hbrush_old = SelectObject( hdc, hbrush );\r
2684             Polygon( hdc, triangle, 3 );\r
2685             SelectObject( hdc, hbrush_old );\r
2686             DeleteObject(hbrush);\r
2687             triangle[0].x++;\r
2688             triangle[2].y++;\r
2689         }\r
2690 \r
2691         SelectObject( hdc, hpen );\r
2692     }\r
2693 }\r
2694 \r
2695 /*\r
2696     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2697     seems to work ok. The main problem here is to find the "inside" of a chess\r
2698     piece: follow the steps as explained below.\r
2699 */\r
2700 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2701 {\r
2702     HBITMAP hbm;\r
2703     HBITMAP hbm_old;\r
2704     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2705     RECT rc;\r
2706     SIZE sz;\r
2707     POINT pt;\r
2708     int backColor = whitePieceColor; \r
2709     int foreColor = blackPieceColor;\r
2710     \r
2711     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2712         backColor = appData.fontBackColorWhite;\r
2713         foreColor = appData.fontForeColorWhite;\r
2714     }\r
2715     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2716         backColor = appData.fontBackColorBlack;\r
2717         foreColor = appData.fontForeColorBlack;\r
2718     }\r
2719 \r
2720     /* Mask */\r
2721     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2722 \r
2723     hbm_old = SelectObject( hdc, hbm );\r
2724 \r
2725     rc.left = 0;\r
2726     rc.top = 0;\r
2727     rc.right = squareSize;\r
2728     rc.bottom = squareSize;\r
2729 \r
2730     /* Step 1: background is now black */\r
2731     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2732 \r
2733     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2734 \r
2735     pt.x = (squareSize - sz.cx) / 2;\r
2736     pt.y = (squareSize - sz.cy) / 2;\r
2737 \r
2738     SetBkMode( hdc, TRANSPARENT );\r
2739     SetTextColor( hdc, chroma );\r
2740     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2741     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2742 \r
2743     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2744     /* Step 3: the area outside the piece is filled with white */\r
2745 //    FloodFill( hdc, 0, 0, chroma );\r
2746     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2747     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2748     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2749     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2750     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2751     /* \r
2752         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2753         but if the start point is not inside the piece we're lost!\r
2754         There should be a better way to do this... if we could create a region or path\r
2755         from the fill operation we would be fine for example.\r
2756     */\r
2757 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2758     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2759 \r
2760     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2761         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2762         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2763 \r
2764         SelectObject( dc2, bm2 );\r
2765         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2766         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2767         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2768         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2769         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2770 \r
2771         DeleteDC( dc2 );\r
2772         DeleteObject( bm2 );\r
2773     }\r
2774 \r
2775     SetTextColor( hdc, 0 );\r
2776     /* \r
2777         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2778         draw the piece again in black for safety.\r
2779     */\r
2780     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2781 \r
2782     SelectObject( hdc, hbm_old );\r
2783 \r
2784     if( hPieceMask[index] != NULL ) {\r
2785         DeleteObject( hPieceMask[index] );\r
2786     }\r
2787 \r
2788     hPieceMask[index] = hbm;\r
2789 \r
2790     /* Face */\r
2791     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2792 \r
2793     SelectObject( hdc, hbm );\r
2794 \r
2795     {\r
2796         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2797         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2798         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2799 \r
2800         SelectObject( dc1, hPieceMask[index] );\r
2801         SelectObject( dc2, bm2 );\r
2802         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2803         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2804         \r
2805         /* \r
2806             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2807             the piece background and deletes (makes transparent) the rest.\r
2808             Thanks to that mask, we are free to paint the background with the greates\r
2809             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2810             We use this, to make gradients and give the pieces a "roundish" look.\r
2811         */\r
2812         SetPieceBackground( hdc, backColor, 2 );\r
2813         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2814 \r
2815         DeleteDC( dc2 );\r
2816         DeleteDC( dc1 );\r
2817         DeleteObject( bm2 );\r
2818     }\r
2819 \r
2820     SetTextColor( hdc, foreColor );\r
2821     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2822 \r
2823     SelectObject( hdc, hbm_old );\r
2824 \r
2825     if( hPieceFace[index] != NULL ) {\r
2826         DeleteObject( hPieceFace[index] );\r
2827     }\r
2828 \r
2829     hPieceFace[index] = hbm;\r
2830 }\r
2831 \r
2832 static int TranslatePieceToFontPiece( int piece )\r
2833 {\r
2834     switch( piece ) {\r
2835     case BlackPawn:\r
2836         return PM_BP;\r
2837     case BlackKnight:\r
2838         return PM_BN;\r
2839     case BlackBishop:\r
2840         return PM_BB;\r
2841     case BlackRook:\r
2842         return PM_BR;\r
2843     case BlackQueen:\r
2844         return PM_BQ;\r
2845     case BlackKing:\r
2846         return PM_BK;\r
2847     case WhitePawn:\r
2848         return PM_WP;\r
2849     case WhiteKnight:\r
2850         return PM_WN;\r
2851     case WhiteBishop:\r
2852         return PM_WB;\r
2853     case WhiteRook:\r
2854         return PM_WR;\r
2855     case WhiteQueen:\r
2856         return PM_WQ;\r
2857     case WhiteKing:\r
2858         return PM_WK;\r
2859 \r
2860     case BlackAngel:\r
2861         return PM_BA;\r
2862     case BlackMarshall:\r
2863         return PM_BC;\r
2864     case BlackFerz:\r
2865         return PM_BF;\r
2866     case BlackNightrider:\r
2867         return PM_BH;\r
2868     case BlackAlfil:\r
2869         return PM_BE;\r
2870     case BlackWazir:\r
2871         return PM_BW;\r
2872     case BlackUnicorn:\r
2873         return PM_BU;\r
2874     case BlackCannon:\r
2875         return PM_BO;\r
2876     case BlackGrasshopper:\r
2877         return PM_BG;\r
2878     case BlackMan:\r
2879         return PM_BM;\r
2880     case BlackSilver:\r
2881         return PM_BSG;\r
2882     case BlackLance:\r
2883         return PM_BL;\r
2884     case BlackFalcon:\r
2885         return PM_BV;\r
2886     case BlackCobra:\r
2887         return PM_BS;\r
2888     case BlackCardinal:\r
2889         return PM_BAB;\r
2890     case BlackDragon:\r
2891         return PM_BD;\r
2892 \r
2893     case WhiteAngel:\r
2894         return PM_WA;\r
2895     case WhiteMarshall:\r
2896         return PM_WC;\r
2897     case WhiteFerz:\r
2898         return PM_WF;\r
2899     case WhiteNightrider:\r
2900         return PM_WH;\r
2901     case WhiteAlfil:\r
2902         return PM_WE;\r
2903     case WhiteWazir:\r
2904         return PM_WW;\r
2905     case WhiteUnicorn:\r
2906         return PM_WU;\r
2907     case WhiteCannon:\r
2908         return PM_WO;\r
2909     case WhiteGrasshopper:\r
2910         return PM_WG;\r
2911     case WhiteMan:\r
2912         return PM_WM;\r
2913     case WhiteSilver:\r
2914         return PM_WSG;\r
2915     case WhiteLance:\r
2916         return PM_WL;\r
2917     case WhiteFalcon:\r
2918         return PM_WV;\r
2919     case WhiteCobra:\r
2920         return PM_WS;\r
2921     case WhiteCardinal:\r
2922         return PM_WAB;\r
2923     case WhiteDragon:\r
2924         return PM_WD;\r
2925     }\r
2926 \r
2927     return 0;\r
2928 }\r
2929 \r
2930 void CreatePiecesFromFont()\r
2931 {\r
2932     LOGFONT lf;\r
2933     HDC hdc_window = NULL;\r
2934     HDC hdc = NULL;\r
2935     HFONT hfont_old;\r
2936     int fontHeight;\r
2937     int i;\r
2938 \r
2939     if( fontBitmapSquareSize < 0 ) {\r
2940         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2941         return;\r
2942     }\r
2943 \r
2944     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2945         fontBitmapSquareSize = -1;\r
2946         return;\r
2947     }\r
2948 \r
2949     if( fontBitmapSquareSize != squareSize ) {\r
2950         hdc_window = GetDC( hwndMain );\r
2951         hdc = CreateCompatibleDC( hdc_window );\r
2952 \r
2953         if( hPieceFont != NULL ) {\r
2954             DeleteObject( hPieceFont );\r
2955         }\r
2956         else {\r
2957             for( i=0; i<=(int)BlackKing; i++ ) {\r
2958                 hPieceMask[i] = NULL;\r
2959                 hPieceFace[i] = NULL;\r
2960             }\r
2961         }\r
2962 \r
2963         fontHeight = 75;\r
2964 \r
2965         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2966             fontHeight = appData.fontPieceSize;\r
2967         }\r
2968 \r
2969         fontHeight = (fontHeight * squareSize) / 100;\r
2970 \r
2971         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2972         lf.lfWidth = 0;\r
2973         lf.lfEscapement = 0;\r
2974         lf.lfOrientation = 0;\r
2975         lf.lfWeight = FW_NORMAL;\r
2976         lf.lfItalic = 0;\r
2977         lf.lfUnderline = 0;\r
2978         lf.lfStrikeOut = 0;\r
2979         lf.lfCharSet = DEFAULT_CHARSET;\r
2980         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2981         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2982         lf.lfQuality = PROOF_QUALITY;\r
2983         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2984         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2985         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2986 \r
2987         hPieceFont = CreateFontIndirect( &lf );\r
2988 \r
2989         if( hPieceFont == NULL ) {\r
2990             fontBitmapSquareSize = -2;\r
2991         }\r
2992         else {\r
2993             /* Setup font-to-piece character table */\r
2994             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2995                 /* No (or wrong) global settings, try to detect the font */\r
2996                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2997                     /* Alpha */\r
2998                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2999                 }\r
3000                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3001                     /* DiagramTT* family */\r
3002                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3003                 }\r
3004                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3005                     /* Fairy symbols */\r
3006                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3007                 }\r
3008                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3009                     /* Good Companion (Some characters get warped as literal :-( */\r
3010                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3011                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3012                     SetCharTable(pieceToFontChar, s);\r
3013                 }\r
3014                 else {\r
3015                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3016                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3017                 }\r
3018             }\r
3019 \r
3020             /* Create bitmaps */\r
3021             hfont_old = SelectObject( hdc, hPieceFont );\r
3022 #if 0\r
3023             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3024             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3033             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3035 \r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3068 #else\r
3069             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3070                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3071                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3072 #endif\r
3073             SelectObject( hdc, hfont_old );\r
3074 \r
3075             fontBitmapSquareSize = squareSize;\r
3076         }\r
3077     }\r
3078 \r
3079     if( hdc != NULL ) {\r
3080         DeleteDC( hdc );\r
3081     }\r
3082 \r
3083     if( hdc_window != NULL ) {\r
3084         ReleaseDC( hwndMain, hdc_window );\r
3085     }\r
3086 }\r
3087 \r
3088 HBITMAP\r
3089 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3090 {\r
3091   char name[128];\r
3092 \r
3093   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3094   if (gameInfo.event &&\r
3095       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3096       strcmp(name, "k80s") == 0) {\r
3097     strcpy(name, "tim");\r
3098   }\r
3099   return LoadBitmap(hinst, name);\r
3100 }\r
3101 \r
3102 \r
3103 /* Insert a color into the program's logical palette\r
3104    structure.  This code assumes the given color is\r
3105    the result of the RGB or PALETTERGB macro, and it\r
3106    knows how those macros work (which is documented).\r
3107 */\r
3108 VOID\r
3109 InsertInPalette(COLORREF color)\r
3110 {\r
3111   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3112 \r
3113   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3114     DisplayFatalError("Too many colors", 0, 1);\r
3115     pLogPal->palNumEntries--;\r
3116     return;\r
3117   }\r
3118 \r
3119   pe->peFlags = (char) 0;\r
3120   pe->peRed = (char) (0xFF & color);\r
3121   pe->peGreen = (char) (0xFF & (color >> 8));\r
3122   pe->peBlue = (char) (0xFF & (color >> 16));\r
3123   return;\r
3124 }\r
3125 \r
3126 \r
3127 VOID\r
3128 InitDrawingColors()\r
3129 {\r
3130   if (pLogPal == NULL) {\r
3131     /* Allocate enough memory for a logical palette with\r
3132      * PALETTESIZE entries and set the size and version fields\r
3133      * of the logical palette structure.\r
3134      */\r
3135     pLogPal = (NPLOGPALETTE)\r
3136       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3137                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3138     pLogPal->palVersion    = 0x300;\r
3139   }\r
3140   pLogPal->palNumEntries = 0;\r
3141 \r
3142   InsertInPalette(lightSquareColor);\r
3143   InsertInPalette(darkSquareColor);\r
3144   InsertInPalette(whitePieceColor);\r
3145   InsertInPalette(blackPieceColor);\r
3146   InsertInPalette(highlightSquareColor);\r
3147   InsertInPalette(premoveHighlightColor);\r
3148 \r
3149   /*  create a logical color palette according the information\r
3150    *  in the LOGPALETTE structure.\r
3151    */\r
3152   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3153 \r
3154   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3155   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3156   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3157   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3158   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3159   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3160   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3161   /* [AS] Force rendering of the font-based pieces */\r
3162   if( fontBitmapSquareSize > 0 ) {\r
3163     fontBitmapSquareSize = 0;\r
3164   }\r
3165 }\r
3166 \r
3167 \r
3168 int\r
3169 BoardWidth(int boardSize, int n)\r
3170 { /* [HGM] argument n added to allow different width and height */\r
3171   int lineGap = sizeInfo[boardSize].lineGap;\r
3172 \r
3173   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3174       lineGap = appData.overrideLineGap;\r
3175   }\r
3176 \r
3177   return (n + 1) * lineGap +\r
3178           n * sizeInfo[boardSize].squareSize;\r
3179 }\r
3180 \r
3181 /* Respond to board resize by dragging edge */\r
3182 VOID\r
3183 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3184 {\r
3185   BoardSize newSize = NUM_SIZES - 1;\r
3186   static int recurse = 0;\r
3187   if (IsIconic(hwndMain)) return;\r
3188   if (recurse > 0) return;\r
3189   recurse++;\r
3190   while (newSize > 0) {\r
3191         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3192         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3193            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3194     newSize--;\r
3195   } \r
3196   boardSize = newSize;\r
3197   InitDrawingSizes(boardSize, flags);\r
3198   recurse--;\r
3199 }\r
3200 \r
3201 \r
3202 \r
3203 VOID\r
3204 InitDrawingSizes(BoardSize boardSize, int flags)\r
3205 {\r
3206   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3207   ChessSquare piece;\r
3208   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3209   HDC hdc;\r
3210   SIZE clockSize, messageSize;\r
3211   HFONT oldFont;\r
3212   char buf[MSG_SIZ];\r
3213   char *str;\r
3214   HMENU hmenu = GetMenu(hwndMain);\r
3215   RECT crect, wrect, oldRect;\r
3216   int offby;\r
3217   LOGBRUSH logbrush;\r
3218 \r
3219   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3220   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3221 \r
3222   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3223   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3224 \r
3225   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3226   oldRect.top = boardY;\r
3227   oldRect.right = boardX + winWidth;\r
3228   oldRect.bottom = boardY + winHeight;\r
3229 \r
3230   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3231   smallLayout = sizeInfo[boardSize].smallLayout;\r
3232   squareSize = sizeInfo[boardSize].squareSize;\r
3233   lineGap = sizeInfo[boardSize].lineGap;\r
3234   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3235 \r
3236   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3237       lineGap = appData.overrideLineGap;\r
3238   }\r
3239 \r
3240   if (tinyLayout != oldTinyLayout) {\r
3241     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3242     if (tinyLayout) {\r
3243       style &= ~WS_SYSMENU;\r
3244       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3245                  "&Minimize\tCtrl+F4");\r
3246     } else {\r
3247       style |= WS_SYSMENU;\r
3248       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3249     }\r
3250     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3251 \r
3252     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3253       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3254         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3255     }\r
3256     DrawMenuBar(hwndMain);\r
3257   }\r
3258 \r
3259   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3260   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3261 \r
3262   /* Get text area sizes */\r
3263   hdc = GetDC(hwndMain);\r
3264   if (appData.clockMode) {\r
3265     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3266   } else {\r
3267     sprintf(buf, "White");\r
3268   }\r
3269   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3270   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3271   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3272   str = "We only care about the height here";\r
3273   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3274   SelectObject(hdc, oldFont);\r
3275   ReleaseDC(hwndMain, hdc);\r
3276 \r
3277   /* Compute where everything goes */\r
3278   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3279         /* [HGM] logo: if either logo is on, reserve space for it */\r
3280         logoHeight =  2*clockSize.cy;\r
3281         leftLogoRect.left   = OUTER_MARGIN;\r
3282         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3283         leftLogoRect.top    = OUTER_MARGIN;\r
3284         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3285 \r
3286         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3287         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3288         rightLogoRect.top    = OUTER_MARGIN;\r
3289         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3290 \r
3291 \r
3292     whiteRect.left = leftLogoRect.right;\r
3293     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3294     whiteRect.top = OUTER_MARGIN;\r
3295     whiteRect.bottom = whiteRect.top + logoHeight;\r
3296 \r
3297     blackRect.right = rightLogoRect.left;\r
3298     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3299     blackRect.top = whiteRect.top;\r
3300     blackRect.bottom = whiteRect.bottom;\r
3301   } else {\r
3302     whiteRect.left = OUTER_MARGIN;\r
3303     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3304     whiteRect.top = OUTER_MARGIN;\r
3305     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3306 \r
3307     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3308     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3309     blackRect.top = whiteRect.top;\r
3310     blackRect.bottom = whiteRect.bottom;\r
3311   }\r
3312 \r
3313   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3314   if (appData.showButtonBar) {\r
3315     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3316       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3317   } else {\r
3318     messageRect.right = OUTER_MARGIN + boardWidth;\r
3319   }\r
3320   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3321   messageRect.bottom = messageRect.top + messageSize.cy;\r
3322 \r
3323   boardRect.left = OUTER_MARGIN;\r
3324   boardRect.right = boardRect.left + boardWidth;\r
3325   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3326   boardRect.bottom = boardRect.top + boardHeight;\r
3327 \r
3328   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3329   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3330   oldBoardSize = boardSize;\r
3331   oldTinyLayout = tinyLayout;\r
3332   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3333   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3334     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3335   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3336   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3337   winHeight = winH; //       without disturbing window attachments\r
3338   GetWindowRect(hwndMain, &wrect);\r
3339   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3340                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3341 \r
3342   // [HGM] placement: let attached windows follow size change.\r
3343   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3344   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3345   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3346   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3347   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3348 \r
3349   /* compensate if menu bar wrapped */\r
3350   GetClientRect(hwndMain, &crect);\r
3351   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3352   winHeight += offby;\r
3353   switch (flags) {\r
3354   case WMSZ_TOPLEFT:\r
3355     SetWindowPos(hwndMain, NULL, \r
3356                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3357                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3358     break;\r
3359 \r
3360   case WMSZ_TOPRIGHT:\r
3361   case WMSZ_TOP:\r
3362     SetWindowPos(hwndMain, NULL, \r
3363                  wrect.left, wrect.bottom - winHeight, \r
3364                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3365     break;\r
3366 \r
3367   case WMSZ_BOTTOMLEFT:\r
3368   case WMSZ_LEFT:\r
3369     SetWindowPos(hwndMain, NULL, \r
3370                  wrect.right - winWidth, wrect.top, \r
3371                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3372     break;\r
3373 \r
3374   case WMSZ_BOTTOMRIGHT:\r
3375   case WMSZ_BOTTOM:\r
3376   case WMSZ_RIGHT:\r
3377   default:\r
3378     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3379                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3380     break;\r
3381   }\r
3382 \r
3383   hwndPause = NULL;\r
3384   for (i = 0; i < N_BUTTONS; i++) {\r
3385     if (buttonDesc[i].hwnd != NULL) {\r
3386       DestroyWindow(buttonDesc[i].hwnd);\r
3387       buttonDesc[i].hwnd = NULL;\r
3388     }\r
3389     if (appData.showButtonBar) {\r
3390       buttonDesc[i].hwnd =\r
3391         CreateWindow("BUTTON", buttonDesc[i].label,\r
3392                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3393                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3394                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3395                      (HMENU) buttonDesc[i].id,\r
3396                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3397       if (tinyLayout) {\r
3398         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3399                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3400                     MAKELPARAM(FALSE, 0));\r
3401       }\r
3402       if (buttonDesc[i].id == IDM_Pause)\r
3403         hwndPause = buttonDesc[i].hwnd;\r
3404       buttonDesc[i].wndproc = (WNDPROC)\r
3405         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3406     }\r
3407   }\r
3408   if (gridPen != NULL) DeleteObject(gridPen);\r
3409   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3410   if (premovePen != NULL) DeleteObject(premovePen);\r
3411   if (lineGap != 0) {\r
3412     logbrush.lbStyle = BS_SOLID;\r
3413     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3414     gridPen =\r
3415       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3416                    lineGap, &logbrush, 0, NULL);\r
3417     logbrush.lbColor = highlightSquareColor;\r
3418     highlightPen =\r
3419       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3420                    lineGap, &logbrush, 0, NULL);\r
3421 \r
3422     logbrush.lbColor = premoveHighlightColor; \r
3423     premovePen =\r
3424       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3425                    lineGap, &logbrush, 0, NULL);\r
3426 \r
3427     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3428     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3429       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3430       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3431         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3432       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3433         BOARD_WIDTH * (squareSize + lineGap);\r
3434       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3435     }\r
3436     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3437       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3438       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3439         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3440         lineGap / 2 + (i * (squareSize + lineGap));\r
3441       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3442         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3443       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3444     }\r
3445   }\r
3446 \r
3447   /* [HGM] Licensing requirement */\r
3448 #ifdef GOTHIC\r
3449   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3450 #endif\r
3451 #ifdef FALCON\r
3452   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3453 #endif\r
3454   GothicPopUp( "", VariantNormal);\r
3455 \r
3456 \r
3457 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3458 \r
3459   /* Load piece bitmaps for this board size */\r
3460   for (i=0; i<=2; i++) {\r
3461     for (piece = WhitePawn;\r
3462          (int) piece < (int) BlackPawn;\r
3463          piece = (ChessSquare) ((int) piece + 1)) {\r
3464       if (pieceBitmap[i][piece] != NULL)\r
3465         DeleteObject(pieceBitmap[i][piece]);\r
3466     }\r
3467   }\r
3468 \r
3469   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3470   // Orthodox Chess pieces\r
3471   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3472   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3473   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3474   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3475   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3476   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3477   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3478   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3479   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3480   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3481   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3482   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3483   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3484   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3485   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3486   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3487     // in Shogi, Hijack the unused Queen for Lance\r
3488     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3489     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3490     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3491   } else {\r
3492     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3493     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3494     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3495   }\r
3496 \r
3497   if(squareSize <= 72 && squareSize >= 33) { \r
3498     /* A & C are available in most sizes now */\r
3499     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3500       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3501       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3502       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3503       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3504       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3505       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3506       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3507       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3508       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3509       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3510       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3511       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3512     } else { // Smirf-like\r
3513       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3514       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3515       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3516     }\r
3517     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3518       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3519       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3520       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3521     } else { // WinBoard standard\r
3522       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3523       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3524       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3525     }\r
3526   }\r
3527 \r
3528 \r
3529   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3530     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3531     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3532     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3533     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3534     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3535     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3536     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3537     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3538     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3539     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3540     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3541     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3542     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3543     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3544     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3545     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3546     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3547     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3548     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3549     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3550     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3551     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3552     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3553     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3554     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3555     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3556     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3557     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3558     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3559     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3560 \r
3561     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3562       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3563       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3564       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3565       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3566       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3567       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3568       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3569       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3570       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3571       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3572       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3573       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3574     } else {\r
3575       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3576       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3577       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3578       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3579       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3580       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3581       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3582       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3583       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3584       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3585       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3586       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3587     }\r
3588 \r
3589   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3590     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3591     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3592     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3593     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3594     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3595     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3596     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3597     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3598     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3599     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3600     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3601     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3602     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3603     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3604   }\r
3605 \r
3606 \r
3607   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3608   /* special Shogi support in this size */\r
3609   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3610       for (piece = WhitePawn;\r
3611            (int) piece < (int) BlackPawn;\r
3612            piece = (ChessSquare) ((int) piece + 1)) {\r
3613         if (pieceBitmap[i][piece] != NULL)\r
3614           DeleteObject(pieceBitmap[i][piece]);\r
3615       }\r
3616     }\r
3617   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3618   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3619   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3620   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3621   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3622   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3623   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3624   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3625   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3626   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3627   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3628   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3629   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3630   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3631   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3632   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3633   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3634   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3635   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3636   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3637   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3638   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3639   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3640   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3641   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3642   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3643   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3644   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3645   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3646   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3647   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3648   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3649   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3650   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3651   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3652   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3653   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3654   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3655   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3656   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3657   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3658   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3659   minorSize = 0;\r
3660   }\r
3661 }\r
3662 \r
3663 HBITMAP\r
3664 PieceBitmap(ChessSquare p, int kind)\r
3665 {\r
3666   if ((int) p >= (int) BlackPawn)\r
3667     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3668 \r
3669   return pieceBitmap[kind][(int) p];\r
3670 }\r
3671 \r
3672 /***************************************************************/\r
3673 \r
3674 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3675 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3676 /*\r
3677 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3678 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3679 */\r
3680 \r
3681 VOID\r
3682 SquareToPos(int row, int column, int * x, int * y)\r
3683 {\r
3684   if (flipView) {\r
3685     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3686     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3687   } else {\r
3688     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3689     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3690   }\r
3691 }\r
3692 \r
3693 VOID\r
3694 DrawCoordsOnDC(HDC hdc)\r
3695 {\r
3696   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
3697   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
3698   char str[2] = { NULLCHAR, NULLCHAR };\r
3699   int oldMode, oldAlign, x, y, start, i;\r
3700   HFONT oldFont;\r
3701   HBRUSH oldBrush;\r
3702 \r
3703   if (!appData.showCoords)\r
3704     return;\r
3705 \r
3706   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3707 \r
3708   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3709   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3710   oldAlign = GetTextAlign(hdc);\r
3711   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3712 \r
3713   y = boardRect.top + lineGap;\r
3714   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3715 \r
3716   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3717   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3718     str[0] = files[start + i];\r
3719     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3720     y += squareSize + lineGap;\r
3721   }\r
3722 \r
3723   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3724 \r
3725   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3726   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3727     str[0] = ranks[start + i];\r
3728     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3729     x += squareSize + lineGap;\r
3730   }    \r
3731 \r
3732   SelectObject(hdc, oldBrush);\r
3733   SetBkMode(hdc, oldMode);\r
3734   SetTextAlign(hdc, oldAlign);\r
3735   SelectObject(hdc, oldFont);\r
3736 }\r
3737 \r
3738 VOID\r
3739 DrawGridOnDC(HDC hdc)\r
3740 {\r
3741   HPEN oldPen;\r
3742  \r
3743   if (lineGap != 0) {\r
3744     oldPen = SelectObject(hdc, gridPen);\r
3745     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3746     SelectObject(hdc, oldPen);\r
3747   }\r
3748 }\r
3749 \r
3750 #define HIGHLIGHT_PEN 0\r
3751 #define PREMOVE_PEN   1\r
3752 \r
3753 VOID\r
3754 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3755 {\r
3756   int x1, y1;\r
3757   HPEN oldPen, hPen;\r
3758   if (lineGap == 0) return;\r
3759   if (flipView) {\r
3760     x1 = boardRect.left +\r
3761       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3762     y1 = boardRect.top +\r
3763       lineGap/2 + y * (squareSize + lineGap);\r
3764   } else {\r
3765     x1 = boardRect.left +\r
3766       lineGap/2 + x * (squareSize + lineGap);\r
3767     y1 = boardRect.top +\r
3768       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3769   }\r
3770   hPen = pen ? premovePen : highlightPen;\r
3771   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3772   MoveToEx(hdc, x1, y1, NULL);\r
3773   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3774   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3775   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3776   LineTo(hdc, x1, y1);\r
3777   SelectObject(hdc, oldPen);\r
3778 }\r
3779 \r
3780 VOID\r
3781 DrawHighlightsOnDC(HDC hdc)\r
3782 {\r
3783   int i;\r
3784   for (i=0; i<2; i++) {\r
3785     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3786       DrawHighlightOnDC(hdc, TRUE,\r
3787                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3788                         HIGHLIGHT_PEN);\r
3789   }\r
3790   for (i=0; i<2; i++) {\r
3791     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3792         premoveHighlightInfo.sq[i].y >= 0) {\r
3793         DrawHighlightOnDC(hdc, TRUE,\r
3794                           premoveHighlightInfo.sq[i].x, \r
3795                           premoveHighlightInfo.sq[i].y,\r
3796                           PREMOVE_PEN);\r
3797     }\r
3798   }\r
3799 }\r
3800 \r
3801 /* Note: sqcolor is used only in monoMode */\r
3802 /* Note that this code is largely duplicated in woptions.c,\r
3803    function DrawSampleSquare, so that needs to be updated too */\r
3804 VOID\r
3805 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3806 {\r
3807   HBITMAP oldBitmap;\r
3808   HBRUSH oldBrush;\r
3809   int tmpSize;\r
3810 \r
3811   if (appData.blindfold) return;\r
3812 \r
3813   /* [AS] Use font-based pieces if needed */\r
3814   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3815     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3816     CreatePiecesFromFont();\r
3817 \r
3818     if( fontBitmapSquareSize == squareSize ) {\r
3819         int index = TranslatePieceToFontPiece(piece);\r
3820 \r
3821         SelectObject( tmphdc, hPieceMask[ index ] );\r
3822 \r
3823         BitBlt( hdc,\r
3824             x, y,\r
3825             squareSize, squareSize,\r
3826             tmphdc,\r
3827             0, 0,\r
3828             SRCAND );\r
3829 \r
3830         SelectObject( tmphdc, hPieceFace[ index ] );\r
3831 \r
3832         BitBlt( hdc,\r
3833             x, y,\r
3834             squareSize, squareSize,\r
3835             tmphdc,\r
3836             0, 0,\r
3837             SRCPAINT );\r
3838 \r
3839         return;\r
3840     }\r
3841   }\r
3842 \r
3843   if (appData.monoMode) {\r
3844     SelectObject(tmphdc, PieceBitmap(piece, \r
3845       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3846     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3847            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3848   } else {\r
3849     tmpSize = squareSize;\r
3850     if(minorSize &&\r
3851         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3852          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3853       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3854       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3855       x += (squareSize - minorSize)>>1;\r
3856       y += squareSize - minorSize - 2;\r
3857       tmpSize = minorSize;\r
3858     }\r
3859     if (color || appData.allWhite ) {\r
3860       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3861       if( color )\r
3862               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3863       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3864       if(appData.upsideDown && color==flipView)\r
3865         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3866       else\r
3867         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3868 #if 0\r
3869       /* Use black piece color for outline of white pieces */\r
3870       /* Not sure this looks really good (though xboard does it).\r
3871          Maybe better to have another selectable color, default black */\r
3872       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3873       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3874       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3875 #else\r
3876       /* Use black for outline of white pieces */\r
3877       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3878       if(appData.upsideDown && color==flipView)\r
3879         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3880       else\r
3881         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3882 #endif\r
3883     } else {\r
3884 #if 0\r
3885       /* Use white piece color for details of black pieces */\r
3886       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3887          WHITE_PIECE ones aren't always the right shape. */\r
3888       /* Not sure this looks really good (though xboard does it).\r
3889          Maybe better to have another selectable color, default medium gray? */\r
3890       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3891       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3892       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3893       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3894       SelectObject(hdc, blackPieceBrush);\r
3895       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3896 #else\r
3897       /* Use square color for details of black pieces */\r
3898       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3899       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3900       if(appData.upsideDown && !flipView)\r
3901         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3902       else\r
3903         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3904 #endif\r
3905     }\r
3906     SelectObject(hdc, oldBrush);\r
3907     SelectObject(tmphdc, oldBitmap);\r
3908   }\r
3909 }\r
3910 \r
3911 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3912 int GetBackTextureMode( int algo )\r
3913 {\r
3914     int result = BACK_TEXTURE_MODE_DISABLED;\r
3915 \r
3916     switch( algo ) \r
3917     {\r
3918         case BACK_TEXTURE_MODE_PLAIN:\r
3919             result = 1; /* Always use identity map */\r
3920             break;\r
3921         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3922             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3923             break;\r
3924     }\r
3925 \r
3926     return result;\r
3927 }\r
3928 \r
3929 /* \r
3930     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3931     to handle redraws cleanly (as random numbers would always be different).\r
3932 */\r
3933 VOID RebuildTextureSquareInfo()\r
3934 {\r
3935     BITMAP bi;\r
3936     int lite_w = 0;\r
3937     int lite_h = 0;\r
3938     int dark_w = 0;\r
3939     int dark_h = 0;\r
3940     int row;\r
3941     int col;\r
3942 \r
3943     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3944 \r
3945     if( liteBackTexture != NULL ) {\r
3946         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3947             lite_w = bi.bmWidth;\r
3948             lite_h = bi.bmHeight;\r
3949         }\r
3950     }\r
3951 \r
3952     if( darkBackTexture != NULL ) {\r
3953         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3954             dark_w = bi.bmWidth;\r
3955             dark_h = bi.bmHeight;\r
3956         }\r
3957     }\r
3958 \r
3959     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3960         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3961             if( (col + row) & 1 ) {\r
3962                 /* Lite square */\r
3963                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3964                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3965                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3966                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3967                 }\r
3968             }\r
3969             else {\r
3970                 /* Dark square */\r
3971                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3972                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3973                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3974                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3975                 }\r
3976             }\r
3977         }\r
3978     }\r
3979 }\r
3980 \r
3981 /* [AS] Arrow highlighting support */\r
3982 \r
3983 static int A_WIDTH = 5; /* Width of arrow body */\r
3984 \r
3985 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3986 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3987 \r
3988 static double Sqr( double x )\r
3989 {\r
3990     return x*x;\r
3991 }\r
3992 \r
3993 static int Round( double x )\r
3994 {\r
3995     return (int) (x + 0.5);\r
3996 }\r
3997 \r
3998 /* Draw an arrow between two points using current settings */\r
3999 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4000 {\r
4001     POINT arrow[7];\r
4002     double dx, dy, j, k, x, y;\r
4003 \r
4004     if( d_x == s_x ) {\r
4005         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4006 \r
4007         arrow[0].x = s_x + A_WIDTH;\r
4008         arrow[0].y = s_y;\r
4009 \r
4010         arrow[1].x = s_x + A_WIDTH;\r
4011         arrow[1].y = d_y - h;\r
4012 \r
4013         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4014         arrow[2].y = d_y - h;\r
4015 \r
4016         arrow[3].x = d_x;\r
4017         arrow[3].y = d_y;\r
4018 \r
4019         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4020         arrow[4].y = d_y - h;\r
4021 \r
4022         arrow[5].x = s_x - A_WIDTH;\r
4023         arrow[5].y = d_y - h;\r
4024 \r
4025         arrow[6].x = s_x - A_WIDTH;\r
4026         arrow[6].y = s_y;\r
4027     }\r
4028     else if( d_y == s_y ) {\r
4029         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4030 \r
4031         arrow[0].x = s_x;\r
4032         arrow[0].y = s_y + A_WIDTH;\r
4033 \r
4034         arrow[1].x = d_x - w;\r
4035         arrow[1].y = s_y + A_WIDTH;\r
4036 \r
4037         arrow[2].x = d_x - w;\r
4038         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4039 \r
4040         arrow[3].x = d_x;\r
4041         arrow[3].y = d_y;\r
4042 \r
4043         arrow[4].x = d_x - w;\r
4044         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4045 \r
4046         arrow[5].x = d_x - w;\r
4047         arrow[5].y = s_y - A_WIDTH;\r
4048 \r
4049         arrow[6].x = s_x;\r
4050         arrow[6].y = s_y - A_WIDTH;\r
4051     }\r
4052     else {\r
4053         /* [AS] Needed a lot of paper for this! :-) */\r
4054         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4055         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4056   \r
4057         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4058 \r
4059         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4060 \r
4061         x = s_x;\r
4062         y = s_y;\r
4063 \r
4064         arrow[0].x = Round(x - j);\r
4065         arrow[0].y = Round(y + j*dx);\r
4066 \r
4067         arrow[1].x = Round(x + j);\r
4068         arrow[1].y = Round(y - j*dx);\r
4069 \r
4070         if( d_x > s_x ) {\r
4071             x = (double) d_x - k;\r
4072             y = (double) d_y - k*dy;\r
4073         }\r
4074         else {\r
4075             x = (double) d_x + k;\r
4076             y = (double) d_y + k*dy;\r
4077         }\r
4078 \r
4079         arrow[2].x = Round(x + j);\r
4080         arrow[2].y = Round(y - j*dx);\r
4081 \r
4082         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4083         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4084 \r
4085         arrow[4].x = d_x;\r
4086         arrow[4].y = d_y;\r
4087 \r
4088         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4089         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4090 \r
4091         arrow[6].x = Round(x - j);\r
4092         arrow[6].y = Round(y + j*dx);\r
4093     }\r
4094 \r
4095     Polygon( hdc, arrow, 7 );\r
4096 }\r
4097 \r
4098 /* [AS] Draw an arrow between two squares */\r
4099 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4100 {\r
4101     int s_x, s_y, d_x, d_y;\r
4102     HPEN hpen;\r
4103     HPEN holdpen;\r
4104     HBRUSH hbrush;\r
4105     HBRUSH holdbrush;\r
4106     LOGBRUSH stLB;\r
4107 \r
4108     if( s_col == d_col && s_row == d_row ) {\r
4109         return;\r
4110     }\r
4111 \r
4112     /* Get source and destination points */\r
4113     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4114     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4115 \r
4116     if( d_y > s_y ) {\r
4117         d_y += squareSize / 4;\r
4118     }\r
4119     else if( d_y < s_y ) {\r
4120         d_y += 3 * squareSize / 4;\r
4121     }\r
4122     else {\r
4123         d_y += squareSize / 2;\r
4124     }\r
4125 \r
4126     if( d_x > s_x ) {\r
4127         d_x += squareSize / 4;\r
4128     }\r
4129     else if( d_x < s_x ) {\r
4130         d_x += 3 * squareSize / 4;\r
4131     }\r
4132     else {\r
4133         d_x += squareSize / 2;\r
4134     }\r
4135 \r
4136     s_x += squareSize / 2;\r
4137     s_y += squareSize / 2;\r
4138 \r
4139     /* Adjust width */\r
4140     A_WIDTH = squareSize / 14;\r
4141 \r
4142     /* Draw */\r
4143     stLB.lbStyle = BS_SOLID;\r
4144     stLB.lbColor = appData.highlightArrowColor;\r
4145     stLB.lbHatch = 0;\r
4146 \r
4147     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4148     holdpen = SelectObject( hdc, hpen );\r
4149     hbrush = CreateBrushIndirect( &stLB );\r
4150     holdbrush = SelectObject( hdc, hbrush );\r
4151 \r
4152     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4153 \r
4154     SelectObject( hdc, holdpen );\r
4155     SelectObject( hdc, holdbrush );\r
4156     DeleteObject( hpen );\r
4157     DeleteObject( hbrush );\r
4158 }\r
4159 \r
4160 BOOL HasHighlightInfo()\r
4161 {\r
4162     BOOL result = FALSE;\r
4163 \r
4164     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4165         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4166     {\r
4167         result = TRUE;\r
4168     }\r
4169 \r
4170     return result;\r
4171 }\r
4172 \r
4173 BOOL IsDrawArrowEnabled()\r
4174 {\r
4175     BOOL result = FALSE;\r
4176 \r
4177     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4178         result = TRUE;\r
4179     }\r
4180 \r
4181     return result;\r
4182 }\r
4183 \r
4184 VOID DrawArrowHighlight( HDC hdc )\r
4185 {\r
4186     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4187         DrawArrowBetweenSquares( hdc,\r
4188             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4189             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4190     }\r
4191 }\r
4192 \r
4193 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4194 {\r
4195     HRGN result = NULL;\r
4196 \r
4197     if( HasHighlightInfo() ) {\r
4198         int x1, y1, x2, y2;\r
4199         int sx, sy, dx, dy;\r
4200 \r
4201         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4202         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4203 \r
4204         sx = MIN( x1, x2 );\r
4205         sy = MIN( y1, y2 );\r
4206         dx = MAX( x1, x2 ) + squareSize;\r
4207         dy = MAX( y1, y2 ) + squareSize;\r
4208 \r
4209         result = CreateRectRgn( sx, sy, dx, dy );\r
4210     }\r
4211 \r
4212     return result;\r
4213 }\r
4214 \r
4215 /*\r
4216     Warning: this function modifies the behavior of several other functions. \r
4217     \r
4218     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4219     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4220     repaint is scattered all over the place, which is not good for features such as\r
4221     "arrow highlighting" that require a full repaint of the board.\r
4222 \r
4223     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4224     user interaction, when speed is not so important) but especially to avoid errors\r
4225     in the displayed graphics.\r
4226 \r
4227     In such patched places, I always try refer to this function so there is a single\r
4228     place to maintain knowledge.\r
4229     \r
4230     To restore the original behavior, just return FALSE unconditionally.\r
4231 */\r
4232 BOOL IsFullRepaintPreferrable()\r
4233 {\r
4234     BOOL result = FALSE;\r
4235 \r
4236     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4237         /* Arrow may appear on the board */\r
4238         result = TRUE;\r
4239     }\r
4240 \r
4241     return result;\r
4242 }\r
4243 \r
4244 /* \r
4245     This function is called by DrawPosition to know whether a full repaint must\r
4246     be forced or not.\r
4247 \r
4248     Only DrawPosition may directly call this function, which makes use of \r
4249     some state information. Other function should call DrawPosition specifying \r
4250     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4251 */\r
4252 BOOL DrawPositionNeedsFullRepaint()\r
4253 {\r
4254     BOOL result = FALSE;\r
4255 \r
4256     /* \r
4257         Probably a slightly better policy would be to trigger a full repaint\r
4258         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4259         but animation is fast enough that it's difficult to notice.\r
4260     */\r
4261     if( animInfo.piece == EmptySquare ) {\r
4262         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4263             result = TRUE;\r
4264         }\r
4265     }\r
4266 \r
4267     return result;\r
4268 }\r
4269 \r
4270 VOID\r
4271 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4272 {\r
4273   int row, column, x, y, square_color, piece_color;\r
4274   ChessSquare piece;\r
4275   HBRUSH oldBrush;\r
4276   HDC texture_hdc = NULL;\r
4277 \r
4278   /* [AS] Initialize background textures if needed */\r
4279   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4280       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4281       if( backTextureSquareSize != squareSize \r
4282        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4283           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4284           backTextureSquareSize = squareSize;\r
4285           RebuildTextureSquareInfo();\r
4286       }\r
4287 \r
4288       texture_hdc = CreateCompatibleDC( hdc );\r
4289   }\r
4290 \r
4291   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4292     for (column = 0; column < BOARD_WIDTH; column++) {\r
4293   \r
4294       SquareToPos(row, column, &x, &y);\r
4295 \r
4296       piece = board[row][column];\r
4297 \r
4298       square_color = ((column + row) % 2) == 1;\r
4299       if( gameInfo.variant == VariantXiangqi ) {\r
4300           square_color = !InPalace(row, column);\r
4301           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4302           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4303       }\r
4304       piece_color = (int) piece < (int) BlackPawn;\r
4305 \r
4306 \r
4307       /* [HGM] holdings file: light square or black */\r
4308       if(column == BOARD_LEFT-2) {\r
4309             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4310                 square_color = 1;\r
4311             else {\r
4312                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4313                 continue;\r
4314             }\r
4315       } else\r
4316       if(column == BOARD_RGHT + 1 ) {\r
4317             if( row < gameInfo.holdingsSize )\r
4318                 square_color = 1;\r
4319             else {\r
4320                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4321                 continue;\r
4322             }\r
4323       }\r
4324       if(column == BOARD_LEFT-1 ) /* left align */\r
4325             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4326       else if( column == BOARD_RGHT) /* right align */\r
4327             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4328       else\r
4329       if (appData.monoMode) {\r
4330         if (piece == EmptySquare) {\r
4331           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4332                  square_color ? WHITENESS : BLACKNESS);\r
4333         } else {\r
4334           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4335         }\r
4336       } \r
4337       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4338           /* [AS] Draw the square using a texture bitmap */\r
4339           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4340           int r = row, c = column; // [HGM] do not flip board in flipView\r
4341           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4342 \r
4343           DrawTile( x, y, \r
4344               squareSize, squareSize, \r
4345               hdc, \r
4346               texture_hdc,\r
4347               backTextureSquareInfo[r][c].mode,\r
4348               backTextureSquareInfo[r][c].x,\r
4349               backTextureSquareInfo[r][c].y );\r
4350 \r
4351           SelectObject( texture_hdc, hbm );\r
4352 \r
4353           if (piece != EmptySquare) {\r
4354               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4355           }\r
4356       }\r
4357       else {\r
4358         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4359 \r
4360         oldBrush = SelectObject(hdc, brush );\r
4361         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4362         SelectObject(hdc, oldBrush);\r
4363         if (piece != EmptySquare)\r
4364           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4365       }\r
4366     }\r
4367   }\r
4368 \r
4369   if( texture_hdc != NULL ) {\r
4370     DeleteDC( texture_hdc );\r
4371   }\r
4372 }\r
4373 \r
4374 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4375 void fputDW(FILE *f, int x)\r
4376 {\r
4377         fputc(x     & 255, f);\r
4378         fputc(x>>8  & 255, f);\r
4379         fputc(x>>16 & 255, f);\r
4380         fputc(x>>24 & 255, f);\r
4381 }\r
4382 \r
4383 #define MAX_CLIPS 200   /* more than enough */\r
4384 \r
4385 VOID\r
4386 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4387 {\r
4388 //  HBITMAP bufferBitmap;\r
4389   BITMAP bi;\r
4390 //  RECT Rect;\r
4391   HDC tmphdc;\r
4392   HBITMAP hbm;\r
4393   int w = 100, h = 50;\r
4394 \r
4395   if(logo == NULL) return;\r
4396 //  GetClientRect(hwndMain, &Rect);\r
4397 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4398 //                                      Rect.bottom-Rect.top+1);\r
4399   tmphdc = CreateCompatibleDC(hdc);\r
4400   hbm = SelectObject(tmphdc, logo);\r
4401   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4402             w = bi.bmWidth;\r
4403             h = bi.bmHeight;\r
4404   }\r
4405   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4406                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4407   SelectObject(tmphdc, hbm);\r
4408   DeleteDC(tmphdc);\r
4409 }\r
4410 \r
4411 VOID\r
4412 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4413 {\r
4414   static Board lastReq, lastDrawn;\r
4415   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4416   static int lastDrawnFlipView = 0;\r
4417   static int lastReqValid = 0, lastDrawnValid = 0;\r
4418   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4419   HDC tmphdc;\r
4420   HDC hdcmem;\r
4421   HBITMAP bufferBitmap;\r
4422   HBITMAP oldBitmap;\r
4423   RECT Rect;\r
4424   HRGN clips[MAX_CLIPS];\r
4425   ChessSquare dragged_piece = EmptySquare;\r
4426 \r
4427   /* I'm undecided on this - this function figures out whether a full\r
4428    * repaint is necessary on its own, so there's no real reason to have the\r
4429    * caller tell it that.  I think this can safely be set to FALSE - but\r
4430    * if we trust the callers not to request full repaints unnessesarily, then\r
4431    * we could skip some clipping work.  In other words, only request a full\r
4432    * redraw when the majority of pieces have changed positions (ie. flip, \r
4433    * gamestart and similar)  --Hawk\r
4434    */\r
4435   Boolean fullrepaint = repaint;\r
4436 \r
4437   if( DrawPositionNeedsFullRepaint() ) {\r
4438       fullrepaint = TRUE;\r
4439   }\r
4440 \r
4441 #if 0\r
4442   if( fullrepaint ) {\r
4443       static int repaint_count = 0;\r
4444       char buf[128];\r
4445 \r
4446       repaint_count++;\r
4447       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4448       OutputDebugString( buf );\r
4449   }\r
4450 #endif\r
4451 \r
4452   if (board == NULL) {\r
4453     if (!lastReqValid) {\r
4454       return;\r
4455     }\r
4456     board = lastReq;\r
4457   } else {\r
4458     CopyBoard(lastReq, board);\r
4459     lastReqValid = 1;\r
4460   }\r
4461 \r
4462   if (doingSizing) {\r
4463     return;\r
4464   }\r
4465 \r
4466   if (IsIconic(hwndMain)) {\r
4467     return;\r
4468   }\r
4469 \r
4470   if (hdc == NULL) {\r
4471     hdc = GetDC(hwndMain);\r
4472     if (!appData.monoMode) {\r
4473       SelectPalette(hdc, hPal, FALSE);\r
4474       RealizePalette(hdc);\r
4475     }\r
4476     releaseDC = TRUE;\r
4477   } else {\r
4478     releaseDC = FALSE;\r
4479   }\r
4480 \r
4481 #if 0\r
4482   fprintf(debugFP, "*******************************\n"\r
4483                    "repaint = %s\n"\r
4484                    "dragInfo.from (%d,%d)\n"\r
4485                    "dragInfo.start (%d,%d)\n"\r
4486                    "dragInfo.pos (%d,%d)\n"\r
4487                    "dragInfo.lastpos (%d,%d)\n", \r
4488                     repaint ? "TRUE" : "FALSE",\r
4489                     dragInfo.from.x, dragInfo.from.y, \r
4490                     dragInfo.start.x, dragInfo.start.y,\r
4491                     dragInfo.pos.x, dragInfo.pos.y,\r
4492                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4493   fprintf(debugFP, "prev:  ");\r
4494   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4495     for (column = 0; column < BOARD_WIDTH; column++) {\r
4496       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4497     }\r
4498   }\r
4499   fprintf(debugFP, "\n");\r
4500   fprintf(debugFP, "board: ");\r
4501   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4502     for (column = 0; column < BOARD_WIDTH; column++) {\r
4503       fprintf(debugFP, "%d ", board[row][column]);\r
4504     }\r
4505   }\r
4506   fprintf(debugFP, "\n");\r
4507   fflush(debugFP);\r
4508 #endif\r
4509 \r
4510   /* Create some work-DCs */\r
4511   hdcmem = CreateCompatibleDC(hdc);\r
4512   tmphdc = CreateCompatibleDC(hdc);\r
4513 \r
4514   /* If dragging is in progress, we temporarely remove the piece */\r
4515   /* [HGM] or temporarily decrease count if stacked              */\r
4516   /*       !! Moved to before board compare !!                   */\r
4517   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4518     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4519     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4520             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4521         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4522     } else \r
4523     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4524             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4525         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4526     } else \r
4527         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4528   }\r
4529 \r
4530   /* Figure out which squares need updating by comparing the \r
4531    * newest board with the last drawn board and checking if\r
4532    * flipping has changed.\r
4533    */\r
4534   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4535     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4536       for (column = 0; column < BOARD_WIDTH; column++) {\r
4537         if (lastDrawn[row][column] != board[row][column]) {\r
4538           SquareToPos(row, column, &x, &y);\r
4539           clips[num_clips++] =\r
4540             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4541         }\r
4542       }\r
4543     }\r
4544     for (i=0; i<2; i++) {\r
4545       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4546           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4547         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4548             lastDrawnHighlight.sq[i].y >= 0) {\r
4549           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4550                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4551           clips[num_clips++] =\r
4552             CreateRectRgn(x - lineGap, y - lineGap, \r
4553                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4554         }\r
4555         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4556           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4557           clips[num_clips++] =\r
4558             CreateRectRgn(x - lineGap, y - lineGap, \r
4559                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4560         }\r
4561       }\r
4562     }\r
4563     for (i=0; i<2; i++) {\r
4564       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4565           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4566         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4567             lastDrawnPremove.sq[i].y >= 0) {\r
4568           SquareToPos(lastDrawnPremove.sq[i].y,\r
4569                       lastDrawnPremove.sq[i].x, &x, &y);\r
4570           clips[num_clips++] =\r
4571             CreateRectRgn(x - lineGap, y - lineGap, \r
4572                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4573         }\r
4574         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4575             premoveHighlightInfo.sq[i].y >= 0) {\r
4576           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4577                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4578           clips[num_clips++] =\r
4579             CreateRectRgn(x - lineGap, y - lineGap, \r
4580                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4581         }\r
4582       }\r
4583     }\r
4584   } else {\r
4585     fullrepaint = TRUE;\r
4586   }\r
4587 \r
4588   /* Create a buffer bitmap - this is the actual bitmap\r
4589    * being written to.  When all the work is done, we can\r
4590    * copy it to the real DC (the screen).  This avoids\r
4591    * the problems with flickering.\r
4592    */\r
4593   GetClientRect(hwndMain, &Rect);\r
4594   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4595                                         Rect.bottom-Rect.top+1);\r
4596   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4597   if (!appData.monoMode) {\r
4598     SelectPalette(hdcmem, hPal, FALSE);\r
4599   }\r
4600 \r
4601   /* Create clips for dragging */\r
4602   if (!fullrepaint) {\r
4603     if (dragInfo.from.x >= 0) {\r
4604       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4605       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4606     }\r
4607     if (dragInfo.start.x >= 0) {\r
4608       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4609       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4610     }\r
4611     if (dragInfo.pos.x >= 0) {\r
4612       x = dragInfo.pos.x - squareSize / 2;\r
4613       y = dragInfo.pos.y - squareSize / 2;\r
4614       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4615     }\r
4616     if (dragInfo.lastpos.x >= 0) {\r
4617       x = dragInfo.lastpos.x - squareSize / 2;\r
4618       y = dragInfo.lastpos.y - squareSize / 2;\r
4619       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4620     }\r
4621   }\r
4622 \r
4623   /* Are we animating a move?  \r
4624    * If so, \r
4625    *   - remove the piece from the board (temporarely)\r
4626    *   - calculate the clipping region\r
4627    */\r
4628   if (!fullrepaint) {\r
4629     if (animInfo.piece != EmptySquare) {\r
4630       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4631       x = boardRect.left + animInfo.lastpos.x;\r
4632       y = boardRect.top + animInfo.lastpos.y;\r
4633       x2 = boardRect.left + animInfo.pos.x;\r
4634       y2 = boardRect.top + animInfo.pos.y;\r
4635       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4636       /* Slight kludge.  The real problem is that after AnimateMove is\r
4637          done, the position on the screen does not match lastDrawn.\r
4638          This currently causes trouble only on e.p. captures in\r
4639          atomic, where the piece moves to an empty square and then\r
4640          explodes.  The old and new positions both had an empty square\r
4641          at the destination, but animation has drawn a piece there and\r
4642          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4643       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4644     }\r
4645   }\r
4646 \r
4647   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4648   if (num_clips == 0)\r
4649     fullrepaint = TRUE;\r
4650 \r
4651   /* Set clipping on the memory DC */\r
4652   if (!fullrepaint) {\r
4653     SelectClipRgn(hdcmem, clips[0]);\r
4654     for (x = 1; x < num_clips; x++) {\r
4655       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4656         abort();  // this should never ever happen!\r
4657     }\r
4658   }\r
4659 \r
4660   /* Do all the drawing to the memory DC */\r
4661   if(explodeInfo.radius) { // [HGM] atomic\r
4662         HBRUSH oldBrush;\r
4663         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4664         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4665         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4666         x += squareSize/2;\r
4667         y += squareSize/2;\r
4668         if(!fullrepaint) {\r
4669           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4670           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4671         }\r
4672         DrawGridOnDC(hdcmem);\r
4673         DrawHighlightsOnDC(hdcmem);\r
4674         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4675         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4676         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4677         SelectObject(hdcmem, oldBrush);\r
4678   } else {\r
4679     DrawGridOnDC(hdcmem);\r
4680     DrawHighlightsOnDC(hdcmem);\r
4681     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4682   }\r
4683   if(logoHeight) {\r
4684         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4685         if(appData.autoLogo) {\r
4686           \r
4687           switch(gameMode) { // pick logos based on game mode\r
4688             case IcsObserving:\r
4689                 whiteLogo = second.programLogo; // ICS logo\r
4690                 blackLogo = second.programLogo;\r
4691             default:\r
4692                 break;\r
4693             case IcsPlayingWhite:\r
4694                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4695                 blackLogo = second.programLogo; // ICS logo\r
4696                 break;\r
4697             case IcsPlayingBlack:\r
4698                 whiteLogo = second.programLogo; // ICS logo\r
4699                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4700                 break;\r
4701             case TwoMachinesPlay:\r
4702                 if(first.twoMachinesColor[0] == 'b') {\r
4703                     whiteLogo = second.programLogo;\r
4704                     blackLogo = first.programLogo;\r
4705                 }\r
4706                 break;\r
4707             case MachinePlaysWhite:\r
4708                 blackLogo = userLogo;\r
4709                 break;\r
4710             case MachinePlaysBlack:\r
4711                 whiteLogo = userLogo;\r
4712                 blackLogo = first.programLogo;\r
4713           }\r
4714         }\r
4715         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4716         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4717   }\r
4718 \r
4719   if( appData.highlightMoveWithArrow ) {\r
4720     DrawArrowHighlight(hdcmem);\r
4721   }\r
4722 \r
4723   DrawCoordsOnDC(hdcmem);\r
4724 \r
4725   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4726                  /* to make sure lastDrawn contains what is actually drawn */\r
4727 \r
4728   /* Put the dragged piece back into place and draw it (out of place!) */\r
4729     if (dragged_piece != EmptySquare) {\r
4730     /* [HGM] or restack */\r
4731     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4732                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4733     else\r
4734     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4735                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4736     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4737     x = dragInfo.pos.x - squareSize / 2;\r
4738     y = dragInfo.pos.y - squareSize / 2;\r
4739     DrawPieceOnDC(hdcmem, dragged_piece,\r
4740                   ((int) dragged_piece < (int) BlackPawn), \r
4741                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4742   }   \r
4743   \r
4744   /* Put the animated piece back into place and draw it */\r
4745   if (animInfo.piece != EmptySquare) {\r
4746     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4747     x = boardRect.left + animInfo.pos.x;\r
4748     y = boardRect.top + animInfo.pos.y;\r
4749     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4750                   ((int) animInfo.piece < (int) BlackPawn),\r
4751                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4752   }\r
4753 \r
4754   /* Release the bufferBitmap by selecting in the old bitmap \r
4755    * and delete the memory DC\r
4756    */\r
4757   SelectObject(hdcmem, oldBitmap);\r
4758   DeleteDC(hdcmem);\r
4759 \r
4760   /* Set clipping on the target DC */\r
4761   if (!fullrepaint) {\r
4762     SelectClipRgn(hdc, clips[0]);\r
4763     for (x = 1; x < num_clips; x++) {\r
4764       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4765         abort();   // this should never ever happen!\r
4766     } \r
4767   }\r
4768 \r
4769   /* Copy the new bitmap onto the screen in one go.\r
4770    * This way we avoid any flickering\r
4771    */\r
4772   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4773   BitBlt(hdc, boardRect.left, boardRect.top,\r
4774          boardRect.right - boardRect.left,\r
4775          boardRect.bottom - boardRect.top,\r
4776          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4777   if(saveDiagFlag) { \r
4778     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4779     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4780 \r
4781     GetObject(bufferBitmap, sizeof(b), &b);\r
4782     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4783         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4784         bih.biWidth = b.bmWidth;\r
4785         bih.biHeight = b.bmHeight;\r
4786         bih.biPlanes = 1;\r
4787         bih.biBitCount = b.bmBitsPixel;\r
4788         bih.biCompression = 0;\r
4789         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4790         bih.biXPelsPerMeter = 0;\r
4791         bih.biYPelsPerMeter = 0;\r
4792         bih.biClrUsed = 0;\r
4793         bih.biClrImportant = 0;\r
4794 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4795 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4796         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4797 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4798 \r
4799 #if 1\r
4800         wb = b.bmWidthBytes;\r
4801         // count colors\r
4802         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4803                 int k = ((int*) pData)[i];\r
4804                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4805                 if(j >= 16) break;\r
4806                 color[j] = k;\r
4807                 if(j >= nrColors) nrColors = j+1;\r
4808         }\r
4809         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4810                 INT p = 0;\r
4811                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4812                     for(w=0; w<(wb>>2); w+=2) {\r
4813                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4814                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4815                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4816                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4817                         pData[p++] = m | j<<4;\r
4818                     }\r
4819                     while(p&3) pData[p++] = 0;\r
4820                 }\r
4821                 fac = 3;\r
4822                 wb = ((wb+31)>>5)<<2;\r
4823         }\r
4824         // write BITMAPFILEHEADER\r
4825         fprintf(diagFile, "BM");\r
4826         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4827         fputDW(diagFile, 0);\r
4828         fputDW(diagFile, 0x36 + (fac?64:0));\r
4829         // write BITMAPINFOHEADER\r
4830         fputDW(diagFile, 40);\r
4831         fputDW(diagFile, b.bmWidth);\r
4832         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4833         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4834         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4835         fputDW(diagFile, 0);\r
4836         fputDW(diagFile, 0);\r
4837         fputDW(diagFile, 0);\r
4838         fputDW(diagFile, 0);\r
4839         fputDW(diagFile, 0);\r
4840         fputDW(diagFile, 0);\r
4841         // write color table\r
4842         if(fac)\r
4843         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4844         // write bitmap data\r
4845         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4846                 fputc(pData[i], diagFile);\r
4847 #endif\r
4848      }\r
4849   }\r
4850 \r
4851   SelectObject(tmphdc, oldBitmap);\r
4852 \r
4853   /* Massive cleanup */\r
4854   for (x = 0; x < num_clips; x++)\r
4855     DeleteObject(clips[x]);\r
4856 \r
4857   DeleteDC(tmphdc);\r
4858   DeleteObject(bufferBitmap);\r
4859 \r
4860   if (releaseDC) \r
4861     ReleaseDC(hwndMain, hdc);\r
4862   \r
4863   if (lastDrawnFlipView != flipView) {\r
4864     if (flipView)\r
4865       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4866     else\r
4867       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4868   }\r
4869 \r
4870 /*  CopyBoard(lastDrawn, board);*/\r
4871   lastDrawnHighlight = highlightInfo;\r
4872   lastDrawnPremove   = premoveHighlightInfo;\r
4873   lastDrawnFlipView = flipView;\r
4874   lastDrawnValid = 1;\r
4875 }\r
4876 \r
4877 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4878 int\r
4879 SaveDiagram(f)\r
4880      FILE *f;\r
4881 {\r
4882     saveDiagFlag = 1; diagFile = f;\r
4883     HDCDrawPosition(NULL, TRUE, NULL);\r
4884 \r
4885     saveDiagFlag = 0;\r
4886 \r
4887 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4888     \r
4889     fclose(f);\r
4890     return TRUE;\r
4891 }\r
4892 \r
4893 \r
4894 /*---------------------------------------------------------------------------*\\r
4895 | CLIENT PAINT PROCEDURE\r
4896 |   This is the main event-handler for the WM_PAINT message.\r
4897 |\r
4898 \*---------------------------------------------------------------------------*/\r
4899 VOID\r
4900 PaintProc(HWND hwnd)\r
4901 {\r
4902   HDC         hdc;\r
4903   PAINTSTRUCT ps;\r
4904   HFONT       oldFont;\r
4905 \r
4906   if((hdc = BeginPaint(hwnd, &ps))) {\r
4907     if (IsIconic(hwnd)) {\r
4908       DrawIcon(hdc, 2, 2, iconCurrent);\r
4909     } else {\r
4910       if (!appData.monoMode) {\r
4911         SelectPalette(hdc, hPal, FALSE);\r
4912         RealizePalette(hdc);\r
4913       }\r
4914       HDCDrawPosition(hdc, 1, NULL);\r
4915       oldFont =\r
4916         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4917       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4918                  ETO_CLIPPED|ETO_OPAQUE,\r
4919                  &messageRect, messageText, strlen(messageText), NULL);\r
4920       SelectObject(hdc, oldFont);\r
4921       DisplayBothClocks();\r
4922     }\r
4923     EndPaint(hwnd,&ps);\r
4924   }\r
4925 \r
4926   return;\r
4927 }\r
4928 \r
4929 \r
4930 /*\r
4931  * If the user selects on a border boundary, return -1; if off the board,\r
4932  *   return -2.  Otherwise map the event coordinate to the square.\r
4933  * The offset boardRect.left or boardRect.top must already have been\r
4934  *   subtracted from x.\r
4935  */\r
4936 int\r
4937 EventToSquare(int x)\r
4938 {\r
4939   if (x <= 0)\r
4940     return -2;\r
4941   if (x < lineGap)\r
4942     return -1;\r
4943   x -= lineGap;\r
4944   if ((x % (squareSize + lineGap)) >= squareSize)\r
4945     return -1;\r
4946   x /= (squareSize + lineGap);\r
4947   if (x >= BOARD_SIZE)\r
4948     return -2;\r
4949   return x;\r
4950 }\r
4951 \r
4952 typedef struct {\r
4953   char piece;\r
4954   int command;\r
4955   char* name;\r
4956 } DropEnable;\r
4957 \r
4958 DropEnable dropEnables[] = {\r
4959   { 'P', DP_Pawn, "Pawn" },\r
4960   { 'N', DP_Knight, "Knight" },\r
4961   { 'B', DP_Bishop, "Bishop" },\r
4962   { 'R', DP_Rook, "Rook" },\r
4963   { 'Q', DP_Queen, "Queen" },\r
4964 };\r
4965 \r
4966 VOID\r
4967 SetupDropMenu(HMENU hmenu)\r
4968 {\r
4969   int i, count, enable;\r
4970   char *p;\r
4971   extern char white_holding[], black_holding[];\r
4972   char item[MSG_SIZ];\r
4973 \r
4974   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4975     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4976                dropEnables[i].piece);\r
4977     count = 0;\r
4978     while (p && *p++ == dropEnables[i].piece) count++;\r
4979     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4980     enable = count > 0 || !appData.testLegality\r
4981       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4982                       && !appData.icsActive);\r
4983     ModifyMenu(hmenu, dropEnables[i].command,\r
4984                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4985                dropEnables[i].command, item);\r
4986   }\r
4987 }\r
4988 \r
4989 /* Event handler for mouse messages */\r
4990 VOID\r
4991 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4992 {\r
4993   int x, y;\r
4994   POINT pt;\r
4995   static int recursive = 0;\r
4996   HMENU hmenu;\r
4997 //  BOOLEAN needsRedraw = FALSE;\r
4998   BOOLEAN saveAnimate;\r
4999   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5000   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5001   ChessMove moveType;\r
5002 \r
5003   if (recursive) {\r
5004     if (message == WM_MBUTTONUP) {\r
5005       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5006          to the middle button: we simulate pressing the left button too!\r
5007          */\r
5008       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5009       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5010     }\r
5011     return;\r
5012   }\r
5013   recursive++;\r
5014   \r
5015   pt.x = LOWORD(lParam);\r
5016   pt.y = HIWORD(lParam);\r
5017   x = EventToSquare(pt.x - boardRect.left);\r
5018   y = EventToSquare(pt.y - boardRect.top);\r
5019   if (!flipView && y >= 0) {\r
5020     y = BOARD_HEIGHT - 1 - y;\r
5021   }\r
5022   if (flipView && x >= 0) {\r
5023     x = BOARD_WIDTH - 1 - x;\r
5024   }\r
5025 \r
5026   switch (message) {\r
5027   case WM_LBUTTONDOWN:\r
5028     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5029         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5030         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5031         if(gameInfo.holdingsWidth && \r
5032                 (WhiteOnMove(currentMove) \r
5033                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5034                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5035             // click in right holdings, for determining promotion piece\r
5036             ChessSquare p = boards[currentMove][y][x];\r
5037             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5038             if(p != EmptySquare) {\r
5039                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5040                 fromX = fromY = -1;\r
5041                 break;\r
5042             }\r
5043         }\r
5044         DrawPosition(FALSE, boards[currentMove]);\r
5045         break;\r
5046     }\r
5047     ErrorPopDown();\r
5048     sameAgain = FALSE;\r
5049     if (y == -2) {\r
5050       /* Downclick vertically off board; check if on clock */\r
5051       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5052         if (gameMode == EditPosition) {\r
5053           SetWhiteToPlayEvent();\r
5054         } else if (gameMode == IcsPlayingBlack ||\r
5055                    gameMode == MachinePlaysWhite) {\r
5056           CallFlagEvent();\r
5057         } else if (gameMode == EditGame) {\r
5058           AdjustClock(flipClock, -1);\r
5059         }\r
5060       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5061         if (gameMode == EditPosition) {\r
5062           SetBlackToPlayEvent();\r
5063         } else if (gameMode == IcsPlayingWhite ||\r
5064                    gameMode == MachinePlaysBlack) {\r
5065           CallFlagEvent();\r
5066         } else if (gameMode == EditGame) {\r
5067           AdjustClock(!flipClock, -1);\r
5068         }\r
5069       }\r
5070       if (!appData.highlightLastMove) {\r
5071         ClearHighlights();\r
5072         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5073       }\r
5074       fromX = fromY = -1;\r
5075       dragInfo.start.x = dragInfo.start.y = -1;\r
5076       dragInfo.from = dragInfo.start;\r
5077       break;\r
5078     } else if (x < 0 || y < 0\r
5079       /* [HGM] block clicks between board and holdings */\r
5080               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5081               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5082               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5083         /* EditPosition, empty square, or different color piece;\r
5084            click-click move is possible */\r
5085                                ) {\r
5086       break;\r
5087     } else if (fromX == x && fromY == y) {\r
5088       /* Downclick on same square again */\r
5089       ClearHighlights();\r
5090       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5091       sameAgain = TRUE;  \r
5092     } else if (fromX != -1 &&\r
5093                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5094                                                                         ) {\r
5095       /* Downclick on different square. */\r
5096       /* [HGM] if on holdings file, should count as new first click ! */\r
5097       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5098         toX = x;\r
5099         toY = y;\r
5100         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5101            to make sure move is legal before showing promotion popup */\r
5102         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5103         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5104                 fromX = fromY = -1; \r
5105                 ClearHighlights();\r
5106                 DrawPosition(FALSE, boards[currentMove]);\r
5107                 break; \r
5108         } else \r
5109         if(moveType != ImpossibleMove) {\r
5110           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5111           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5112             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5113               appData.alwaysPromoteToQueen)) {\r
5114                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5115                   if (!appData.highlightLastMove) {\r
5116                       ClearHighlights();\r
5117                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5118                   }\r
5119           } else\r
5120           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5121                   SetHighlights(fromX, fromY, toX, toY);\r
5122                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5123                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5124                      If promotion to Q is legal, all are legal! */\r
5125                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5126                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5127                     // kludge to temporarily execute move on display, wthout promotng yet\r
5128                     promotionChoice = TRUE;\r
5129                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5130                     boards[currentMove][toY][toX] = p;\r
5131                     DrawPosition(FALSE, boards[currentMove]);\r
5132                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5133                     boards[currentMove][toY][toX] = q;\r
5134                   } else\r
5135                   PromotionPopup(hwnd);\r
5136           } else {       /* not a promotion */\r
5137              if (appData.animate || appData.highlightLastMove) {\r
5138                  SetHighlights(fromX, fromY, toX, toY);\r
5139              } else {\r
5140                  ClearHighlights();\r
5141              }\r
5142              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5143              fromX = fromY = -1;\r
5144              if (appData.animate && !appData.highlightLastMove) {\r
5145                   ClearHighlights();\r
5146                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5147              }\r
5148           }\r
5149           break;\r
5150         }\r
5151         if (gotPremove) {\r
5152             /* [HGM] it seemed that braces were missing here */\r
5153             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5154             fromX = fromY = -1;\r
5155             break;\r
5156         }\r
5157       }\r
5158       ClearHighlights();\r
5159       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5160     }\r
5161     /* First downclick, or restart on a square with same color piece */\r
5162     if (!frozen && OKToStartUserMove(x, y)) {\r
5163       fromX = x;\r
5164       fromY = y;\r
5165       dragInfo.lastpos = pt;\r
5166       dragInfo.from.x = fromX;\r
5167       dragInfo.from.y = fromY;\r
5168       dragInfo.start = dragInfo.from;\r
5169       SetCapture(hwndMain);\r
5170     } else {\r
5171       fromX = fromY = -1;\r
5172       dragInfo.start.x = dragInfo.start.y = -1;\r
5173       dragInfo.from = dragInfo.start;\r
5174       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5175     }\r
5176     break;\r
5177 \r
5178   case WM_LBUTTONUP:\r
5179     ReleaseCapture();\r
5180     if (fromX == -1) break;\r
5181     if (x == fromX && y == fromY) {\r
5182       dragInfo.from.x = dragInfo.from.y = -1;\r
5183       /* Upclick on same square */\r
5184       if (sameAgain) {\r
5185         /* Clicked same square twice: abort click-click move */\r
5186         fromX = fromY = -1;\r
5187         gotPremove = 0;\r
5188         ClearPremoveHighlights();\r
5189       } else {\r
5190         /* First square clicked: start click-click move */\r
5191         SetHighlights(fromX, fromY, -1, -1);\r
5192       }\r
5193       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5194     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5195       /* Errant click; ignore */\r
5196       break;\r
5197     } else {\r
5198       /* Finish drag move. */\r
5199     if (appData.debugMode) {\r
5200         fprintf(debugFP, "release\n");\r
5201     }\r
5202       dragInfo.from.x = dragInfo.from.y = -1;\r
5203       toX = x;\r
5204       toY = y;\r
5205       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5206       appData.animate = appData.animate && !appData.animateDragging;\r
5207       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5208       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5209                 fromX = fromY = -1; \r
5210                 ClearHighlights();\r
5211                 DrawPosition(FALSE, boards[currentMove]);\r
5212                 appData.animate = saveAnimate;\r
5213                 break; \r
5214       } else \r
5215       if(moveType != ImpossibleMove) {\r
5216           /* [HGM] use move type to determine if move is promotion.\r
5217              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5218           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5219             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5220               appData.alwaysPromoteToQueen)) \r
5221                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5222           else \r
5223           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5224                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5225                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5226                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5227                     // kludge to temporarily execute move on display, wthout promotng yet\r
5228                     promotionChoice = TRUE;\r
5229                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5230                     boards[currentMove][toY][toX] = p;\r
5231                     DrawPosition(FALSE, boards[currentMove]);\r
5232                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5233                     boards[currentMove][toY][toX] = q;\r
5234                     appData.animate = saveAnimate;\r
5235                     break;\r
5236                   } else\r
5237                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5238           } else {\r
5239             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5240                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5241                                         moveType == WhiteCapturesEnPassant || \r
5242                                         moveType == BlackCapturesEnPassant   ) )\r
5243                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5244             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5245           }\r
5246       }\r
5247       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5248       appData.animate = saveAnimate;\r
5249       fromX = fromY = -1;\r
5250       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5251         ClearHighlights();\r
5252       }\r
5253       if (appData.animate || appData.animateDragging ||\r
5254           appData.highlightDragging || gotPremove) {\r
5255         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5256       }\r
5257     }\r
5258     dragInfo.start.x = dragInfo.start.y = -1; \r
5259     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5260     break;\r
5261 \r
5262   case WM_MOUSEMOVE:\r
5263     if ((appData.animateDragging || appData.highlightDragging)\r
5264         && (wParam & MK_LBUTTON)\r
5265         && dragInfo.from.x >= 0) \r
5266     {\r
5267       BOOL full_repaint = FALSE;\r
5268 \r
5269       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5270       if (appData.animateDragging) {\r
5271         dragInfo.pos = pt;\r
5272       }\r
5273       if (appData.highlightDragging) {\r
5274         SetHighlights(fromX, fromY, x, y);\r
5275         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5276             full_repaint = TRUE;\r
5277         }\r
5278       }\r
5279       \r
5280       DrawPosition( full_repaint, NULL);\r
5281       \r
5282       dragInfo.lastpos = dragInfo.pos;\r
5283     }\r
5284     break;\r
5285 \r
5286   case WM_MOUSEWHEEL: // [DM]\r
5287     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5288        /* Mouse Wheel is being rolled forward\r
5289         * Play moves forward\r
5290         */\r
5291        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5292                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5293        /* Mouse Wheel is being rolled backward\r
5294         * Play moves backward\r
5295         */\r
5296        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5297                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5298     }\r
5299     break;\r
5300 \r
5301   case WM_MBUTTONDOWN:\r
5302   case WM_RBUTTONDOWN:\r
5303     ErrorPopDown();\r
5304     ReleaseCapture();\r
5305     fromX = fromY = -1;\r
5306     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5307     dragInfo.start.x = dragInfo.start.y = -1;\r
5308     dragInfo.from = dragInfo.start;\r
5309     dragInfo.lastpos = dragInfo.pos;\r
5310     if (appData.highlightDragging) {\r
5311       ClearHighlights();\r
5312     }\r
5313     if(y == -2) {\r
5314       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5315       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5316           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5317       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5318           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5319       }\r
5320     }\r
5321     DrawPosition(TRUE, NULL);\r
5322 \r
5323     switch (gameMode) {\r
5324     case EditPosition:\r
5325     case IcsExamining:\r
5326       if (x < 0 || y < 0) break;\r
5327       fromX = x;\r
5328       fromY = y;\r
5329       if (message == WM_MBUTTONDOWN) {\r
5330         buttonCount = 3;  /* even if system didn't think so */\r
5331         if (wParam & MK_SHIFT) \r
5332           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5333         else\r
5334           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5335       } else { /* message == WM_RBUTTONDOWN */\r
5336 #if 0\r
5337         if (buttonCount == 3) {\r
5338           if (wParam & MK_SHIFT) \r
5339             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5340           else\r
5341             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5342         } else {\r
5343           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5344         }\r
5345 #else\r
5346         /* Just have one menu, on the right button.  Windows users don't\r
5347            think to try the middle one, and sometimes other software steals\r
5348            it, or it doesn't really exist. */\r
5349         if(gameInfo.variant != VariantShogi)\r
5350             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5351         else\r
5352             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5353 #endif\r
5354       }\r
5355       break;\r
5356     case IcsPlayingWhite:\r
5357     case IcsPlayingBlack:\r
5358     case EditGame:\r
5359     case MachinePlaysWhite:\r
5360     case MachinePlaysBlack:\r
5361       if (appData.testLegality &&\r
5362           gameInfo.variant != VariantBughouse &&\r
5363           gameInfo.variant != VariantCrazyhouse) break;\r
5364       if (x < 0 || y < 0) break;\r
5365       fromX = x;\r
5366       fromY = y;\r
5367       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5368       SetupDropMenu(hmenu);\r
5369       MenuPopup(hwnd, pt, hmenu, -1);\r
5370       break;\r
5371     default:\r
5372       break;\r
5373     }\r
5374     break;\r
5375   }\r
5376 \r
5377   recursive--;\r
5378 }\r
5379 \r
5380 /* Preprocess messages for buttons in main window */\r
5381 LRESULT CALLBACK\r
5382 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5383 {\r
5384   int id = GetWindowLong(hwnd, GWL_ID);\r
5385   int i, dir;\r
5386 \r
5387   for (i=0; i<N_BUTTONS; i++) {\r
5388     if (buttonDesc[i].id == id) break;\r
5389   }\r
5390   if (i == N_BUTTONS) return 0;\r
5391   switch (message) {\r
5392   case WM_KEYDOWN:\r
5393     switch (wParam) {\r
5394     case VK_LEFT:\r
5395     case VK_RIGHT:\r
5396       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5397       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5398       return TRUE;\r
5399     }\r
5400     break;\r
5401   case WM_CHAR:\r
5402     switch (wParam) {\r
5403     case '\r':\r
5404       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5405       return TRUE;\r
5406     default:\r
5407       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5408         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5409         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5410         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5411         SetFocus(h);\r
5412         SendMessage(h, WM_CHAR, wParam, lParam);\r
5413         return TRUE;\r
5414       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5415         PopUpMoveDialog((char)wParam);\r
5416       }\r
5417       break;\r
5418     }\r
5419     break;\r
5420   }\r
5421   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5422 }\r
5423 \r
5424 /* Process messages for Promotion dialog box */\r
5425 LRESULT CALLBACK\r
5426 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5427 {\r
5428   char promoChar;\r
5429 \r
5430   switch (message) {\r
5431   case WM_INITDIALOG: /* message: initialize dialog box */\r
5432     /* Center the dialog over the application window */\r
5433     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5434     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5435       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5436        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5437                SW_SHOW : SW_HIDE);\r
5438     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5439     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5440        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5441          PieceToChar(WhiteAngel) != '~') ||\r
5442         (PieceToChar(BlackAngel) >= 'A' &&\r
5443          PieceToChar(BlackAngel) != '~')   ) ?\r
5444                SW_SHOW : SW_HIDE);\r
5445     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5446        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5447          PieceToChar(WhiteMarshall) != '~') ||\r
5448         (PieceToChar(BlackMarshall) >= 'A' &&\r
5449          PieceToChar(BlackMarshall) != '~')   ) ?\r
5450                SW_SHOW : SW_HIDE);\r
5451     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5452     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5453        gameInfo.variant != VariantShogi ?\r
5454                SW_SHOW : SW_HIDE);\r
5455     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5456        gameInfo.variant != VariantShogi ?\r
5457                SW_SHOW : SW_HIDE);\r
5458     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5459        gameInfo.variant == VariantShogi ?\r
5460                SW_SHOW : SW_HIDE);\r
5461     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5462        gameInfo.variant == VariantShogi ?\r
5463                SW_SHOW : SW_HIDE);\r
5464     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5465        gameInfo.variant == VariantSuper ?\r
5466                SW_SHOW : SW_HIDE);\r
5467     return TRUE;\r
5468 \r
5469   case WM_COMMAND: /* message: received a command */\r
5470     switch (LOWORD(wParam)) {\r
5471     case IDCANCEL:\r
5472       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5473       ClearHighlights();\r
5474       DrawPosition(FALSE, NULL);\r
5475       return TRUE;\r
5476     case PB_King:\r
5477       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5478       break;\r
5479     case PB_Queen:\r
5480       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5481       break;\r
5482     case PB_Rook:\r
5483       promoChar = PieceToChar(BlackRook);\r
5484       break;\r
5485     case PB_Bishop:\r
5486       promoChar = PieceToChar(BlackBishop);\r
5487       break;\r
5488     case PB_Chancellor:\r
5489       promoChar = PieceToChar(BlackMarshall);\r
5490       break;\r
5491     case PB_Archbishop:\r
5492       promoChar = PieceToChar(BlackAngel);\r
5493       break;\r
5494     case PB_Knight:\r
5495       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5496       break;\r
5497     default:\r
5498       return FALSE;\r
5499     }\r
5500     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5501     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5502        only show the popup when we are already sure the move is valid or\r
5503        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5504        will figure out it is a promotion from the promoChar. */\r
5505     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5506     if (!appData.highlightLastMove) {\r
5507       ClearHighlights();\r
5508       DrawPosition(FALSE, NULL);\r
5509     }\r
5510     return TRUE;\r
5511   }\r
5512   return FALSE;\r
5513 }\r
5514 \r
5515 /* Pop up promotion dialog */\r
5516 VOID\r
5517 PromotionPopup(HWND hwnd)\r
5518 {\r
5519   FARPROC lpProc;\r
5520 \r
5521   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5522   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5523     hwnd, (DLGPROC)lpProc);\r
5524   FreeProcInstance(lpProc);\r
5525 }\r
5526 \r
5527 /* Toggle ShowThinking */\r
5528 VOID\r
5529 ToggleShowThinking()\r
5530 {\r
5531   appData.showThinking = !appData.showThinking;\r
5532   ShowThinkingEvent();\r
5533 }\r
5534 \r
5535 VOID\r
5536 LoadGameDialog(HWND hwnd, char* title)\r
5537 {\r
5538   UINT number = 0;\r
5539   FILE *f;\r
5540   char fileTitle[MSG_SIZ];\r
5541   f = OpenFileDialog(hwnd, "rb", "",\r
5542                      appData.oldSaveStyle ? "gam" : "pgn",\r
5543                      GAME_FILT,\r
5544                      title, &number, fileTitle, NULL);\r
5545   if (f != NULL) {\r
5546     cmailMsgLoaded = FALSE;\r
5547     if (number == 0) {\r
5548       int error = GameListBuild(f);\r
5549       if (error) {\r
5550         DisplayError("Cannot build game list", error);\r
5551       } else if (!ListEmpty(&gameList) &&\r
5552                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5553         GameListPopUp(f, fileTitle);\r
5554         return;\r
5555       }\r
5556       GameListDestroy();\r
5557       number = 1;\r
5558     }\r
5559     LoadGame(f, number, fileTitle, FALSE);\r
5560   }\r
5561 }\r
5562 \r
5563 VOID\r
5564 ChangedConsoleFont()\r
5565 {\r
5566   CHARFORMAT cfmt;\r
5567   CHARRANGE tmpsel, sel;\r
5568   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5569   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5570   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5571   PARAFORMAT paraf;\r
5572 \r
5573   cfmt.cbSize = sizeof(CHARFORMAT);\r
5574   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5575   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5576   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5577    * size.  This was undocumented in the version of MSVC++ that I had\r
5578    * when I wrote the code, but is apparently documented now.\r
5579    */\r
5580   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5581   cfmt.bCharSet = f->lf.lfCharSet;\r
5582   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5583   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5584   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5585   /* Why are the following seemingly needed too? */\r
5586   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5587   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5588   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5589   tmpsel.cpMin = 0;\r
5590   tmpsel.cpMax = -1; /*999999?*/\r
5591   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5592   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5593   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5594    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5595    */\r
5596   paraf.cbSize = sizeof(paraf);\r
5597   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5598   paraf.dxStartIndent = 0;\r
5599   paraf.dxOffset = WRAP_INDENT;\r
5600   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5601   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5602 }\r
5603 \r
5604 /*---------------------------------------------------------------------------*\\r
5605  *\r
5606  * Window Proc for main window\r
5607  *\r
5608 \*---------------------------------------------------------------------------*/\r
5609 \r
5610 /* Process messages for main window, etc. */\r
5611 LRESULT CALLBACK\r
5612 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5613 {\r
5614   FARPROC lpProc;\r
5615   int wmId, wmEvent;\r
5616   char *defName;\r
5617   FILE *f;\r
5618   UINT number;\r
5619   char fileTitle[MSG_SIZ];\r
5620   char buf[MSG_SIZ];\r
5621   static SnapData sd;\r
5622 \r
5623   switch (message) {\r
5624 \r
5625   case WM_PAINT: /* message: repaint portion of window */\r
5626     PaintProc(hwnd);\r
5627     break;\r
5628 \r
5629   case WM_ERASEBKGND:\r
5630     if (IsIconic(hwnd)) {\r
5631       /* Cheat; change the message */\r
5632       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5633     } else {\r
5634       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5635     }\r
5636     break;\r
5637 \r
5638   case WM_LBUTTONDOWN:\r
5639   case WM_MBUTTONDOWN:\r
5640   case WM_RBUTTONDOWN:\r
5641   case WM_LBUTTONUP:\r
5642   case WM_MBUTTONUP:\r
5643   case WM_RBUTTONUP:\r
5644   case WM_MOUSEMOVE:\r
5645   case WM_MOUSEWHEEL:\r
5646     MouseEvent(hwnd, message, wParam, lParam);\r
5647     break;\r
5648 \r
5649   JAWS_KB_NAVIGATION\r
5650 \r
5651   case WM_CHAR:\r
5652     \r
5653     JAWS_ALT_INTERCEPT\r
5654 \r
5655     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5656         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5657         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5658         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5659         SetFocus(h);\r
5660         SendMessage(h, message, wParam, lParam);\r
5661     } else if(lParam != KF_REPEAT) {\r
5662         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5663                 PopUpMoveDialog((char)wParam);\r
5664         } else if((char)wParam == 003) CopyGameToClipboard();\r
5665          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5666     }\r
5667 \r
5668     break;\r
5669 \r
5670   case WM_PALETTECHANGED:\r
5671     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5672       int nnew;\r
5673       HDC hdc = GetDC(hwndMain);\r
5674       SelectPalette(hdc, hPal, TRUE);\r
5675       nnew = RealizePalette(hdc);\r
5676       if (nnew > 0) {\r
5677         paletteChanged = TRUE;\r
5678 #if 0\r
5679         UpdateColors(hdc);\r
5680 #else\r
5681         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5682 #endif\r
5683       }\r
5684       ReleaseDC(hwnd, hdc);\r
5685     }\r
5686     break;\r
5687 \r
5688   case WM_QUERYNEWPALETTE:\r
5689     if (!appData.monoMode /*&& paletteChanged*/) {\r
5690       int nnew;\r
5691       HDC hdc = GetDC(hwndMain);\r
5692       paletteChanged = FALSE;\r
5693       SelectPalette(hdc, hPal, FALSE);\r
5694       nnew = RealizePalette(hdc);\r
5695       if (nnew > 0) {\r
5696         InvalidateRect(hwnd, &boardRect, FALSE);\r
5697       }\r
5698       ReleaseDC(hwnd, hdc);\r
5699       return TRUE;\r
5700     }\r
5701     return FALSE;\r
5702 \r
5703   case WM_COMMAND: /* message: command from application menu */\r
5704     wmId    = LOWORD(wParam);\r
5705     wmEvent = HIWORD(wParam);\r
5706 \r
5707     switch (wmId) {\r
5708     case IDM_NewGame:\r
5709       ResetGameEvent();\r
5710       AnalysisPopDown();\r
5711       SAY("new game enter a move to play against the computer with white");\r
5712       break;\r
5713 \r
5714     case IDM_NewGameFRC:\r
5715       if( NewGameFRC() == 0 ) {\r
5716         ResetGameEvent();\r
5717         AnalysisPopDown();\r
5718       }\r
5719       break;\r
5720 \r
5721     case IDM_NewVariant:\r
5722       NewVariantPopup(hwnd);\r
5723       break;\r
5724 \r
5725     case IDM_LoadGame:\r
5726       LoadGameDialog(hwnd, "Load Game from File");\r
5727       break;\r
5728 \r
5729     case IDM_LoadNextGame:\r
5730       ReloadGame(1);\r
5731       break;\r
5732 \r
5733     case IDM_LoadPrevGame:\r
5734       ReloadGame(-1);\r
5735       break;\r
5736 \r
5737     case IDM_ReloadGame:\r
5738       ReloadGame(0);\r
5739       break;\r
5740 \r
5741     case IDM_LoadPosition:\r
5742       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5743         Reset(FALSE, TRUE);\r
5744       }\r
5745       number = 1;\r
5746       f = OpenFileDialog(hwnd, "rb", "",\r
5747                          appData.oldSaveStyle ? "pos" : "fen",\r
5748                          POSITION_FILT,\r
5749                          "Load Position from File", &number, fileTitle, NULL);\r
5750       if (f != NULL) {\r
5751         LoadPosition(f, number, fileTitle);\r
5752       }\r
5753       break;\r
5754 \r
5755     case IDM_LoadNextPosition:\r
5756       ReloadPosition(1);\r
5757       break;\r
5758 \r
5759     case IDM_LoadPrevPosition:\r
5760       ReloadPosition(-1);\r
5761       break;\r
5762 \r
5763     case IDM_ReloadPosition:\r
5764       ReloadPosition(0);\r
5765       break;\r
5766 \r
5767     case IDM_SaveGame:\r
5768       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5769       f = OpenFileDialog(hwnd, "a", defName,\r
5770                          appData.oldSaveStyle ? "gam" : "pgn",\r
5771                          GAME_FILT,\r
5772                          "Save Game to File", NULL, fileTitle, NULL);\r
5773       if (f != NULL) {\r
5774         SaveGame(f, 0, "");\r
5775       }\r
5776       break;\r
5777 \r
5778     case IDM_SavePosition:\r
5779       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5780       f = OpenFileDialog(hwnd, "a", defName,\r
5781                          appData.oldSaveStyle ? "pos" : "fen",\r
5782                          POSITION_FILT,\r
5783                          "Save Position to File", NULL, fileTitle, NULL);\r
5784       if (f != NULL) {\r
5785         SavePosition(f, 0, "");\r
5786       }\r
5787       break;\r
5788 \r
5789     case IDM_SaveDiagram:\r
5790       defName = "diagram";\r
5791       f = OpenFileDialog(hwnd, "wb", defName,\r
5792                          "bmp",\r
5793                          DIAGRAM_FILT,\r
5794                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5795       if (f != NULL) {\r
5796         SaveDiagram(f);\r
5797       }\r
5798       break;\r
5799 \r
5800     case IDM_CopyGame:\r
5801       CopyGameToClipboard();\r
5802       break;\r
5803 \r
5804     case IDM_PasteGame:\r
5805       PasteGameFromClipboard();\r
5806       break;\r
5807 \r
5808     case IDM_CopyGameListToClipboard:\r
5809       CopyGameListToClipboard();\r
5810       break;\r
5811 \r
5812     /* [AS] Autodetect FEN or PGN data */\r
5813     case IDM_PasteAny:\r
5814       PasteGameOrFENFromClipboard();\r
5815       break;\r
5816 \r
5817     /* [AS] Move history */\r
5818     case IDM_ShowMoveHistory:\r
5819         if( MoveHistoryIsUp() ) {\r
5820             MoveHistoryPopDown();\r
5821         }\r
5822         else {\r
5823             MoveHistoryPopUp();\r
5824         }\r
5825         break;\r
5826 \r
5827     /* [AS] Eval graph */\r
5828     case IDM_ShowEvalGraph:\r
5829         if( EvalGraphIsUp() ) {\r
5830             EvalGraphPopDown();\r
5831         }\r
5832         else {\r
5833             EvalGraphPopUp();\r
5834             SetFocus(hwndMain);\r
5835         }\r
5836         break;\r
5837 \r
5838     /* [AS] Engine output */\r
5839     case IDM_ShowEngineOutput:\r
5840         if( EngineOutputIsUp() ) {\r
5841             EngineOutputPopDown();\r
5842         }\r
5843         else {\r
5844             EngineOutputPopUp();\r
5845         }\r
5846         break;\r
5847 \r
5848     /* [AS] User adjudication */\r
5849     case IDM_UserAdjudication_White:\r
5850         UserAdjudicationEvent( +1 );\r
5851         break;\r
5852 \r
5853     case IDM_UserAdjudication_Black:\r
5854         UserAdjudicationEvent( -1 );\r
5855         break;\r
5856 \r
5857     case IDM_UserAdjudication_Draw:\r
5858         UserAdjudicationEvent( 0 );\r
5859         break;\r
5860 \r
5861     /* [AS] Game list options dialog */\r
5862     case IDM_GameListOptions:\r
5863       GameListOptions();\r
5864       break;\r
5865 \r
5866     case IDM_CopyPosition:\r
5867       CopyFENToClipboard();\r
5868       break;\r
5869 \r
5870     case IDM_PastePosition:\r
5871       PasteFENFromClipboard();\r
5872       break;\r
5873 \r
5874     case IDM_MailMove:\r
5875       MailMoveEvent();\r
5876       break;\r
5877 \r
5878     case IDM_ReloadCMailMsg:\r
5879       Reset(TRUE, TRUE);\r
5880       ReloadCmailMsgEvent(FALSE);\r
5881       break;\r
5882 \r
5883     case IDM_Minimize:\r
5884       ShowWindow(hwnd, SW_MINIMIZE);\r
5885       break;\r
5886 \r
5887     case IDM_Exit:\r
5888       ExitEvent(0);\r
5889       break;\r
5890 \r
5891     case IDM_MachineWhite:\r
5892       MachineWhiteEvent();\r
5893       /*\r
5894        * refresh the tags dialog only if it's visible\r
5895        */\r
5896       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5897           char *tags;\r
5898           tags = PGNTags(&gameInfo);\r
5899           TagsPopUp(tags, CmailMsg());\r
5900           free(tags);\r
5901       }\r
5902       SAY("computer starts playing white");\r
5903       break;\r
5904 \r
5905     case IDM_MachineBlack:\r
5906       MachineBlackEvent();\r
5907       /*\r
5908        * refresh the tags dialog only if it's visible\r
5909        */\r
5910       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5911           char *tags;\r
5912           tags = PGNTags(&gameInfo);\r
5913           TagsPopUp(tags, CmailMsg());\r
5914           free(tags);\r
5915       }\r
5916       SAY("computer starts playing black");\r
5917       break;\r
5918 \r
5919     case IDM_TwoMachines:\r
5920       TwoMachinesEvent();\r
5921       /*\r
5922        * refresh the tags dialog only if it's visible\r
5923        */\r
5924       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5925           char *tags;\r
5926           tags = PGNTags(&gameInfo);\r
5927           TagsPopUp(tags, CmailMsg());\r
5928           free(tags);\r
5929       }\r
5930       SAY("programs start playing each other");\r
5931       break;\r
5932 \r
5933     case IDM_AnalysisMode:\r
5934       if (!first.analysisSupport) {\r
5935         sprintf(buf, "%s does not support analysis", first.tidy);\r
5936         DisplayError(buf, 0);\r
5937       } else {\r
5938         SAY("analyzing current position");\r
5939         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5940         if (appData.icsActive) {\r
5941                if (gameMode != IcsObserving) {\r
5942                        sprintf(buf, "You are not observing a game");\r
5943                        DisplayError(buf, 0);\r
5944                        /* secure check */\r
5945                        if (appData.icsEngineAnalyze) {\r
5946                                if (appData.debugMode) \r
5947                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5948                                ExitAnalyzeMode();\r
5949                                ModeHighlight();\r
5950                                break;\r
5951                        }\r
5952                        break;\r
5953                } else {\r
5954                        /* if enable, user want disable icsEngineAnalyze */\r
5955                        if (appData.icsEngineAnalyze) {\r
5956                                ExitAnalyzeMode();\r
5957                                ModeHighlight();\r
5958                                break;\r
5959                        }\r
5960                        appData.icsEngineAnalyze = TRUE;\r
5961                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5962                }\r
5963         } \r
5964         if (!appData.showThinking) ToggleShowThinking();\r
5965         AnalyzeModeEvent();\r
5966       }\r
5967       break;\r
5968 \r
5969     case IDM_AnalyzeFile:\r
5970       if (!first.analysisSupport) {\r
5971         char buf[MSG_SIZ];\r
5972         sprintf(buf, "%s does not support analysis", first.tidy);\r
5973         DisplayError(buf, 0);\r
5974       } else {\r
5975         if (!appData.showThinking) ToggleShowThinking();\r
5976         AnalyzeFileEvent();\r
5977         LoadGameDialog(hwnd, "Analyze Game from File");\r
5978         AnalysisPeriodicEvent(1);\r
5979       }\r
5980       break;\r
5981 \r
5982     case IDM_IcsClient:\r
5983       IcsClientEvent();\r
5984       break;\r
5985 \r
5986     case IDM_EditGame:\r
5987       EditGameEvent();\r
5988       SAY("edit game");\r
5989       break;\r
5990 \r
5991     case IDM_EditPosition:\r
5992       EditPositionEvent();\r
5993       SAY("to set up a position type a FEN");\r
5994       break;\r
5995 \r
5996     case IDM_Training:\r
5997       TrainingEvent();\r
5998       break;\r
5999 \r
6000     case IDM_ShowGameList:\r
6001       ShowGameListProc();\r
6002       break;\r
6003 \r
6004     case IDM_EditTags:\r
6005       EditTagsProc();\r
6006       break;\r
6007 \r
6008     case IDM_EditComment:\r
6009       if (commentDialogUp && editComment) {\r
6010         CommentPopDown();\r
6011       } else {\r
6012         EditCommentEvent();\r
6013       }\r
6014       break;\r
6015 \r
6016     case IDM_Pause:\r
6017       PauseEvent();\r
6018       break;\r
6019 \r
6020     case IDM_Accept:\r
6021       AcceptEvent();\r
6022       break;\r
6023 \r
6024     case IDM_Decline:\r
6025       DeclineEvent();\r
6026       break;\r
6027 \r
6028     case IDM_Rematch:\r
6029       RematchEvent();\r
6030       break;\r
6031 \r
6032     case IDM_CallFlag:\r
6033       CallFlagEvent();\r
6034       break;\r
6035 \r
6036     case IDM_Draw:\r
6037       DrawEvent();\r
6038       break;\r
6039 \r
6040     case IDM_Adjourn:\r
6041       AdjournEvent();\r
6042       break;\r
6043 \r
6044     case IDM_Abort:\r
6045       AbortEvent();\r
6046       break;\r
6047 \r
6048     case IDM_Resign:\r
6049       ResignEvent();\r
6050       break;\r
6051 \r
6052     case IDM_StopObserving:\r
6053       StopObservingEvent();\r
6054       break;\r
6055 \r
6056     case IDM_StopExamining:\r
6057       StopExaminingEvent();\r
6058       break;\r
6059 \r
6060     case IDM_TypeInMove:\r
6061       PopUpMoveDialog('\000');\r
6062       break;\r
6063 \r
6064     case IDM_TypeInName:\r
6065       PopUpNameDialog('\000');\r
6066       break;\r
6067 \r
6068     case IDM_Backward:\r
6069       BackwardEvent();\r
6070       SetFocus(hwndMain);\r
6071       break;\r
6072 \r
6073     JAWS_MENU_ITEMS\r
6074 \r
6075     case IDM_Forward:\r
6076       ForwardEvent();\r
6077       SetFocus(hwndMain);\r
6078       break;\r
6079 \r
6080     case IDM_ToStart:\r
6081       ToStartEvent();\r
6082       SetFocus(hwndMain);\r
6083       break;\r
6084 \r
6085     case IDM_ToEnd:\r
6086       ToEndEvent();\r
6087       SetFocus(hwndMain);\r
6088       break;\r
6089 \r
6090     case IDM_Revert:\r
6091       RevertEvent();\r
6092       break;\r
6093 \r
6094     case IDM_TruncateGame:\r
6095       TruncateGameEvent();\r
6096       break;\r
6097 \r
6098     case IDM_MoveNow:\r
6099       MoveNowEvent();\r
6100       break;\r
6101 \r
6102     case IDM_RetractMove:\r
6103       RetractMoveEvent();\r
6104       break;\r
6105 \r
6106     case IDM_FlipView:\r
6107       flipView = !flipView;\r
6108       DrawPosition(FALSE, NULL);\r
6109       break;\r
6110 \r
6111     case IDM_FlipClock:\r
6112       flipClock = !flipClock;\r
6113       DisplayBothClocks();\r
6114       DrawPosition(FALSE, NULL);\r
6115       break;\r
6116 \r
6117     case IDM_GeneralOptions:\r
6118       GeneralOptionsPopup(hwnd);\r
6119       DrawPosition(TRUE, NULL);\r
6120       break;\r
6121 \r
6122     case IDM_BoardOptions:\r
6123       BoardOptionsPopup(hwnd);\r
6124       break;\r
6125 \r
6126     case IDM_EnginePlayOptions:\r
6127       EnginePlayOptionsPopup(hwnd);\r
6128       break;\r
6129 \r
6130     case IDM_Engine1Options:\r
6131       EngineOptionsPopup(hwnd, &first);\r
6132       break;\r
6133 \r
6134     case IDM_Engine2Options:\r
6135       EngineOptionsPopup(hwnd, &second);\r
6136       break;\r
6137 \r
6138     case IDM_OptionsUCI:\r
6139       UciOptionsPopup(hwnd);\r
6140       break;\r
6141 \r
6142     case IDM_IcsOptions:\r
6143       IcsOptionsPopup(hwnd);\r
6144       break;\r
6145 \r
6146     case IDM_Fonts:\r
6147       FontsOptionsPopup(hwnd);\r
6148       break;\r
6149 \r
6150     case IDM_Sounds:\r
6151       SoundOptionsPopup(hwnd);\r
6152       break;\r
6153 \r
6154     case IDM_CommPort:\r
6155       CommPortOptionsPopup(hwnd);\r
6156       break;\r
6157 \r
6158     case IDM_LoadOptions:\r
6159       LoadOptionsPopup(hwnd);\r
6160       break;\r
6161 \r
6162     case IDM_SaveOptions:\r
6163       SaveOptionsPopup(hwnd);\r
6164       break;\r
6165 \r
6166     case IDM_TimeControl:\r
6167       TimeControlOptionsPopup(hwnd);\r
6168       break;\r
6169 \r
6170     case IDM_SaveSettings:\r
6171       SaveSettings(settingsFileName);\r
6172       break;\r
6173 \r
6174     case IDM_SaveSettingsOnExit:\r
6175       saveSettingsOnExit = !saveSettingsOnExit;\r
6176       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6177                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6178                                          MF_CHECKED : MF_UNCHECKED));\r
6179       break;\r
6180 \r
6181     case IDM_Hint:\r
6182       HintEvent();\r
6183       break;\r
6184 \r
6185     case IDM_Book:\r
6186       BookEvent();\r
6187       break;\r
6188 \r
6189     case IDM_AboutGame:\r
6190       AboutGameEvent();\r
6191       break;\r
6192 \r
6193     case IDM_Debug:\r
6194       appData.debugMode = !appData.debugMode;\r
6195       if (appData.debugMode) {\r
6196         char dir[MSG_SIZ];\r
6197         GetCurrentDirectory(MSG_SIZ, dir);\r
6198         SetCurrentDirectory(installDir);\r
6199         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6200         SetCurrentDirectory(dir);\r
6201         setbuf(debugFP, NULL);\r
6202       } else {\r
6203         fclose(debugFP);\r
6204         debugFP = NULL;\r
6205       }\r
6206       break;\r
6207 \r
6208     case IDM_HELPCONTENTS:\r
6209       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6210           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6211           MessageBox (GetFocus(),\r
6212                     "Unable to activate help",\r
6213                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6214       }\r
6215       break;\r
6216 \r
6217     case IDM_HELPSEARCH:\r
6218         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6219             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6220         MessageBox (GetFocus(),\r
6221                     "Unable to activate help",\r
6222                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6223       }\r
6224       break;\r
6225 \r
6226     case IDM_HELPHELP:\r
6227       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6228         MessageBox (GetFocus(),\r
6229                     "Unable to activate help",\r
6230                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6231       }\r
6232       break;\r
6233 \r
6234     case IDM_ABOUT:\r
6235       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6236       DialogBox(hInst, \r
6237         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6238         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6239       FreeProcInstance(lpProc);\r
6240       break;\r
6241 \r
6242     case IDM_DirectCommand1:\r
6243       AskQuestionEvent("Direct Command",\r
6244                        "Send to chess program:", "", "1");\r
6245       break;\r
6246     case IDM_DirectCommand2:\r
6247       AskQuestionEvent("Direct Command",\r
6248                        "Send to second chess program:", "", "2");\r
6249       break;\r
6250 \r
6251     case EP_WhitePawn:\r
6252       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6253       fromX = fromY = -1;\r
6254       break;\r
6255 \r
6256     case EP_WhiteKnight:\r
6257       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6258       fromX = fromY = -1;\r
6259       break;\r
6260 \r
6261     case EP_WhiteBishop:\r
6262       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6263       fromX = fromY = -1;\r
6264       break;\r
6265 \r
6266     case EP_WhiteRook:\r
6267       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6268       fromX = fromY = -1;\r
6269       break;\r
6270 \r
6271     case EP_WhiteQueen:\r
6272       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6273       fromX = fromY = -1;\r
6274       break;\r
6275 \r
6276     case EP_WhiteFerz:\r
6277       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6278       fromX = fromY = -1;\r
6279       break;\r
6280 \r
6281     case EP_WhiteWazir:\r
6282       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6283       fromX = fromY = -1;\r
6284       break;\r
6285 \r
6286     case EP_WhiteAlfil:\r
6287       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6288       fromX = fromY = -1;\r
6289       break;\r
6290 \r
6291     case EP_WhiteCannon:\r
6292       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6293       fromX = fromY = -1;\r
6294       break;\r
6295 \r
6296     case EP_WhiteCardinal:\r
6297       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6298       fromX = fromY = -1;\r
6299       break;\r
6300 \r
6301     case EP_WhiteMarshall:\r
6302       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6303       fromX = fromY = -1;\r
6304       break;\r
6305 \r
6306     case EP_WhiteKing:\r
6307       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6308       fromX = fromY = -1;\r
6309       break;\r
6310 \r
6311     case EP_BlackPawn:\r
6312       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6313       fromX = fromY = -1;\r
6314       break;\r
6315 \r
6316     case EP_BlackKnight:\r
6317       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6318       fromX = fromY = -1;\r
6319       break;\r
6320 \r
6321     case EP_BlackBishop:\r
6322       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6323       fromX = fromY = -1;\r
6324       break;\r
6325 \r
6326     case EP_BlackRook:\r
6327       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6328       fromX = fromY = -1;\r
6329       break;\r
6330 \r
6331     case EP_BlackQueen:\r
6332       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6333       fromX = fromY = -1;\r
6334       break;\r
6335 \r
6336     case EP_BlackFerz:\r
6337       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6338       fromX = fromY = -1;\r
6339       break;\r
6340 \r
6341     case EP_BlackWazir:\r
6342       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6343       fromX = fromY = -1;\r
6344       break;\r
6345 \r
6346     case EP_BlackAlfil:\r
6347       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6348       fromX = fromY = -1;\r
6349       break;\r
6350 \r
6351     case EP_BlackCannon:\r
6352       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6353       fromX = fromY = -1;\r
6354       break;\r
6355 \r
6356     case EP_BlackCardinal:\r
6357       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6358       fromX = fromY = -1;\r
6359       break;\r
6360 \r
6361     case EP_BlackMarshall:\r
6362       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6363       fromX = fromY = -1;\r
6364       break;\r
6365 \r
6366     case EP_BlackKing:\r
6367       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6368       fromX = fromY = -1;\r
6369       break;\r
6370 \r
6371     case EP_EmptySquare:\r
6372       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6373       fromX = fromY = -1;\r
6374       break;\r
6375 \r
6376     case EP_ClearBoard:\r
6377       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6378       fromX = fromY = -1;\r
6379       break;\r
6380 \r
6381     case EP_White:\r
6382       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6383       fromX = fromY = -1;\r
6384       break;\r
6385 \r
6386     case EP_Black:\r
6387       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6388       fromX = fromY = -1;\r
6389       break;\r
6390 \r
6391     case EP_Promote:\r
6392       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6393       fromX = fromY = -1;\r
6394       break;\r
6395 \r
6396     case EP_Demote:\r
6397       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6398       fromX = fromY = -1;\r
6399       break;\r
6400 \r
6401     case DP_Pawn:\r
6402       DropMenuEvent(WhitePawn, fromX, fromY);\r
6403       fromX = fromY = -1;\r
6404       break;\r
6405 \r
6406     case DP_Knight:\r
6407       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6408       fromX = fromY = -1;\r
6409       break;\r
6410 \r
6411     case DP_Bishop:\r
6412       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6413       fromX = fromY = -1;\r
6414       break;\r
6415 \r
6416     case DP_Rook:\r
6417       DropMenuEvent(WhiteRook, fromX, fromY);\r
6418       fromX = fromY = -1;\r
6419       break;\r
6420 \r
6421     case DP_Queen:\r
6422       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6423       fromX = fromY = -1;\r
6424       break;\r
6425 \r
6426     default:\r
6427       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6428     }\r
6429     break;\r
6430 \r
6431   case WM_TIMER:\r
6432     switch (wParam) {\r
6433     case CLOCK_TIMER_ID:\r
6434       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6435       clockTimerEvent = 0;\r
6436       DecrementClocks(); /* call into back end */\r
6437       break;\r
6438     case LOAD_GAME_TIMER_ID:\r
6439       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6440       loadGameTimerEvent = 0;\r
6441       AutoPlayGameLoop(); /* call into back end */\r
6442       break;\r
6443     case ANALYSIS_TIMER_ID:\r
6444       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6445                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6446         AnalysisPeriodicEvent(0);\r
6447       } else {\r
6448         KillTimer(hwnd, analysisTimerEvent);\r
6449         analysisTimerEvent = 0;\r
6450       }\r
6451       break;\r
6452     case DELAYED_TIMER_ID:\r
6453       KillTimer(hwnd, delayedTimerEvent);\r
6454       delayedTimerEvent = 0;\r
6455       delayedTimerCallback();\r
6456       break;\r
6457     }\r
6458     break;\r
6459 \r
6460   case WM_USER_Input:\r
6461     InputEvent(hwnd, message, wParam, lParam);\r
6462     break;\r
6463 \r
6464   /* [AS] Also move "attached" child windows */\r
6465   case WM_WINDOWPOSCHANGING:\r
6466 \r
6467     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6468         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6469 \r
6470         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6471             /* Window is moving */\r
6472             RECT rcMain;\r
6473 \r
6474 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6475             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6476             rcMain.right  = boardX + winWidth;\r
6477             rcMain.top    = boardY;\r
6478             rcMain.bottom = boardY + winHeight;\r
6479             \r
6480             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6481             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6482             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6483             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6484             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6485             boardX = lpwp->x;\r
6486             boardY = lpwp->y;\r
6487         }\r
6488     }\r
6489     break;\r
6490 \r
6491   /* [AS] Snapping */\r
6492   case WM_ENTERSIZEMOVE:\r
6493     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6494     if (hwnd == hwndMain) {\r
6495       doingSizing = TRUE;\r
6496       lastSizing = 0;\r
6497     }\r
6498     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6499     break;\r
6500 \r
6501   case WM_SIZING:\r
6502     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6503     if (hwnd == hwndMain) {\r
6504       lastSizing = wParam;\r
6505     }\r
6506     break;\r
6507 \r
6508   case WM_MOVING:\r
6509     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6510       return OnMoving( &sd, hwnd, wParam, lParam );\r
6511 \r
6512   case WM_EXITSIZEMOVE:\r
6513     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6514     if (hwnd == hwndMain) {\r
6515       RECT client;\r
6516       doingSizing = FALSE;\r
6517       InvalidateRect(hwnd, &boardRect, FALSE);\r
6518       GetClientRect(hwnd, &client);\r
6519       ResizeBoard(client.right, client.bottom, lastSizing);\r
6520       lastSizing = 0;\r
6521       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6522     }\r
6523     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6524     break;\r
6525 \r
6526   case WM_DESTROY: /* message: window being destroyed */\r
6527     PostQuitMessage(0);\r
6528     break;\r
6529 \r
6530   case WM_CLOSE:\r
6531     if (hwnd == hwndMain) {\r
6532       ExitEvent(0);\r
6533     }\r
6534     break;\r
6535 \r
6536   default:      /* Passes it on if unprocessed */\r
6537     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6538   }\r
6539   return 0;\r
6540 }\r
6541 \r
6542 /*---------------------------------------------------------------------------*\\r
6543  *\r
6544  * Misc utility routines\r
6545  *\r
6546 \*---------------------------------------------------------------------------*/\r
6547 \r
6548 /*\r
6549  * Decent random number generator, at least not as bad as Windows\r
6550  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6551  */\r
6552 unsigned int randstate;\r
6553 \r
6554 int\r
6555 myrandom(void)\r
6556 {\r
6557   randstate = randstate * 1664525 + 1013904223;\r
6558   return (int) randstate & 0x7fffffff;\r
6559 }\r
6560 \r
6561 void\r
6562 mysrandom(unsigned int seed)\r
6563 {\r
6564   randstate = seed;\r
6565 }\r
6566 \r
6567 \r
6568 /* \r
6569  * returns TRUE if user selects a different color, FALSE otherwise \r
6570  */\r
6571 \r
6572 BOOL\r
6573 ChangeColor(HWND hwnd, COLORREF *which)\r
6574 {\r
6575   static BOOL firstTime = TRUE;\r
6576   static DWORD customColors[16];\r
6577   CHOOSECOLOR cc;\r
6578   COLORREF newcolor;\r
6579   int i;\r
6580   ColorClass ccl;\r
6581 \r
6582   if (firstTime) {\r
6583     /* Make initial colors in use available as custom colors */\r
6584     /* Should we put the compiled-in defaults here instead? */\r
6585     i = 0;\r
6586     customColors[i++] = lightSquareColor & 0xffffff;\r
6587     customColors[i++] = darkSquareColor & 0xffffff;\r
6588     customColors[i++] = whitePieceColor & 0xffffff;\r
6589     customColors[i++] = blackPieceColor & 0xffffff;\r
6590     customColors[i++] = highlightSquareColor & 0xffffff;\r
6591     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6592 \r
6593     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6594       customColors[i++] = textAttribs[ccl].color;\r
6595     }\r
6596     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6597     firstTime = FALSE;\r
6598   }\r
6599 \r
6600   cc.lStructSize = sizeof(cc);\r
6601   cc.hwndOwner = hwnd;\r
6602   cc.hInstance = NULL;\r
6603   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6604   cc.lpCustColors = (LPDWORD) customColors;\r
6605   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6606 \r
6607   if (!ChooseColor(&cc)) return FALSE;\r
6608 \r
6609   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6610   if (newcolor == *which) return FALSE;\r
6611   *which = newcolor;\r
6612   return TRUE;\r
6613 \r
6614   /*\r
6615   InitDrawingColors();\r
6616   InvalidateRect(hwnd, &boardRect, FALSE);\r
6617   */\r
6618 }\r
6619 \r
6620 BOOLEAN\r
6621 MyLoadSound(MySound *ms)\r
6622 {\r
6623   BOOL ok = FALSE;\r
6624   struct stat st;\r
6625   FILE *f;\r
6626 \r
6627   if (ms->data) free(ms->data);\r
6628   ms->data = NULL;\r
6629 \r
6630   switch (ms->name[0]) {\r
6631   case NULLCHAR:\r
6632     /* Silence */\r
6633     ok = TRUE;\r
6634     break;\r
6635   case '$':\r
6636     /* System sound from Control Panel.  Don't preload here. */\r
6637     ok = TRUE;\r
6638     break;\r
6639   case '!':\r
6640     if (ms->name[1] == NULLCHAR) {\r
6641       /* "!" alone = silence */\r
6642       ok = TRUE;\r
6643     } else {\r
6644       /* Builtin wave resource.  Error if not found. */\r
6645       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6646       if (h == NULL) break;\r
6647       ms->data = (void *)LoadResource(hInst, h);\r
6648       if (h == NULL) break;\r
6649       ok = TRUE;\r
6650     }\r
6651     break;\r
6652   default:\r
6653     /* .wav file.  Error if not found. */\r
6654     f = fopen(ms->name, "rb");\r
6655     if (f == NULL) break;\r
6656     if (fstat(fileno(f), &st) < 0) break;\r
6657     ms->data = malloc(st.st_size);\r
6658     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6659     fclose(f);\r
6660     ok = TRUE;\r
6661     break;\r
6662   }\r
6663   if (!ok) {\r
6664     char buf[MSG_SIZ];\r
6665     sprintf(buf, "Error loading sound %s", ms->name);\r
6666     DisplayError(buf, GetLastError());\r
6667   }\r
6668   return ok;\r
6669 }\r
6670 \r
6671 BOOLEAN\r
6672 MyPlaySound(MySound *ms)\r
6673 {\r
6674   BOOLEAN ok = FALSE;\r
6675 \r
6676   switch (ms->name[0]) {\r
6677   case NULLCHAR:\r
6678         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6679     /* Silence */\r
6680     ok = TRUE;\r
6681     break;\r
6682   case '$':\r
6683     /* System sound from Control Panel (deprecated feature).\r
6684        "$" alone or an unset sound name gets default beep (still in use). */\r
6685     if (ms->name[1]) {\r
6686       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6687     }\r
6688     if (!ok) ok = MessageBeep(MB_OK);\r
6689     break; \r
6690   case '!':\r
6691     /* Builtin wave resource, or "!" alone for silence */\r
6692     if (ms->name[1]) {\r
6693       if (ms->data == NULL) return FALSE;\r
6694       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6695     } else {\r
6696       ok = TRUE;\r
6697     }\r
6698     break;\r
6699   default:\r
6700     /* .wav file.  Error if not found. */\r
6701     if (ms->data == NULL) return FALSE;\r
6702     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6703     break;\r
6704   }\r
6705   /* Don't print an error: this can happen innocently if the sound driver\r
6706      is busy; for instance, if another instance of WinBoard is playing\r
6707      a sound at about the same time. */\r
6708 #if 0\r
6709   if (!ok) {\r
6710     char buf[MSG_SIZ];\r
6711     sprintf(buf, "Error playing sound %s", ms->name);\r
6712     DisplayError(buf, GetLastError());\r
6713   }\r
6714 #endif\r
6715   return ok;\r
6716 }\r
6717 \r
6718 \r
6719 LRESULT CALLBACK\r
6720 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6721 {\r
6722   BOOL ok;\r
6723   OPENFILENAME *ofn;\r
6724   static UINT *number; /* gross that this is static */\r
6725 \r
6726   switch (message) {\r
6727   case WM_INITDIALOG: /* message: initialize dialog box */\r
6728     /* Center the dialog over the application window */\r
6729     ofn = (OPENFILENAME *) lParam;\r
6730     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6731       number = (UINT *) ofn->lCustData;\r
6732       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6733     } else {\r
6734       number = NULL;\r
6735     }\r
6736     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6737     return FALSE;  /* Allow for further processing */\r
6738 \r
6739   case WM_COMMAND:\r
6740     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6741       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6742     }\r
6743     return FALSE;  /* Allow for further processing */\r
6744   }\r
6745   return FALSE;\r
6746 }\r
6747 \r
6748 UINT APIENTRY\r
6749 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6750 {\r
6751   static UINT *number;\r
6752   OPENFILENAME *ofname;\r
6753   OFNOTIFY *ofnot;\r
6754   switch (uiMsg) {\r
6755   case WM_INITDIALOG:\r
6756     ofname = (OPENFILENAME *)lParam;\r
6757     number = (UINT *)(ofname->lCustData);\r
6758     break;\r
6759   case WM_NOTIFY:\r
6760     ofnot = (OFNOTIFY *)lParam;\r
6761     if (ofnot->hdr.code == CDN_FILEOK) {\r
6762       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6763     }\r
6764     break;\r
6765   }\r
6766   return 0;\r
6767 }\r
6768 \r
6769 \r
6770 FILE *\r
6771 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6772                char *nameFilt, char *dlgTitle, UINT *number,\r
6773                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6774 {\r
6775   OPENFILENAME openFileName;\r
6776   char buf1[MSG_SIZ];\r
6777   FILE *f;\r
6778 \r
6779   if (fileName == NULL) fileName = buf1;\r
6780   if (defName == NULL) {\r
6781     strcpy(fileName, "*.");\r
6782     strcat(fileName, defExt);\r
6783   } else {\r
6784     strcpy(fileName, defName);\r
6785   }\r
6786   if (fileTitle) strcpy(fileTitle, "");\r
6787   if (number) *number = 0;\r
6788 \r
6789   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6790   openFileName.hwndOwner         = hwnd;\r
6791   openFileName.hInstance         = (HANDLE) hInst;\r
6792   openFileName.lpstrFilter       = nameFilt;\r
6793   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6794   openFileName.nMaxCustFilter    = 0L;\r
6795   openFileName.nFilterIndex      = 1L;\r
6796   openFileName.lpstrFile         = fileName;\r
6797   openFileName.nMaxFile          = MSG_SIZ;\r
6798   openFileName.lpstrFileTitle    = fileTitle;\r
6799   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6800   openFileName.lpstrInitialDir   = NULL;\r
6801   openFileName.lpstrTitle        = dlgTitle;\r
6802   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6803     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6804     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6805     | (oldDialog ? 0 : OFN_EXPLORER);\r
6806   openFileName.nFileOffset       = 0;\r
6807   openFileName.nFileExtension    = 0;\r
6808   openFileName.lpstrDefExt       = defExt;\r
6809   openFileName.lCustData         = (LONG) number;\r
6810   openFileName.lpfnHook          = oldDialog ?\r
6811     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6812   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6813 \r
6814   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6815                         GetOpenFileName(&openFileName)) {\r
6816     /* open the file */\r
6817     f = fopen(openFileName.lpstrFile, write);\r
6818     if (f == NULL) {\r
6819       MessageBox(hwnd, "File open failed", NULL,\r
6820                  MB_OK|MB_ICONEXCLAMATION);\r
6821       return NULL;\r
6822     }\r
6823   } else {\r
6824     int err = CommDlgExtendedError();\r
6825     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6826     return FALSE;\r
6827   }\r
6828   return f;\r
6829 }\r
6830 \r
6831 \r
6832 \r
6833 VOID APIENTRY\r
6834 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6835 {\r
6836   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6837 \r
6838   /*\r
6839    * Get the first pop-up menu in the menu template. This is the\r
6840    * menu that TrackPopupMenu displays.\r
6841    */\r
6842   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6843 \r
6844   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6845 \r
6846   /*\r
6847    * TrackPopup uses screen coordinates, so convert the\r
6848    * coordinates of the mouse click to screen coordinates.\r
6849    */\r
6850   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6851 \r
6852   /* Draw and track the floating pop-up menu. */\r
6853   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6854                  pt.x, pt.y, 0, hwnd, NULL);\r
6855 \r
6856   /* Destroy the menu.*/\r
6857   DestroyMenu(hmenu);\r
6858 }\r
6859    \r
6860 typedef struct {\r
6861   HWND hDlg, hText;\r
6862   int sizeX, sizeY, newSizeX, newSizeY;\r
6863   HDWP hdwp;\r
6864 } ResizeEditPlusButtonsClosure;\r
6865 \r
6866 BOOL CALLBACK\r
6867 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6868 {\r
6869   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6870   RECT rect;\r
6871   POINT pt;\r
6872 \r
6873   if (hChild == cl->hText) return TRUE;\r
6874   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6875   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6876   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6877   ScreenToClient(cl->hDlg, &pt);\r
6878   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6879     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6880   return TRUE;\r
6881 }\r
6882 \r
6883 /* Resize a dialog that has a (rich) edit field filling most of\r
6884    the top, with a row of buttons below */\r
6885 VOID\r
6886 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6887 {\r
6888   RECT rectText;\r
6889   int newTextHeight, newTextWidth;\r
6890   ResizeEditPlusButtonsClosure cl;\r
6891   \r
6892   /*if (IsIconic(hDlg)) return;*/\r
6893   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6894   \r
6895   cl.hdwp = BeginDeferWindowPos(8);\r
6896 \r
6897   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6898   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6899   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6900   if (newTextHeight < 0) {\r
6901     newSizeY += -newTextHeight;\r
6902     newTextHeight = 0;\r
6903   }\r
6904   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6905     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6906 \r
6907   cl.hDlg = hDlg;\r
6908   cl.hText = hText;\r
6909   cl.sizeX = sizeX;\r
6910   cl.sizeY = sizeY;\r
6911   cl.newSizeX = newSizeX;\r
6912   cl.newSizeY = newSizeY;\r
6913   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6914 \r
6915   EndDeferWindowPos(cl.hdwp);\r
6916 }\r
6917 \r
6918 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6919 {\r
6920     RECT    rChild, rParent;\r
6921     int     wChild, hChild, wParent, hParent;\r
6922     int     wScreen, hScreen, xNew, yNew;\r
6923     HDC     hdc;\r
6924 \r
6925     /* Get the Height and Width of the child window */\r
6926     GetWindowRect (hwndChild, &rChild);\r
6927     wChild = rChild.right - rChild.left;\r
6928     hChild = rChild.bottom - rChild.top;\r
6929 \r
6930     /* Get the Height and Width of the parent window */\r
6931     GetWindowRect (hwndParent, &rParent);\r
6932     wParent = rParent.right - rParent.left;\r
6933     hParent = rParent.bottom - rParent.top;\r
6934 \r
6935     /* Get the display limits */\r
6936     hdc = GetDC (hwndChild);\r
6937     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6938     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6939     ReleaseDC(hwndChild, hdc);\r
6940 \r
6941     /* Calculate new X position, then adjust for screen */\r
6942     xNew = rParent.left + ((wParent - wChild) /2);\r
6943     if (xNew < 0) {\r
6944         xNew = 0;\r
6945     } else if ((xNew+wChild) > wScreen) {\r
6946         xNew = wScreen - wChild;\r
6947     }\r
6948 \r
6949     /* Calculate new Y position, then adjust for screen */\r
6950     if( mode == 0 ) {\r
6951         yNew = rParent.top  + ((hParent - hChild) /2);\r
6952     }\r
6953     else {\r
6954         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6955     }\r
6956 \r
6957     if (yNew < 0) {\r
6958         yNew = 0;\r
6959     } else if ((yNew+hChild) > hScreen) {\r
6960         yNew = hScreen - hChild;\r
6961     }\r
6962 \r
6963     /* Set it, and return */\r
6964     return SetWindowPos (hwndChild, NULL,\r
6965                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6966 }\r
6967 \r
6968 /* Center one window over another */\r
6969 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6970 {\r
6971     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6972 }\r
6973 \r
6974 /*---------------------------------------------------------------------------*\\r
6975  *\r
6976  * Startup Dialog functions\r
6977  *\r
6978 \*---------------------------------------------------------------------------*/\r
6979 void\r
6980 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6981 {\r
6982   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6983 \r
6984   while (*cd != NULL) {\r
6985     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6986     cd++;\r
6987   }\r
6988 }\r
6989 \r
6990 void\r
6991 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6992 {\r
6993   char buf1[ARG_MAX];\r
6994   int len;\r
6995 \r
6996   if (str[0] == '@') {\r
6997     FILE* f = fopen(str + 1, "r");\r
6998     if (f == NULL) {\r
6999       DisplayFatalError(str + 1, errno, 2);\r
7000       return;\r
7001     }\r
7002     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7003     fclose(f);\r
7004     buf1[len] = NULLCHAR;\r
7005     str = buf1;\r
7006   }\r
7007 \r
7008   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7009 \r
7010   for (;;) {\r
7011     char buf[MSG_SIZ];\r
7012     char *end = strchr(str, '\n');\r
7013     if (end == NULL) return;\r
7014     memcpy(buf, str, end - str);\r
7015     buf[end - str] = NULLCHAR;\r
7016     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7017     str = end + 1;\r
7018   }\r
7019 }\r
7020 \r
7021 void\r
7022 SetStartupDialogEnables(HWND hDlg)\r
7023 {\r
7024   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7025     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7026     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7027   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7028     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7029   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7030     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7031   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7032     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7033   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7034     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7035     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7036     IsDlgButtonChecked(hDlg, OPT_View));\r
7037 }\r
7038 \r
7039 char *\r
7040 QuoteForFilename(char *filename)\r
7041 {\r
7042   int dquote, space;\r
7043   dquote = strchr(filename, '"') != NULL;\r
7044   space = strchr(filename, ' ') != NULL;\r
7045   if (dquote || space) {\r
7046     if (dquote) {\r
7047       return "'";\r
7048     } else {\r
7049       return "\"";\r
7050     }\r
7051   } else {\r
7052     return "";\r
7053   }\r
7054 }\r
7055 \r
7056 VOID\r
7057 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7058 {\r
7059   char buf[MSG_SIZ];\r
7060   char *q;\r
7061 \r
7062   InitComboStringsFromOption(hwndCombo, nthnames);\r
7063   q = QuoteForFilename(nthcp);\r
7064   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7065   if (*nthdir != NULLCHAR) {\r
7066     q = QuoteForFilename(nthdir);\r
7067     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7068   }\r
7069   if (*nthcp == NULLCHAR) {\r
7070     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7071   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7072     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7073     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7074   }\r
7075 }\r
7076 \r
7077 LRESULT CALLBACK\r
7078 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7079 {\r
7080   char buf[MSG_SIZ];\r
7081   HANDLE hwndCombo;\r
7082   char *p;\r
7083 \r
7084   switch (message) {\r
7085   case WM_INITDIALOG:\r
7086     /* Center the dialog */\r
7087     CenterWindow (hDlg, GetDesktopWindow());\r
7088     /* Initialize the dialog items */\r
7089     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7090                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7091                   firstChessProgramNames);\r
7092     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7093                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7094                   secondChessProgramNames);\r
7095     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7096     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7097     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7098     if (*appData.icsHelper != NULLCHAR) {\r
7099       char *q = QuoteForFilename(appData.icsHelper);\r
7100       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7101     }\r
7102     if (*appData.icsHost == NULLCHAR) {\r
7103       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7104       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7105     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7106       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7107       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7108     }\r
7109 \r
7110     if (appData.icsActive) {\r
7111       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7112     }\r
7113     else if (appData.noChessProgram) {\r
7114       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7115     }\r
7116     else {\r
7117       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7118     }\r
7119 \r
7120     SetStartupDialogEnables(hDlg);\r
7121     return TRUE;\r
7122 \r
7123   case WM_COMMAND:\r
7124     switch (LOWORD(wParam)) {\r
7125     case IDOK:\r
7126       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7127         strcpy(buf, "/fcp=");\r
7128         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7129         p = buf;\r
7130         ParseArgs(StringGet, &p);\r
7131         strcpy(buf, "/scp=");\r
7132         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7133         p = buf;\r
7134         ParseArgs(StringGet, &p);\r
7135         appData.noChessProgram = FALSE;\r
7136         appData.icsActive = FALSE;\r
7137       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7138         strcpy(buf, "/ics /icshost=");\r
7139         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7140         p = buf;\r
7141         ParseArgs(StringGet, &p);\r
7142         if (appData.zippyPlay) {\r
7143           strcpy(buf, "/fcp=");\r
7144           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7145           p = buf;\r
7146           ParseArgs(StringGet, &p);\r
7147         }\r
7148       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7149         appData.noChessProgram = TRUE;\r
7150         appData.icsActive = FALSE;\r
7151       } else {\r
7152         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7153                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7154         return TRUE;\r
7155       }\r
7156       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7157         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7158         p = buf;\r
7159         ParseArgs(StringGet, &p);\r
7160       }\r
7161       EndDialog(hDlg, TRUE);\r
7162       return TRUE;\r
7163 \r
7164     case IDCANCEL:\r
7165       ExitEvent(0);\r
7166       return TRUE;\r
7167 \r
7168     case IDM_HELPCONTENTS:\r
7169       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7170         MessageBox (GetFocus(),\r
7171                     "Unable to activate help",\r
7172                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7173       }\r
7174       break;\r
7175 \r
7176     default:\r
7177       SetStartupDialogEnables(hDlg);\r
7178       break;\r
7179     }\r
7180     break;\r
7181   }\r
7182   return FALSE;\r
7183 }\r
7184 \r
7185 /*---------------------------------------------------------------------------*\\r
7186  *\r
7187  * About box dialog functions\r
7188  *\r
7189 \*---------------------------------------------------------------------------*/\r
7190 \r
7191 /* Process messages for "About" dialog box */\r
7192 LRESULT CALLBACK\r
7193 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7194 {\r
7195   switch (message) {\r
7196   case WM_INITDIALOG: /* message: initialize dialog box */\r
7197     /* Center the dialog over the application window */\r
7198     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7199     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7200     JAWS_COPYRIGHT\r
7201     return (TRUE);\r
7202 \r
7203   case WM_COMMAND: /* message: received a command */\r
7204     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7205         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7206       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7207       return (TRUE);\r
7208     }\r
7209     break;\r
7210   }\r
7211   return (FALSE);\r
7212 }\r
7213 \r
7214 /*---------------------------------------------------------------------------*\\r
7215  *\r
7216  * Comment Dialog functions\r
7217  *\r
7218 \*---------------------------------------------------------------------------*/\r
7219 \r
7220 LRESULT CALLBACK\r
7221 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7222 {\r
7223   static HANDLE hwndText = NULL;\r
7224   int len, newSizeX, newSizeY, flags;\r
7225   static int sizeX, sizeY;\r
7226   char *str;\r
7227   RECT rect;\r
7228   MINMAXINFO *mmi;\r
7229 \r
7230   switch (message) {\r
7231   case WM_INITDIALOG: /* message: initialize dialog box */\r
7232     /* Initialize the dialog items */\r
7233     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7234     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7235     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7236     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7237     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7238     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7239     SetWindowText(hDlg, commentTitle);\r
7240     if (editComment) {\r
7241       SetFocus(hwndText);\r
7242     } else {\r
7243       SetFocus(GetDlgItem(hDlg, IDOK));\r
7244     }\r
7245     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7246                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7247                 MAKELPARAM(FALSE, 0));\r
7248     /* Size and position the dialog */\r
7249     if (!commentDialog) {\r
7250       commentDialog = hDlg;\r
7251       flags = SWP_NOZORDER;\r
7252       GetClientRect(hDlg, &rect);\r
7253       sizeX = rect.right;\r
7254       sizeY = rect.bottom;\r
7255       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7256           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7257         WINDOWPLACEMENT wp;\r
7258         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7259         wp.length = sizeof(WINDOWPLACEMENT);\r
7260         wp.flags = 0;\r
7261         wp.showCmd = SW_SHOW;\r
7262         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7263         wp.rcNormalPosition.left = commentX;\r
7264         wp.rcNormalPosition.right = commentX + commentW;\r
7265         wp.rcNormalPosition.top = commentY;\r
7266         wp.rcNormalPosition.bottom = commentY + commentH;\r
7267         SetWindowPlacement(hDlg, &wp);\r
7268 \r
7269         GetClientRect(hDlg, &rect);\r
7270         newSizeX = rect.right;\r
7271         newSizeY = rect.bottom;\r
7272         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7273                               newSizeX, newSizeY);\r
7274         sizeX = newSizeX;\r
7275         sizeY = newSizeY;\r
7276       }\r
7277     }\r
7278     return FALSE;\r
7279 \r
7280   case WM_COMMAND: /* message: received a command */\r
7281     switch (LOWORD(wParam)) {\r
7282     case IDOK:\r
7283       if (editComment) {\r
7284         char *p, *q;\r
7285         /* Read changed options from the dialog box */\r
7286         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7287         len = GetWindowTextLength(hwndText);\r
7288         str = (char *) malloc(len + 1);\r
7289         GetWindowText(hwndText, str, len + 1);\r
7290         p = q = str;\r
7291         while (*q) {\r
7292           if (*q == '\r')\r
7293             q++;\r
7294           else\r
7295             *p++ = *q++;\r
7296         }\r
7297         *p = NULLCHAR;\r
7298         ReplaceComment(commentIndex, str);\r
7299         free(str);\r
7300       }\r
7301       CommentPopDown();\r
7302       return TRUE;\r
7303 \r
7304     case IDCANCEL:\r
7305     case OPT_CancelComment:\r
7306       CommentPopDown();\r
7307       return TRUE;\r
7308 \r
7309     case OPT_ClearComment:\r
7310       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7311       break;\r
7312 \r
7313     case OPT_EditComment:\r
7314       EditCommentEvent();\r
7315       return TRUE;\r
7316 \r
7317     default:\r
7318       break;\r
7319     }\r
7320     break;\r
7321 \r
7322   case WM_SIZE:\r
7323     newSizeX = LOWORD(lParam);\r
7324     newSizeY = HIWORD(lParam);\r
7325     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7326     sizeX = newSizeX;\r
7327     sizeY = newSizeY;\r
7328     break;\r
7329 \r
7330   case WM_GETMINMAXINFO:\r
7331     /* Prevent resizing window too small */\r
7332     mmi = (MINMAXINFO *) lParam;\r
7333     mmi->ptMinTrackSize.x = 100;\r
7334     mmi->ptMinTrackSize.y = 100;\r
7335     break;\r
7336   }\r
7337   return FALSE;\r
7338 }\r
7339 \r
7340 VOID\r
7341 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7342 {\r
7343   FARPROC lpProc;\r
7344   char *p, *q;\r
7345 \r
7346   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7347 \r
7348   if (str == NULL) str = "";\r
7349   p = (char *) malloc(2 * strlen(str) + 2);\r
7350   q = p;\r
7351   while (*str) {\r
7352     if (*str == '\n') *q++ = '\r';\r
7353     *q++ = *str++;\r
7354   }\r
7355   *q = NULLCHAR;\r
7356   if (commentText != NULL) free(commentText);\r
7357 \r
7358   commentIndex = index;\r
7359   commentTitle = title;\r
7360   commentText = p;\r
7361   editComment = edit;\r
7362 \r
7363   if (commentDialog) {\r
7364     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7365     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7366   } else {\r
7367     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7368     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7369                  hwndMain, (DLGPROC)lpProc);\r
7370     FreeProcInstance(lpProc);\r
7371   }\r
7372   commentDialogUp = TRUE;\r
7373 }\r
7374 \r
7375 \r
7376 /*---------------------------------------------------------------------------*\\r
7377  *\r
7378  * Type-in move dialog functions\r
7379  * \r
7380 \*---------------------------------------------------------------------------*/\r
7381 \r
7382 LRESULT CALLBACK\r
7383 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7384 {\r
7385   char move[MSG_SIZ];\r
7386   HWND hInput;\r
7387   ChessMove moveType;\r
7388   int fromX, fromY, toX, toY;\r
7389   char promoChar;\r
7390 \r
7391   switch (message) {\r
7392   case WM_INITDIALOG:\r
7393     move[0] = (char) lParam;\r
7394     move[1] = NULLCHAR;\r
7395     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7396     hInput = GetDlgItem(hDlg, OPT_Move);\r
7397     SetWindowText(hInput, move);\r
7398     SetFocus(hInput);\r
7399     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7400     return FALSE;\r
7401 \r
7402   case WM_COMMAND:\r
7403     switch (LOWORD(wParam)) {\r
7404     case IDOK:\r
7405       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7406       { int n; Board board;\r
7407         // [HGM] FENedit\r
7408         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7409                 EditPositionPasteFEN(move);\r
7410                 EndDialog(hDlg, TRUE);\r
7411                 return TRUE;\r
7412         }\r
7413         // [HGM] movenum: allow move number to be typed in any mode\r
7414         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7415           currentMove = 2*n-1;\r
7416           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7417           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7418           EndDialog(hDlg, TRUE);\r
7419           DrawPosition(TRUE, boards[currentMove]);\r
7420           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7421           else DisplayMessage("", "");\r
7422           return TRUE;\r
7423         }\r
7424       }\r
7425       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7426         gameMode != Training) {\r
7427         DisplayMoveError("Displayed move is not current");\r
7428       } else {\r
7429 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7430         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7431           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7432         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7433         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7434           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7435           if (gameMode != Training)\r
7436               forwardMostMove = currentMove;\r
7437           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7438         } else {\r
7439           DisplayMoveError("Could not parse move");\r
7440         }\r
7441       }\r
7442       EndDialog(hDlg, TRUE);\r
7443       return TRUE;\r
7444     case IDCANCEL:\r
7445       EndDialog(hDlg, FALSE);\r
7446       return TRUE;\r
7447     default:\r
7448       break;\r
7449     }\r
7450     break;\r
7451   }\r
7452   return FALSE;\r
7453 }\r
7454 \r
7455 VOID\r
7456 PopUpMoveDialog(char firstchar)\r
7457 {\r
7458     FARPROC lpProc;\r
7459     \r
7460     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7461         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7462         gameMode == AnalyzeMode || gameMode == EditGame || \r
7463         gameMode == EditPosition || gameMode == IcsExamining ||\r
7464         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7465         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7466                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7467                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7468         gameMode == Training) {\r
7469       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7470       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7471         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7472       FreeProcInstance(lpProc);\r
7473     }\r
7474 }\r
7475 \r
7476 /*---------------------------------------------------------------------------*\\r
7477  *\r
7478  * Type-in name dialog functions\r
7479  * \r
7480 \*---------------------------------------------------------------------------*/\r
7481 \r
7482 LRESULT CALLBACK\r
7483 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7484 {\r
7485   char move[MSG_SIZ];\r
7486   HWND hInput;\r
7487 \r
7488   switch (message) {\r
7489   case WM_INITDIALOG:\r
7490     move[0] = (char) lParam;\r
7491     move[1] = NULLCHAR;\r
7492     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7493     hInput = GetDlgItem(hDlg, OPT_Name);\r
7494     SetWindowText(hInput, move);\r
7495     SetFocus(hInput);\r
7496     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7497     return FALSE;\r
7498 \r
7499   case WM_COMMAND:\r
7500     switch (LOWORD(wParam)) {\r
7501     case IDOK:\r
7502       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7503       appData.userName = strdup(move);\r
7504       SetUserLogo();\r
7505 \r
7506       EndDialog(hDlg, TRUE);\r
7507       return TRUE;\r
7508     case IDCANCEL:\r
7509       EndDialog(hDlg, FALSE);\r
7510       return TRUE;\r
7511     default:\r
7512       break;\r
7513     }\r
7514     break;\r
7515   }\r
7516   return FALSE;\r
7517 }\r
7518 \r
7519 VOID\r
7520 PopUpNameDialog(char firstchar)\r
7521 {\r
7522     FARPROC lpProc;\r
7523     \r
7524       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7525       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7526         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7527       FreeProcInstance(lpProc);\r
7528 }\r
7529 \r
7530 /*---------------------------------------------------------------------------*\\r
7531  *\r
7532  *  Error dialogs\r
7533  * \r
7534 \*---------------------------------------------------------------------------*/\r
7535 \r
7536 /* Nonmodal error box */\r
7537 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7538                              WPARAM wParam, LPARAM lParam);\r
7539 \r
7540 VOID\r
7541 ErrorPopUp(char *title, char *content)\r
7542 {\r
7543   FARPROC lpProc;\r
7544   char *p, *q;\r
7545   BOOLEAN modal = hwndMain == NULL;\r
7546 \r
7547   p = content;\r
7548   q = errorMessage;\r
7549   while (*p) {\r
7550     if (*p == '\n') {\r
7551       if (modal) {\r
7552         *q++ = ' ';\r
7553         p++;\r
7554       } else {\r
7555         *q++ = '\r';\r
7556         *q++ = *p++;\r
7557       }\r
7558     } else {\r
7559       *q++ = *p++;\r
7560     }\r
7561   }\r
7562   *q = NULLCHAR;\r
7563   strncpy(errorTitle, title, sizeof(errorTitle));\r
7564   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7565   \r
7566   if (modal) {\r
7567     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7568   } else {\r
7569     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7570     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7571                  hwndMain, (DLGPROC)lpProc);\r
7572     FreeProcInstance(lpProc);\r
7573   }\r
7574 }\r
7575 \r
7576 VOID\r
7577 ErrorPopDown()\r
7578 {\r
7579   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7580   if (errorDialog == NULL) return;\r
7581   DestroyWindow(errorDialog);\r
7582   errorDialog = NULL;\r
7583 }\r
7584 \r
7585 LRESULT CALLBACK\r
7586 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7587 {\r
7588   HANDLE hwndText;\r
7589   RECT rChild;\r
7590 \r
7591   switch (message) {\r
7592   case WM_INITDIALOG:\r
7593     GetWindowRect(hDlg, &rChild);\r
7594 \r
7595     /*\r
7596     SetWindowPos(hDlg, NULL, rChild.left,\r
7597       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7598       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7599     */\r
7600 \r
7601     /* \r
7602         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7603         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7604         and it doesn't work when you resize the dialog.\r
7605         For now, just give it a default position.\r
7606     */\r
7607     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7608 \r
7609     errorDialog = hDlg;\r
7610     SetWindowText(hDlg, errorTitle);\r
7611     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7612     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7613     return FALSE;\r
7614 \r
7615   case WM_COMMAND:\r
7616     switch (LOWORD(wParam)) {\r
7617     case IDOK:\r
7618     case IDCANCEL:\r
7619       if (errorDialog == hDlg) errorDialog = NULL;\r
7620       DestroyWindow(hDlg);\r
7621       return TRUE;\r
7622 \r
7623     default:\r
7624       break;\r
7625     }\r
7626     break;\r
7627   }\r
7628   return FALSE;\r
7629 }\r
7630 \r
7631 #ifdef GOTHIC\r
7632 HWND gothicDialog = NULL;\r
7633 \r
7634 LRESULT CALLBACK\r
7635 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7636 {\r
7637   HANDLE hwndText;\r
7638   RECT rChild;\r
7639   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7640 \r
7641   switch (message) {\r
7642   case WM_INITDIALOG:\r
7643     GetWindowRect(hDlg, &rChild);\r
7644 \r
7645     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7646                                                              SWP_NOZORDER);\r
7647 \r
7648     /* \r
7649         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7650         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7651         and it doesn't work when you resize the dialog.\r
7652         For now, just give it a default position.\r
7653     */\r
7654     gothicDialog = hDlg;\r
7655     SetWindowText(hDlg, errorTitle);\r
7656     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7657     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7658     return FALSE;\r
7659 \r
7660   case WM_COMMAND:\r
7661     switch (LOWORD(wParam)) {\r
7662     case IDOK:\r
7663     case IDCANCEL:\r
7664       if (errorDialog == hDlg) errorDialog = NULL;\r
7665       DestroyWindow(hDlg);\r
7666       return TRUE;\r
7667 \r
7668     default:\r
7669       break;\r
7670     }\r
7671     break;\r
7672   }\r
7673   return FALSE;\r
7674 }\r
7675 \r
7676 VOID\r
7677 GothicPopUp(char *title, VariantClass variant)\r
7678 {\r
7679   FARPROC lpProc;\r
7680   static char *lastTitle;\r
7681 \r
7682   strncpy(errorTitle, title, sizeof(errorTitle));\r
7683   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7684 \r
7685   if(lastTitle != title && gothicDialog != NULL) {\r
7686     DestroyWindow(gothicDialog);\r
7687     gothicDialog = NULL;\r
7688   }\r
7689   if(variant != VariantNormal && gothicDialog == NULL) {\r
7690     title = lastTitle;\r
7691     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7692     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7693                  hwndMain, (DLGPROC)lpProc);\r
7694     FreeProcInstance(lpProc);\r
7695   }\r
7696 }\r
7697 #endif\r
7698 \r
7699 /*---------------------------------------------------------------------------*\\r
7700  *\r
7701  *  Ics Interaction console functions\r
7702  *\r
7703 \*---------------------------------------------------------------------------*/\r
7704 \r
7705 #define HISTORY_SIZE 64\r
7706 static char *history[HISTORY_SIZE];\r
7707 int histIn = 0, histP = 0;\r
7708 \r
7709 VOID\r
7710 SaveInHistory(char *cmd)\r
7711 {\r
7712   if (history[histIn] != NULL) {\r
7713     free(history[histIn]);\r
7714     history[histIn] = NULL;\r
7715   }\r
7716   if (*cmd == NULLCHAR) return;\r
7717   history[histIn] = StrSave(cmd);\r
7718   histIn = (histIn + 1) % HISTORY_SIZE;\r
7719   if (history[histIn] != NULL) {\r
7720     free(history[histIn]);\r
7721     history[histIn] = NULL;\r
7722   }\r
7723   histP = histIn;\r
7724 }\r
7725 \r
7726 char *\r
7727 PrevInHistory(char *cmd)\r
7728 {\r
7729   int newhp;\r
7730   if (histP == histIn) {\r
7731     if (history[histIn] != NULL) free(history[histIn]);\r
7732     history[histIn] = StrSave(cmd);\r
7733   }\r
7734   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7735   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7736   histP = newhp;\r
7737   return history[histP];\r
7738 }\r
7739 \r
7740 char *\r
7741 NextInHistory()\r
7742 {\r
7743   if (histP == histIn) return NULL;\r
7744   histP = (histP + 1) % HISTORY_SIZE;\r
7745   return history[histP];\r
7746 }\r
7747 \r
7748 typedef struct {\r
7749   char *item;\r
7750   char *command;\r
7751   BOOLEAN getname;\r
7752   BOOLEAN immediate;\r
7753 } IcsTextMenuEntry;\r
7754 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7755 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7756 \r
7757 void\r
7758 ParseIcsTextMenu(char *icsTextMenuString)\r
7759 {\r
7760 //  int flags = 0;\r
7761   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7762   char *p = icsTextMenuString;\r
7763   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7764     free(e->item);\r
7765     e->item = NULL;\r
7766     if (e->command != NULL) {\r
7767       free(e->command);\r
7768       e->command = NULL;\r
7769     }\r
7770     e++;\r
7771   }\r
7772   e = icsTextMenuEntry;\r
7773   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7774     if (*p == ';' || *p == '\n') {\r
7775       e->item = strdup("-");\r
7776       e->command = NULL;\r
7777       p++;\r
7778     } else if (*p == '-') {\r
7779       e->item = strdup("-");\r
7780       e->command = NULL;\r
7781       p++;\r
7782       if (*p) p++;\r
7783     } else {\r
7784       char *q, *r, *s, *t;\r
7785       char c;\r
7786       q = strchr(p, ',');\r
7787       if (q == NULL) break;\r
7788       *q = NULLCHAR;\r
7789       r = strchr(q + 1, ',');\r
7790       if (r == NULL) break;\r
7791       *r = NULLCHAR;\r
7792       s = strchr(r + 1, ',');\r
7793       if (s == NULL) break;\r
7794       *s = NULLCHAR;\r
7795       c = ';';\r
7796       t = strchr(s + 1, c);\r
7797       if (t == NULL) {\r
7798         c = '\n';\r
7799         t = strchr(s + 1, c);\r
7800       }\r
7801       if (t != NULL) *t = NULLCHAR;\r
7802       e->item = strdup(p);\r
7803       e->command = strdup(q + 1);\r
7804       e->getname = *(r + 1) != '0';\r
7805       e->immediate = *(s + 1) != '0';\r
7806       *q = ',';\r
7807       *r = ',';\r
7808       *s = ',';\r
7809       if (t == NULL) break;\r
7810       *t = c;\r
7811       p = t + 1;\r
7812     }\r
7813     e++;\r
7814   } \r
7815 }\r
7816 \r
7817 HMENU\r
7818 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7819 {\r
7820   HMENU hmenu, h;\r
7821   int i = 0;\r
7822   hmenu = LoadMenu(hInst, "TextMenu");\r
7823   h = GetSubMenu(hmenu, 0);\r
7824   while (e->item) {\r
7825     if (strcmp(e->item, "-") == 0) {\r
7826       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7827     } else {\r
7828       if (e->item[0] == '|') {\r
7829         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7830                    IDM_CommandX + i, &e->item[1]);\r
7831       } else {\r
7832         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7833       }\r
7834     }\r
7835     e++;\r
7836     i++;\r
7837   } \r
7838   return hmenu;\r
7839 }\r
7840 \r
7841 WNDPROC consoleTextWindowProc;\r
7842 \r
7843 void\r
7844 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7845 {\r
7846   char buf[MSG_SIZ], name[MSG_SIZ];\r
7847   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7848   CHARRANGE sel;\r
7849 \r
7850   if (!getname) {\r
7851     SetWindowText(hInput, command);\r
7852     if (immediate) {\r
7853       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7854     } else {\r
7855       sel.cpMin = 999999;\r
7856       sel.cpMax = 999999;\r
7857       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7858       SetFocus(hInput);\r
7859     }\r
7860     return;\r
7861   }    \r
7862   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7863   if (sel.cpMin == sel.cpMax) {\r
7864     /* Expand to surrounding word */\r
7865     TEXTRANGE tr;\r
7866     do {\r
7867       tr.chrg.cpMax = sel.cpMin;\r
7868       tr.chrg.cpMin = --sel.cpMin;\r
7869       if (sel.cpMin < 0) break;\r
7870       tr.lpstrText = name;\r
7871       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7872     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7873     sel.cpMin++;\r
7874 \r
7875     do {\r
7876       tr.chrg.cpMin = sel.cpMax;\r
7877       tr.chrg.cpMax = ++sel.cpMax;\r
7878       tr.lpstrText = name;\r
7879       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7880     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7881     sel.cpMax--;\r
7882 \r
7883     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7884       MessageBeep(MB_ICONEXCLAMATION);\r
7885       return;\r
7886     }\r
7887     tr.chrg = sel;\r
7888     tr.lpstrText = name;\r
7889     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7890   } else {\r
7891     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7892       MessageBeep(MB_ICONEXCLAMATION);\r
7893       return;\r
7894     }\r
7895     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7896   }\r
7897   if (immediate) {\r
7898     sprintf(buf, "%s %s", command, name);\r
7899     SetWindowText(hInput, buf);\r
7900     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7901   } else {\r
7902     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7903     SetWindowText(hInput, buf);\r
7904     sel.cpMin = 999999;\r
7905     sel.cpMax = 999999;\r
7906     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7907     SetFocus(hInput);\r
7908   }\r
7909 }\r
7910 \r
7911 LRESULT CALLBACK \r
7912 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7913 {\r
7914   HWND hInput;\r
7915   CHARRANGE sel;\r
7916 \r
7917   switch (message) {\r
7918   case WM_KEYDOWN:\r
7919     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7920     switch (wParam) {\r
7921     case VK_PRIOR:\r
7922       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7923       return 0;\r
7924     case VK_NEXT:\r
7925       sel.cpMin = 999999;\r
7926       sel.cpMax = 999999;\r
7927       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7928       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7929       return 0;\r
7930     }\r
7931     break;\r
7932   case WM_CHAR:\r
7933    if(wParam != '\022') {\r
7934     if (wParam == '\t') {\r
7935       if (GetKeyState(VK_SHIFT) < 0) {\r
7936         /* shifted */\r
7937         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7938         if (buttonDesc[0].hwnd) {\r
7939           SetFocus(buttonDesc[0].hwnd);\r
7940         } else {\r
7941           SetFocus(hwndMain);\r
7942         }\r
7943       } else {\r
7944         /* unshifted */\r
7945         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7946       }\r
7947     } else {\r
7948       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7949       JAWS_DELETE( SetFocus(hInput); )\r
7950       SendMessage(hInput, message, wParam, lParam);\r
7951     }\r
7952     return 0;\r
7953    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7954   case WM_RBUTTONUP:\r
7955     if (GetKeyState(VK_SHIFT) & ~1) {\r
7956       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7957         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7958     } else {\r
7959       POINT pt;\r
7960       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7961       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7962       if (sel.cpMin == sel.cpMax) {\r
7963         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7964         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7965       }\r
7966       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7967         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7968       }\r
7969       pt.x = LOWORD(lParam);\r
7970       pt.y = HIWORD(lParam);\r
7971       MenuPopup(hwnd, pt, hmenu, -1);\r
7972     }\r
7973     return 0;\r
7974   case WM_PASTE:\r
7975     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7976     SetFocus(hInput);\r
7977     return SendMessage(hInput, message, wParam, lParam);\r
7978   case WM_MBUTTONDOWN:\r
7979     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7980   case WM_RBUTTONDOWN:\r
7981     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7982       /* Move selection here if it was empty */\r
7983       POINT pt;\r
7984       pt.x = LOWORD(lParam);\r
7985       pt.y = HIWORD(lParam);\r
7986       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7987       if (sel.cpMin == sel.cpMax) {\r
7988         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7989         sel.cpMax = sel.cpMin;\r
7990         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7991       }\r
7992       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7993     }\r
7994     return 0;\r
7995   case WM_COMMAND:\r
7996     switch (LOWORD(wParam)) {\r
7997     case IDM_QuickPaste:\r
7998       {\r
7999         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8000         if (sel.cpMin == sel.cpMax) {\r
8001           MessageBeep(MB_ICONEXCLAMATION);\r
8002           return 0;\r
8003         }\r
8004         SendMessage(hwnd, WM_COPY, 0, 0);\r
8005         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8006         SendMessage(hInput, WM_PASTE, 0, 0);\r
8007         SetFocus(hInput);\r
8008         return 0;\r
8009       }\r
8010     case IDM_Cut:\r
8011       SendMessage(hwnd, WM_CUT, 0, 0);\r
8012       return 0;\r
8013     case IDM_Paste:\r
8014       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8015       return 0;\r
8016     case IDM_Copy:\r
8017       SendMessage(hwnd, WM_COPY, 0, 0);\r
8018       return 0;\r
8019     default:\r
8020       {\r
8021         int i = LOWORD(wParam) - IDM_CommandX;\r
8022         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8023             icsTextMenuEntry[i].command != NULL) {\r
8024           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8025                    icsTextMenuEntry[i].getname,\r
8026                    icsTextMenuEntry[i].immediate);\r
8027           return 0;\r
8028         }\r
8029       }\r
8030       break;\r
8031     }\r
8032     break;\r
8033   }\r
8034   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8035 }\r
8036 \r
8037 WNDPROC consoleInputWindowProc;\r
8038 \r
8039 LRESULT CALLBACK\r
8040 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8041 {\r
8042   char buf[MSG_SIZ];\r
8043   char *p;\r
8044   static BOOL sendNextChar = FALSE;\r
8045   static BOOL quoteNextChar = FALSE;\r
8046   InputSource *is = consoleInputSource;\r
8047   CHARFORMAT cf;\r
8048   CHARRANGE sel;\r
8049 \r
8050   switch (message) {\r
8051   case WM_CHAR:\r
8052     if (!appData.localLineEditing || sendNextChar) {\r
8053       is->buf[0] = (CHAR) wParam;\r
8054       is->count = 1;\r
8055       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8056       sendNextChar = FALSE;\r
8057       return 0;\r
8058     }\r
8059     if (quoteNextChar) {\r
8060       buf[0] = (char) wParam;\r
8061       buf[1] = NULLCHAR;\r
8062       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8063       quoteNextChar = FALSE;\r
8064       return 0;\r
8065     }\r
8066     switch (wParam) {\r
8067     case '\r':   /* Enter key */\r
8068       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8069       if (consoleEcho) SaveInHistory(is->buf);\r
8070       is->buf[is->count++] = '\n';\r
8071       is->buf[is->count] = NULLCHAR;\r
8072       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8073       if (consoleEcho) {\r
8074         ConsoleOutput(is->buf, is->count, TRUE);\r
8075       } else if (appData.localLineEditing) {\r
8076         ConsoleOutput("\n", 1, TRUE);\r
8077       }\r
8078       /* fall thru */\r
8079     case '\033': /* Escape key */\r
8080       SetWindowText(hwnd, "");\r
8081       cf.cbSize = sizeof(CHARFORMAT);\r
8082       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8083       if (consoleEcho) {\r
8084         cf.crTextColor = textAttribs[ColorNormal].color;\r
8085       } else {\r
8086         cf.crTextColor = COLOR_ECHOOFF;\r
8087       }\r
8088       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8089       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8090       return 0;\r
8091     case '\t':   /* Tab key */\r
8092       if (GetKeyState(VK_SHIFT) < 0) {\r
8093         /* shifted */\r
8094         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8095       } else {\r
8096         /* unshifted */\r
8097         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8098         if (buttonDesc[0].hwnd) {\r
8099           SetFocus(buttonDesc[0].hwnd);\r
8100         } else {\r
8101           SetFocus(hwndMain);\r
8102         }\r
8103       }\r
8104       return 0;\r
8105     case '\023': /* Ctrl+S */\r
8106       sendNextChar = TRUE;\r
8107       return 0;\r
8108     case '\021': /* Ctrl+Q */\r
8109       quoteNextChar = TRUE;\r
8110       return 0;\r
8111     JAWS_REPLAY\r
8112     default:\r
8113       break;\r
8114     }\r
8115     break;\r
8116   case WM_KEYDOWN:\r
8117     switch (wParam) {\r
8118     case VK_UP:\r
8119       GetWindowText(hwnd, buf, MSG_SIZ);\r
8120       p = PrevInHistory(buf);\r
8121       if (p != NULL) {\r
8122         SetWindowText(hwnd, p);\r
8123         sel.cpMin = 999999;\r
8124         sel.cpMax = 999999;\r
8125         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8126         return 0;\r
8127       }\r
8128       break;\r
8129     case VK_DOWN:\r
8130       p = NextInHistory();\r
8131       if (p != NULL) {\r
8132         SetWindowText(hwnd, p);\r
8133         sel.cpMin = 999999;\r
8134         sel.cpMax = 999999;\r
8135         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8136         return 0;\r
8137       }\r
8138       break;\r
8139     case VK_HOME:\r
8140     case VK_END:\r
8141       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8142       /* fall thru */\r
8143     case VK_PRIOR:\r
8144     case VK_NEXT:\r
8145       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8146       return 0;\r
8147     }\r
8148     break;\r
8149   case WM_MBUTTONDOWN:\r
8150     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8151       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8152     break;\r
8153   case WM_RBUTTONUP:\r
8154     if (GetKeyState(VK_SHIFT) & ~1) {\r
8155       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8156         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8157     } else {\r
8158       POINT pt;\r
8159       HMENU hmenu;\r
8160       hmenu = LoadMenu(hInst, "InputMenu");\r
8161       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8162       if (sel.cpMin == sel.cpMax) {\r
8163         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8164         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8165       }\r
8166       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8167         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8168       }\r
8169       pt.x = LOWORD(lParam);\r
8170       pt.y = HIWORD(lParam);\r
8171       MenuPopup(hwnd, pt, hmenu, -1);\r
8172     }\r
8173     return 0;\r
8174   case WM_COMMAND:\r
8175     switch (LOWORD(wParam)) { \r
8176     case IDM_Undo:\r
8177       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8178       return 0;\r
8179     case IDM_SelectAll:\r
8180       sel.cpMin = 0;\r
8181       sel.cpMax = -1; /*999999?*/\r
8182       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8183       return 0;\r
8184     case IDM_Cut:\r
8185       SendMessage(hwnd, WM_CUT, 0, 0);\r
8186       return 0;\r
8187     case IDM_Paste:\r
8188       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8189       return 0;\r
8190     case IDM_Copy:\r
8191       SendMessage(hwnd, WM_COPY, 0, 0);\r
8192       return 0;\r
8193     }\r
8194     break;\r
8195   }\r
8196   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8197 }\r
8198 \r
8199 #define CO_MAX  100000\r
8200 #define CO_TRIM   1000\r
8201 \r
8202 LRESULT CALLBACK\r
8203 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8204 {\r
8205   static SnapData sd;\r
8206   static HWND hText, hInput /*, hFocus*/;\r
8207 //  InputSource *is = consoleInputSource;\r
8208   RECT rect;\r
8209   static int sizeX, sizeY;\r
8210   int newSizeX, newSizeY;\r
8211   MINMAXINFO *mmi;\r
8212 \r
8213   switch (message) {\r
8214   case WM_INITDIALOG: /* message: initialize dialog box */\r
8215     hwndConsole = hDlg;\r
8216     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8217     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8218     SetFocus(hInput);\r
8219     consoleTextWindowProc = (WNDPROC)\r
8220       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8221     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8222     consoleInputWindowProc = (WNDPROC)\r
8223       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8224     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8225     Colorize(ColorNormal, TRUE);\r
8226     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8227     ChangedConsoleFont();\r
8228     GetClientRect(hDlg, &rect);\r
8229     sizeX = rect.right;\r
8230     sizeY = rect.bottom;\r
8231     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8232         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8233       WINDOWPLACEMENT wp;\r
8234       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8235       wp.length = sizeof(WINDOWPLACEMENT);\r
8236       wp.flags = 0;\r
8237       wp.showCmd = SW_SHOW;\r
8238       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8239       wp.rcNormalPosition.left = wpConsole.x;\r
8240       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8241       wp.rcNormalPosition.top = wpConsole.y;\r
8242       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8243       SetWindowPlacement(hDlg, &wp);\r
8244     }\r
8245 #if 1\r
8246    // [HGM] Chessknight's change 2004-07-13\r
8247    else { /* Determine Defaults */\r
8248        WINDOWPLACEMENT wp;\r
8249        wpConsole.x = winWidth + 1;\r
8250        wpConsole.y = boardY;\r
8251        wpConsole.width = screenWidth -  winWidth;\r
8252        wpConsole.height = winHeight;\r
8253        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8254        wp.length = sizeof(WINDOWPLACEMENT);\r
8255        wp.flags = 0;\r
8256        wp.showCmd = SW_SHOW;\r
8257        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8258        wp.rcNormalPosition.left = wpConsole.x;\r
8259        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8260        wp.rcNormalPosition.top = wpConsole.y;\r
8261        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8262        SetWindowPlacement(hDlg, &wp);\r
8263     }\r
8264 #endif\r
8265     return FALSE;\r
8266 \r
8267   case WM_SETFOCUS:\r
8268     SetFocus(hInput);\r
8269     return 0;\r
8270 \r
8271   case WM_CLOSE:\r
8272     ExitEvent(0);\r
8273     /* not reached */\r
8274     break;\r
8275 \r
8276   case WM_SIZE:\r
8277     if (IsIconic(hDlg)) break;\r
8278     newSizeX = LOWORD(lParam);\r
8279     newSizeY = HIWORD(lParam);\r
8280     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8281       RECT rectText, rectInput;\r
8282       POINT pt;\r
8283       int newTextHeight, newTextWidth;\r
8284       GetWindowRect(hText, &rectText);\r
8285       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8286       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8287       if (newTextHeight < 0) {\r
8288         newSizeY += -newTextHeight;\r
8289         newTextHeight = 0;\r
8290       }\r
8291       SetWindowPos(hText, NULL, 0, 0,\r
8292         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8293       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8294       pt.x = rectInput.left;\r
8295       pt.y = rectInput.top + newSizeY - sizeY;\r
8296       ScreenToClient(hDlg, &pt);\r
8297       SetWindowPos(hInput, NULL, \r
8298         pt.x, pt.y, /* needs client coords */   \r
8299         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8300         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8301     }\r
8302     sizeX = newSizeX;\r
8303     sizeY = newSizeY;\r
8304     break;\r
8305 \r
8306   case WM_GETMINMAXINFO:\r
8307     /* Prevent resizing window too small */\r
8308     mmi = (MINMAXINFO *) lParam;\r
8309     mmi->ptMinTrackSize.x = 100;\r
8310     mmi->ptMinTrackSize.y = 100;\r
8311     break;\r
8312 \r
8313   /* [AS] Snapping */\r
8314   case WM_ENTERSIZEMOVE:\r
8315     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8316 \r
8317   case WM_SIZING:\r
8318     return OnSizing( &sd, hDlg, wParam, lParam );\r
8319 \r
8320   case WM_MOVING:\r
8321     return OnMoving( &sd, hDlg, wParam, lParam );\r
8322 \r
8323   case WM_EXITSIZEMOVE:\r
8324     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8325   }\r
8326 \r
8327   return DefWindowProc(hDlg, message, wParam, lParam);\r
8328 }\r
8329 \r
8330 \r
8331 VOID\r
8332 ConsoleCreate()\r
8333 {\r
8334   HWND hCons;\r
8335   if (hwndConsole) return;\r
8336   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8337   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8338 }\r
8339 \r
8340 \r
8341 VOID\r
8342 ConsoleOutput(char* data, int length, int forceVisible)\r
8343 {\r
8344   HWND hText;\r
8345   int trim, exlen;\r
8346   char *p, *q;\r
8347   char buf[CO_MAX+1];\r
8348   POINT pEnd;\r
8349   RECT rect;\r
8350   static int delayLF = 0;\r
8351   CHARRANGE savesel, sel;\r
8352 \r
8353   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8354   p = data;\r
8355   q = buf;\r
8356   if (delayLF) {\r
8357     *q++ = '\r';\r
8358     *q++ = '\n';\r
8359     delayLF = 0;\r
8360   }\r
8361   while (length--) {\r
8362     if (*p == '\n') {\r
8363       if (*++p) {\r
8364         *q++ = '\r';\r
8365         *q++ = '\n';\r
8366       } else {\r
8367         delayLF = 1;\r
8368       }\r
8369     } else if (*p == '\007') {\r
8370        MyPlaySound(&sounds[(int)SoundBell]);\r
8371        p++;\r
8372     } else {\r
8373       *q++ = *p++;\r
8374     }\r
8375   }\r
8376   *q = NULLCHAR;\r
8377   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8378   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8379   /* Save current selection */\r
8380   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8381   exlen = GetWindowTextLength(hText);\r
8382   /* Find out whether current end of text is visible */\r
8383   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8384   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8385   /* Trim existing text if it's too long */\r
8386   if (exlen + (q - buf) > CO_MAX) {\r
8387     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8388     sel.cpMin = 0;\r
8389     sel.cpMax = trim;\r
8390     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8391     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8392     exlen -= trim;\r
8393     savesel.cpMin -= trim;\r
8394     savesel.cpMax -= trim;\r
8395     if (exlen < 0) exlen = 0;\r
8396     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8397     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8398   }\r
8399   /* Append the new text */\r
8400   sel.cpMin = exlen;\r
8401   sel.cpMax = exlen;\r
8402   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8403   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8404   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8405   if (forceVisible || exlen == 0 ||\r
8406       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8407        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8408     /* Scroll to make new end of text visible if old end of text\r
8409        was visible or new text is an echo of user typein */\r
8410     sel.cpMin = 9999999;\r
8411     sel.cpMax = 9999999;\r
8412     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8413     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8414     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8415     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8416   }\r
8417   if (savesel.cpMax == exlen || forceVisible) {\r
8418     /* Move insert point to new end of text if it was at the old\r
8419        end of text or if the new text is an echo of user typein */\r
8420     sel.cpMin = 9999999;\r
8421     sel.cpMax = 9999999;\r
8422     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8423   } else {\r
8424     /* Restore previous selection */\r
8425     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8426   }\r
8427   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8428 }\r
8429 \r
8430 /*---------*/\r
8431 \r
8432 \r
8433 void\r
8434 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8435 {\r
8436   char buf[100];\r
8437   char *str;\r
8438   COLORREF oldFg, oldBg;\r
8439   HFONT oldFont;\r
8440   RECT rect;\r
8441 \r
8442   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8443 \r
8444   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8445   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8446   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8447 \r
8448   rect.left = x;\r
8449   rect.right = x + squareSize;\r
8450   rect.top  = y;\r
8451   rect.bottom = y + squareSize;\r
8452   str = buf;\r
8453 \r
8454   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8455                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8456              y, ETO_CLIPPED|ETO_OPAQUE,\r
8457              &rect, str, strlen(str), NULL);\r
8458 \r
8459   (void) SetTextColor(hdc, oldFg);\r
8460   (void) SetBkColor(hdc, oldBg);\r
8461   (void) SelectObject(hdc, oldFont);\r
8462 }\r
8463 \r
8464 void\r
8465 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8466               RECT *rect, char *color, char *flagFell)\r
8467 {\r
8468   char buf[100];\r
8469   char *str;\r
8470   COLORREF oldFg, oldBg;\r
8471   HFONT oldFont;\r
8472 \r
8473   if (appData.clockMode) {\r
8474     if (tinyLayout)\r
8475       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8476     else\r
8477       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8478     str = buf;\r
8479   } else {\r
8480     str = color;\r
8481   }\r
8482 \r
8483   if (highlight) {\r
8484     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8485     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8486   } else {\r
8487     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8488     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8489   }\r
8490   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8491 \r
8492   JAWS_SILENCE\r
8493 \r
8494   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8495              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8496              rect, str, strlen(str), NULL);\r
8497   if(logoHeight > 0 && appData.clockMode) {\r
8498       RECT r;\r
8499       sprintf(buf, "%s %s", buf+7, flagFell);\r
8500       r.top = rect->top + logoHeight/2;\r
8501       r.left = rect->left;\r
8502       r.right = rect->right;\r
8503       r.bottom = rect->bottom;\r
8504       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8505                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8506                  &r, str, strlen(str), NULL);\r
8507   }\r
8508   (void) SetTextColor(hdc, oldFg);\r
8509   (void) SetBkColor(hdc, oldBg);\r
8510   (void) SelectObject(hdc, oldFont);\r
8511 }\r
8512 \r
8513 \r
8514 int\r
8515 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8516            OVERLAPPED *ovl)\r
8517 {\r
8518   int ok, err;\r
8519 \r
8520   /* [AS]  */\r
8521   if( count <= 0 ) {\r
8522     if (appData.debugMode) {\r
8523       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8524     }\r
8525 \r
8526     return ERROR_INVALID_USER_BUFFER;\r
8527   }\r
8528 \r
8529   ResetEvent(ovl->hEvent);\r
8530   ovl->Offset = ovl->OffsetHigh = 0;\r
8531   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8532   if (ok) {\r
8533     err = NO_ERROR;\r
8534   } else {\r
8535     err = GetLastError();\r
8536     if (err == ERROR_IO_PENDING) {\r
8537       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8538       if (ok)\r
8539         err = NO_ERROR;\r
8540       else\r
8541         err = GetLastError();\r
8542     }\r
8543   }\r
8544   return err;\r
8545 }\r
8546 \r
8547 int\r
8548 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8549             OVERLAPPED *ovl)\r
8550 {\r
8551   int ok, err;\r
8552 \r
8553   ResetEvent(ovl->hEvent);\r
8554   ovl->Offset = ovl->OffsetHigh = 0;\r
8555   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8556   if (ok) {\r
8557     err = NO_ERROR;\r
8558   } else {\r
8559     err = GetLastError();\r
8560     if (err == ERROR_IO_PENDING) {\r
8561       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8562       if (ok)\r
8563         err = NO_ERROR;\r
8564       else\r
8565         err = GetLastError();\r
8566     }\r
8567   }\r
8568   return err;\r
8569 }\r
8570 \r
8571 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8572 void CheckForInputBufferFull( InputSource * is )\r
8573 {\r
8574     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8575         /* Look for end of line */\r
8576         char * p = is->buf;\r
8577         \r
8578         while( p < is->next && *p != '\n' ) {\r
8579             p++;\r
8580         }\r
8581 \r
8582         if( p >= is->next ) {\r
8583             if (appData.debugMode) {\r
8584                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8585             }\r
8586 \r
8587             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8588             is->count = (DWORD) -1;\r
8589             is->next = is->buf;\r
8590         }\r
8591     }\r
8592 }\r
8593 \r
8594 DWORD\r
8595 InputThread(LPVOID arg)\r
8596 {\r
8597   InputSource *is;\r
8598   OVERLAPPED ovl;\r
8599 \r
8600   is = (InputSource *) arg;\r
8601   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8602   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8603   while (is->hThread != NULL) {\r
8604     is->error = DoReadFile(is->hFile, is->next,\r
8605                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8606                            &is->count, &ovl);\r
8607     if (is->error == NO_ERROR) {\r
8608       is->next += is->count;\r
8609     } else {\r
8610       if (is->error == ERROR_BROKEN_PIPE) {\r
8611         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8612         is->count = 0;\r
8613       } else {\r
8614         is->count = (DWORD) -1;\r
8615         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8616         break; \r
8617       }\r
8618     }\r
8619 \r
8620     CheckForInputBufferFull( is );\r
8621 \r
8622     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8623 \r
8624     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8625 \r
8626     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8627   }\r
8628 \r
8629   CloseHandle(ovl.hEvent);\r
8630   CloseHandle(is->hFile);\r
8631 \r
8632   if (appData.debugMode) {\r
8633     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8634   }\r
8635 \r
8636   return 0;\r
8637 }\r
8638 \r
8639 \r
8640 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8641 DWORD\r
8642 NonOvlInputThread(LPVOID arg)\r
8643 {\r
8644   InputSource *is;\r
8645   char *p, *q;\r
8646   int i;\r
8647   char prev;\r
8648 \r
8649   is = (InputSource *) arg;\r
8650   while (is->hThread != NULL) {\r
8651     is->error = ReadFile(is->hFile, is->next,\r
8652                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8653                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8654     if (is->error == NO_ERROR) {\r
8655       /* Change CRLF to LF */\r
8656       if (is->next > is->buf) {\r
8657         p = is->next - 1;\r
8658         i = is->count + 1;\r
8659       } else {\r
8660         p = is->next;\r
8661         i = is->count;\r
8662       }\r
8663       q = p;\r
8664       prev = NULLCHAR;\r
8665       while (i > 0) {\r
8666         if (prev == '\r' && *p == '\n') {\r
8667           *(q-1) = '\n';\r
8668           is->count--;\r
8669         } else { \r
8670           *q++ = *p;\r
8671         }\r
8672         prev = *p++;\r
8673         i--;\r
8674       }\r
8675       *q = NULLCHAR;\r
8676       is->next = q;\r
8677     } else {\r
8678       if (is->error == ERROR_BROKEN_PIPE) {\r
8679         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8680         is->count = 0; \r
8681       } else {\r
8682         is->count = (DWORD) -1;\r
8683       }\r
8684     }\r
8685 \r
8686     CheckForInputBufferFull( is );\r
8687 \r
8688     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8689 \r
8690     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8691 \r
8692     if (is->count < 0) break;  /* Quit on error */\r
8693   }\r
8694   CloseHandle(is->hFile);\r
8695   return 0;\r
8696 }\r
8697 \r
8698 DWORD\r
8699 SocketInputThread(LPVOID arg)\r
8700 {\r
8701   InputSource *is;\r
8702 \r
8703   is = (InputSource *) arg;\r
8704   while (is->hThread != NULL) {\r
8705     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8706     if ((int)is->count == SOCKET_ERROR) {\r
8707       is->count = (DWORD) -1;\r
8708       is->error = WSAGetLastError();\r
8709     } else {\r
8710       is->error = NO_ERROR;\r
8711       is->next += is->count;\r
8712       if (is->count == 0 && is->second == is) {\r
8713         /* End of file on stderr; quit with no message */\r
8714         break;\r
8715       }\r
8716     }\r
8717     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8718 \r
8719     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8720 \r
8721     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8722   }\r
8723   return 0;\r
8724 }\r
8725 \r
8726 VOID\r
8727 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8728 {\r
8729   InputSource *is;\r
8730 \r
8731   is = (InputSource *) lParam;\r
8732   if (is->lineByLine) {\r
8733     /* Feed in lines one by one */\r
8734     char *p = is->buf;\r
8735     char *q = p;\r
8736     while (q < is->next) {\r
8737       if (*q++ == '\n') {\r
8738         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8739         p = q;\r
8740       }\r
8741     }\r
8742     \r
8743     /* Move any partial line to the start of the buffer */\r
8744     q = is->buf;\r
8745     while (p < is->next) {\r
8746       *q++ = *p++;\r
8747     }\r
8748     is->next = q;\r
8749 \r
8750     if (is->error != NO_ERROR || is->count == 0) {\r
8751       /* Notify backend of the error.  Note: If there was a partial\r
8752          line at the end, it is not flushed through. */\r
8753       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8754     }\r
8755   } else {\r
8756     /* Feed in the whole chunk of input at once */\r
8757     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8758     is->next = is->buf;\r
8759   }\r
8760 }\r
8761 \r
8762 /*---------------------------------------------------------------------------*\\r
8763  *\r
8764  *  Menu enables. Used when setting various modes.\r
8765  *\r
8766 \*---------------------------------------------------------------------------*/\r
8767 \r
8768 typedef struct {\r
8769   int item;\r
8770   int flags;\r
8771 } Enables;\r
8772 \r
8773 VOID\r
8774 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8775 {\r
8776   while (enab->item > 0) {\r
8777     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8778     enab++;\r
8779   }\r
8780 }\r
8781 \r
8782 Enables gnuEnables[] = {\r
8783   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8793   { -1, -1 }\r
8794 };\r
8795 \r
8796 Enables icsEnables[] = {\r
8797   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8800   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8802   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8803   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8805   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8806   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8807   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8808   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8809   { -1, -1 }\r
8810 };\r
8811 \r
8812 #ifdef ZIPPY\r
8813 Enables zippyEnables[] = {\r
8814   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8815   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8816   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8817   { -1, -1 }\r
8818 };\r
8819 #endif\r
8820 \r
8821 Enables ncpEnables[] = {\r
8822   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8830   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8831   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8837   { -1, -1 }\r
8838 };\r
8839 \r
8840 Enables trainingOnEnables[] = {\r
8841   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8843   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8844   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8845   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8846   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8847   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8848   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8849   { -1, -1 }\r
8850 };\r
8851 \r
8852 Enables trainingOffEnables[] = {\r
8853   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8854   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8855   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8856   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8857   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8858   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8860   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8861   { -1, -1 }\r
8862 };\r
8863 \r
8864 /* These modify either ncpEnables or gnuEnables */\r
8865 Enables cmailEnables[] = {\r
8866   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8867   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8868   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8869   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8871   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8873   { -1, -1 }\r
8874 };\r
8875 \r
8876 Enables machineThinkingEnables[] = {\r
8877   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8881   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8882   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8883   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8884   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8885   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8886   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8887   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8888   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8889   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8890   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8891   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8892   { -1, -1 }\r
8893 };\r
8894 \r
8895 Enables userThinkingEnables[] = {\r
8896   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8900   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8901   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8902   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8903   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8904   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8905   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8906   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8907   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8908   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8909   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8910   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8911   { -1, -1 }\r
8912 };\r
8913 \r
8914 /*---------------------------------------------------------------------------*\\r
8915  *\r
8916  *  Front-end interface functions exported by XBoard.\r
8917  *  Functions appear in same order as prototypes in frontend.h.\r
8918  * \r
8919 \*---------------------------------------------------------------------------*/\r
8920 VOID\r
8921 ModeHighlight()\r
8922 {\r
8923   static UINT prevChecked = 0;\r
8924   static int prevPausing = 0;\r
8925   UINT nowChecked;\r
8926 \r
8927   if (pausing != prevPausing) {\r
8928     prevPausing = pausing;\r
8929     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8930                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8931     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8932   }\r
8933 \r
8934   switch (gameMode) {\r
8935   case BeginningOfGame:\r
8936     if (appData.icsActive)\r
8937       nowChecked = IDM_IcsClient;\r
8938     else if (appData.noChessProgram)\r
8939       nowChecked = IDM_EditGame;\r
8940     else\r
8941       nowChecked = IDM_MachineBlack;\r
8942     break;\r
8943   case MachinePlaysBlack:\r
8944     nowChecked = IDM_MachineBlack;\r
8945     break;\r
8946   case MachinePlaysWhite:\r
8947     nowChecked = IDM_MachineWhite;\r
8948     break;\r
8949   case TwoMachinesPlay:\r
8950     nowChecked = IDM_TwoMachines;\r
8951     break;\r
8952   case AnalyzeMode:\r
8953     nowChecked = IDM_AnalysisMode;\r
8954     break;\r
8955   case AnalyzeFile:\r
8956     nowChecked = IDM_AnalyzeFile;\r
8957     break;\r
8958   case EditGame:\r
8959     nowChecked = IDM_EditGame;\r
8960     break;\r
8961   case PlayFromGameFile:\r
8962     nowChecked = IDM_LoadGame;\r
8963     break;\r
8964   case EditPosition:\r
8965     nowChecked = IDM_EditPosition;\r
8966     break;\r
8967   case Training:\r
8968     nowChecked = IDM_Training;\r
8969     break;\r
8970   case IcsPlayingWhite:\r
8971   case IcsPlayingBlack:\r
8972   case IcsObserving:\r
8973   case IcsIdle:\r
8974     nowChecked = IDM_IcsClient;\r
8975     break;\r
8976   default:\r
8977   case EndOfGame:\r
8978     nowChecked = 0;\r
8979     break;\r
8980   }\r
8981   if (prevChecked != 0)\r
8982     (void) CheckMenuItem(GetMenu(hwndMain),\r
8983                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8984   if (nowChecked != 0)\r
8985     (void) CheckMenuItem(GetMenu(hwndMain),\r
8986                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8987 \r
8988   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8989     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8990                           MF_BYCOMMAND|MF_ENABLED);\r
8991   } else {\r
8992     (void) EnableMenuItem(GetMenu(hwndMain), \r
8993                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8994   }\r
8995 \r
8996   prevChecked = nowChecked;\r
8997 \r
8998   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8999   if (appData.icsActive) {\r
9000        if (appData.icsEngineAnalyze) {\r
9001                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9002                        MF_BYCOMMAND|MF_CHECKED);\r
9003        } else {\r
9004                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9005                        MF_BYCOMMAND|MF_UNCHECKED);\r
9006        }\r
9007   }\r
9008 }\r
9009 \r
9010 VOID\r
9011 SetICSMode()\r
9012 {\r
9013   HMENU hmenu = GetMenu(hwndMain);\r
9014   SetMenuEnables(hmenu, icsEnables);\r
9015   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9016     MF_BYPOSITION|MF_ENABLED);\r
9017 #ifdef ZIPPY\r
9018   if (appData.zippyPlay) {\r
9019     SetMenuEnables(hmenu, zippyEnables);\r
9020     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9021          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9022           MF_BYCOMMAND|MF_ENABLED);\r
9023   }\r
9024 #endif\r
9025 }\r
9026 \r
9027 VOID\r
9028 SetGNUMode()\r
9029 {\r
9030   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9031 }\r
9032 \r
9033 VOID\r
9034 SetNCPMode()\r
9035 {\r
9036   HMENU hmenu = GetMenu(hwndMain);\r
9037   SetMenuEnables(hmenu, ncpEnables);\r
9038   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9039     MF_BYPOSITION|MF_GRAYED);\r
9040     DrawMenuBar(hwndMain);\r
9041 }\r
9042 \r
9043 VOID\r
9044 SetCmailMode()\r
9045 {\r
9046   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9047 }\r
9048 \r
9049 VOID \r
9050 SetTrainingModeOn()\r
9051 {\r
9052   int i;\r
9053   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9054   for (i = 0; i < N_BUTTONS; i++) {\r
9055     if (buttonDesc[i].hwnd != NULL)\r
9056       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9057   }\r
9058   CommentPopDown();\r
9059 }\r
9060 \r
9061 VOID SetTrainingModeOff()\r
9062 {\r
9063   int i;\r
9064   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9065   for (i = 0; i < N_BUTTONS; i++) {\r
9066     if (buttonDesc[i].hwnd != NULL)\r
9067       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9068   }\r
9069 }\r
9070 \r
9071 \r
9072 VOID\r
9073 SetUserThinkingEnables()\r
9074 {\r
9075   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9076 }\r
9077 \r
9078 VOID\r
9079 SetMachineThinkingEnables()\r
9080 {\r
9081   HMENU hMenu = GetMenu(hwndMain);\r
9082   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9083 \r
9084   SetMenuEnables(hMenu, machineThinkingEnables);\r
9085 \r
9086   if (gameMode == MachinePlaysBlack) {\r
9087     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9088   } else if (gameMode == MachinePlaysWhite) {\r
9089     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9090   } else if (gameMode == TwoMachinesPlay) {\r
9091     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9092   }\r
9093 }\r
9094 \r
9095 \r
9096 VOID\r
9097 DisplayTitle(char *str)\r
9098 {\r
9099   char title[MSG_SIZ], *host;\r
9100   if (str[0] != NULLCHAR) {\r
9101     strcpy(title, str);\r
9102   } else if (appData.icsActive) {\r
9103     if (appData.icsCommPort[0] != NULLCHAR)\r
9104       host = "ICS";\r
9105     else \r
9106       host = appData.icsHost;\r
9107     sprintf(title, "%s: %s", szTitle, host);\r
9108   } else if (appData.noChessProgram) {\r
9109     strcpy(title, szTitle);\r
9110   } else {\r
9111     strcpy(title, szTitle);\r
9112     strcat(title, ": ");\r
9113     strcat(title, first.tidy);\r
9114   }\r
9115   SetWindowText(hwndMain, title);\r
9116 }\r
9117 \r
9118 \r
9119 VOID\r
9120 DisplayMessage(char *str1, char *str2)\r
9121 {\r
9122   HDC hdc;\r
9123   HFONT oldFont;\r
9124   int remain = MESSAGE_TEXT_MAX - 1;\r
9125   int len;\r
9126 \r
9127   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9128   messageText[0] = NULLCHAR;\r
9129   if (*str1) {\r
9130     len = strlen(str1);\r
9131     if (len > remain) len = remain;\r
9132     strncpy(messageText, str1, len);\r
9133     messageText[len] = NULLCHAR;\r
9134     remain -= len;\r
9135   }\r
9136   if (*str2 && remain >= 2) {\r
9137     if (*str1) {\r
9138       strcat(messageText, "  ");\r
9139       remain -= 2;\r
9140     }\r
9141     len = strlen(str2);\r
9142     if (len > remain) len = remain;\r
9143     strncat(messageText, str2, len);\r
9144   }\r
9145   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9146 \r
9147   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9148 \r
9149   SAYMACHINEMOVE();\r
9150 \r
9151   hdc = GetDC(hwndMain);\r
9152   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9153   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9154              &messageRect, messageText, strlen(messageText), NULL);\r
9155   (void) SelectObject(hdc, oldFont);\r
9156   (void) ReleaseDC(hwndMain, hdc);\r
9157 }\r
9158 \r
9159 VOID\r
9160 DisplayError(char *str, int error)\r
9161 {\r
9162   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9163   int len;\r
9164 \r
9165   if (error == 0) {\r
9166     strcpy(buf, str);\r
9167   } else {\r
9168     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9169                         NULL, error, LANG_NEUTRAL,\r
9170                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9171     if (len > 0) {\r
9172       sprintf(buf, "%s:\n%s", str, buf2);\r
9173     } else {\r
9174       ErrorMap *em = errmap;\r
9175       while (em->err != 0 && em->err != error) em++;\r
9176       if (em->err != 0) {\r
9177         sprintf(buf, "%s:\n%s", str, em->msg);\r
9178       } else {\r
9179         sprintf(buf, "%s:\nError code %d", str, error);\r
9180       }\r
9181     }\r
9182   }\r
9183   \r
9184   ErrorPopUp("Error", buf);\r
9185 }\r
9186 \r
9187 \r
9188 VOID\r
9189 DisplayMoveError(char *str)\r
9190 {\r
9191   fromX = fromY = -1;\r
9192   ClearHighlights();\r
9193   DrawPosition(FALSE, NULL);\r
9194   if (appData.popupMoveErrors) {\r
9195     ErrorPopUp("Error", str);\r
9196   } else {\r
9197     DisplayMessage(str, "");\r
9198     moveErrorMessageUp = TRUE;\r
9199   }\r
9200 }\r
9201 \r
9202 VOID\r
9203 DisplayFatalError(char *str, int error, int exitStatus)\r
9204 {\r
9205   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9206   int len;\r
9207   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9208 \r
9209   if (error != 0) {\r
9210     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9211                         NULL, error, LANG_NEUTRAL,\r
9212                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9213     if (len > 0) {\r
9214       sprintf(buf, "%s:\n%s", str, buf2);\r
9215     } else {\r
9216       ErrorMap *em = errmap;\r
9217       while (em->err != 0 && em->err != error) em++;\r
9218       if (em->err != 0) {\r
9219         sprintf(buf, "%s:\n%s", str, em->msg);\r
9220       } else {\r
9221         sprintf(buf, "%s:\nError code %d", str, error);\r
9222       }\r
9223     }\r
9224     str = buf;\r
9225   }\r
9226   if (appData.debugMode) {\r
9227     fprintf(debugFP, "%s: %s\n", label, str);\r
9228   }\r
9229   if (appData.popupExitMessage) {\r
9230     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9231                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9232   }\r
9233   ExitEvent(exitStatus);\r
9234 }\r
9235 \r
9236 \r
9237 VOID\r
9238 DisplayInformation(char *str)\r
9239 {\r
9240   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9241 }\r
9242 \r
9243 \r
9244 VOID\r
9245 DisplayNote(char *str)\r
9246 {\r
9247   ErrorPopUp("Note", str);\r
9248 }\r
9249 \r
9250 \r
9251 typedef struct {\r
9252   char *title, *question, *replyPrefix;\r
9253   ProcRef pr;\r
9254 } QuestionParams;\r
9255 \r
9256 LRESULT CALLBACK\r
9257 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9258 {\r
9259   static QuestionParams *qp;\r
9260   char reply[MSG_SIZ];\r
9261   int len, err;\r
9262 \r
9263   switch (message) {\r
9264   case WM_INITDIALOG:\r
9265     qp = (QuestionParams *) lParam;\r
9266     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9267     SetWindowText(hDlg, qp->title);\r
9268     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9269     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9270     return FALSE;\r
9271 \r
9272   case WM_COMMAND:\r
9273     switch (LOWORD(wParam)) {\r
9274     case IDOK:\r
9275       strcpy(reply, qp->replyPrefix);\r
9276       if (*reply) strcat(reply, " ");\r
9277       len = strlen(reply);\r
9278       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9279       strcat(reply, "\n");\r
9280       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9281       EndDialog(hDlg, TRUE);\r
9282       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9283       return TRUE;\r
9284     case IDCANCEL:\r
9285       EndDialog(hDlg, FALSE);\r
9286       return TRUE;\r
9287     default:\r
9288       break;\r
9289     }\r
9290     break;\r
9291   }\r
9292   return FALSE;\r
9293 }\r
9294 \r
9295 VOID\r
9296 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9297 {\r
9298     QuestionParams qp;\r
9299     FARPROC lpProc;\r
9300     \r
9301     qp.title = title;\r
9302     qp.question = question;\r
9303     qp.replyPrefix = replyPrefix;\r
9304     qp.pr = pr;\r
9305     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9306     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9307       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9308     FreeProcInstance(lpProc);\r
9309 }\r
9310 \r
9311 /* [AS] Pick FRC position */\r
9312 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9313 {\r
9314     static int * lpIndexFRC;\r
9315     BOOL index_is_ok;\r
9316     char buf[16];\r
9317 \r
9318     switch( message )\r
9319     {\r
9320     case WM_INITDIALOG:\r
9321         lpIndexFRC = (int *) lParam;\r
9322 \r
9323         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9324 \r
9325         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9326         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9327         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9328         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9329 \r
9330         break;\r
9331 \r
9332     case WM_COMMAND:\r
9333         switch( LOWORD(wParam) ) {\r
9334         case IDOK:\r
9335             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9336             EndDialog( hDlg, 0 );\r
9337             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9338             return TRUE;\r
9339         case IDCANCEL:\r
9340             EndDialog( hDlg, 1 );   \r
9341             return TRUE;\r
9342         case IDC_NFG_Edit:\r
9343             if( HIWORD(wParam) == EN_CHANGE ) {\r
9344                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9345 \r
9346                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9347             }\r
9348             return TRUE;\r
9349         case IDC_NFG_Random:\r
9350             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9351             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9352             return TRUE;\r
9353         }\r
9354 \r
9355         break;\r
9356     }\r
9357 \r
9358     return FALSE;\r
9359 }\r
9360 \r
9361 int NewGameFRC()\r
9362 {\r
9363     int result;\r
9364     int index = appData.defaultFrcPosition;\r
9365     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9366 \r
9367     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9368 \r
9369     if( result == 0 ) {\r
9370         appData.defaultFrcPosition = index;\r
9371     }\r
9372 \r
9373     return result;\r
9374 }\r
9375 \r
9376 /* [AS] Game list options */\r
9377 typedef struct {\r
9378     char id;\r
9379     char * name;\r
9380 } GLT_Item;\r
9381 \r
9382 static GLT_Item GLT_ItemInfo[] = {\r
9383     { GLT_EVENT,      "Event" },\r
9384     { GLT_SITE,       "Site" },\r
9385     { GLT_DATE,       "Date" },\r
9386     { GLT_ROUND,      "Round" },\r
9387     { GLT_PLAYERS,    "Players" },\r
9388     { GLT_RESULT,     "Result" },\r
9389     { GLT_WHITE_ELO,  "White Rating" },\r
9390     { GLT_BLACK_ELO,  "Black Rating" },\r
9391     { GLT_TIME_CONTROL,"Time Control" },\r
9392     { GLT_VARIANT,    "Variant" },\r
9393     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9394     { 0, 0 }\r
9395 };\r
9396 \r
9397 const char * GLT_FindItem( char id )\r
9398 {\r
9399     const char * result = 0;\r
9400 \r
9401     GLT_Item * list = GLT_ItemInfo;\r
9402 \r
9403     while( list->id != 0 ) {\r
9404         if( list->id == id ) {\r
9405             result = list->name;\r
9406             break;\r
9407         }\r
9408 \r
9409         list++;\r
9410     }\r
9411 \r
9412     return result;\r
9413 }\r
9414 \r
9415 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9416 {\r
9417     const char * name = GLT_FindItem( id );\r
9418 \r
9419     if( name != 0 ) {\r
9420         if( index >= 0 ) {\r
9421             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9422         }\r
9423         else {\r
9424             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9425         }\r
9426     }\r
9427 }\r
9428 \r
9429 void GLT_TagsToList( HWND hDlg, char * tags )\r
9430 {\r
9431     char * pc = tags;\r
9432 \r
9433     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9434 \r
9435     while( *pc ) {\r
9436         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9437         pc++;\r
9438     }\r
9439 \r
9440     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9441 \r
9442     pc = GLT_ALL_TAGS;\r
9443 \r
9444     while( *pc ) {\r
9445         if( strchr( tags, *pc ) == 0 ) {\r
9446             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9447         }\r
9448         pc++;\r
9449     }\r
9450 \r
9451     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9452 }\r
9453 \r
9454 char GLT_ListItemToTag( HWND hDlg, int index )\r
9455 {\r
9456     char result = '\0';\r
9457     char name[128];\r
9458 \r
9459     GLT_Item * list = GLT_ItemInfo;\r
9460 \r
9461     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9462         while( list->id != 0 ) {\r
9463             if( strcmp( list->name, name ) == 0 ) {\r
9464                 result = list->id;\r
9465                 break;\r
9466             }\r
9467 \r
9468             list++;\r
9469         }\r
9470     }\r
9471 \r
9472     return result;\r
9473 }\r
9474 \r
9475 void GLT_MoveSelection( HWND hDlg, int delta )\r
9476 {\r
9477     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9478     int idx2 = idx1 + delta;\r
9479     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9480 \r
9481     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9482         char buf[128];\r
9483 \r
9484         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9485         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9486         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9487         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9488     }\r
9489 }\r
9490 \r
9491 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9492 {\r
9493     static char glt[64];\r
9494     static char * lpUserGLT;\r
9495 \r
9496     switch( message )\r
9497     {\r
9498     case WM_INITDIALOG:\r
9499         lpUserGLT = (char *) lParam;\r
9500         \r
9501         strcpy( glt, lpUserGLT );\r
9502 \r
9503         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9504 \r
9505         /* Initialize list */\r
9506         GLT_TagsToList( hDlg, glt );\r
9507 \r
9508         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9509 \r
9510         break;\r
9511 \r
9512     case WM_COMMAND:\r
9513         switch( LOWORD(wParam) ) {\r
9514         case IDOK:\r
9515             {\r
9516                 char * pc = lpUserGLT;\r
9517                 int idx = 0;\r
9518 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9519                 char id;\r
9520 \r
9521                 do {\r
9522                     id = GLT_ListItemToTag( hDlg, idx );\r
9523 \r
9524                     *pc++ = id;\r
9525                     idx++;\r
9526                 } while( id != '\0' );\r
9527             }\r
9528             EndDialog( hDlg, 0 );\r
9529             return TRUE;\r
9530         case IDCANCEL:\r
9531             EndDialog( hDlg, 1 );\r
9532             return TRUE;\r
9533 \r
9534         case IDC_GLT_Default:\r
9535             strcpy( glt, GLT_DEFAULT_TAGS );\r
9536             GLT_TagsToList( hDlg, glt );\r
9537             return TRUE;\r
9538 \r
9539         case IDC_GLT_Restore:\r
9540             strcpy( glt, lpUserGLT );\r
9541             GLT_TagsToList( hDlg, glt );\r
9542             return TRUE;\r
9543 \r
9544         case IDC_GLT_Up:\r
9545             GLT_MoveSelection( hDlg, -1 );\r
9546             return TRUE;\r
9547 \r
9548         case IDC_GLT_Down:\r
9549             GLT_MoveSelection( hDlg, +1 );\r
9550             return TRUE;\r
9551         }\r
9552 \r
9553         break;\r
9554     }\r
9555 \r
9556     return FALSE;\r
9557 }\r
9558 \r
9559 int GameListOptions()\r
9560 {\r
9561     char glt[64];\r
9562     int result;\r
9563     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9564 \r
9565     strcpy( glt, appData.gameListTags );\r
9566 \r
9567     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9568 \r
9569     if( result == 0 ) {\r
9570         /* [AS] Memory leak here! */\r
9571         appData.gameListTags = strdup( glt ); \r
9572     }\r
9573 \r
9574     return result;\r
9575 }\r
9576 \r
9577 \r
9578 VOID\r
9579 DisplayIcsInteractionTitle(char *str)\r
9580 {\r
9581   char consoleTitle[MSG_SIZ];\r
9582 \r
9583   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9584   SetWindowText(hwndConsole, consoleTitle);\r
9585 }\r
9586 \r
9587 void\r
9588 DrawPosition(int fullRedraw, Board board)\r
9589 {\r
9590   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9591 }\r
9592 \r
9593 \r
9594 VOID\r
9595 ResetFrontEnd()\r
9596 {\r
9597   fromX = fromY = -1;\r
9598   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9599     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9600     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9601     dragInfo.lastpos = dragInfo.pos;\r
9602     dragInfo.start.x = dragInfo.start.y = -1;\r
9603     dragInfo.from = dragInfo.start;\r
9604     ReleaseCapture();\r
9605     DrawPosition(TRUE, NULL);\r
9606   }\r
9607 }\r
9608 \r
9609 \r
9610 VOID\r
9611 CommentPopUp(char *title, char *str)\r
9612 {\r
9613   HWND hwnd = GetActiveWindow();\r
9614   EitherCommentPopUp(0, title, str, FALSE);\r
9615   SetActiveWindow(hwnd);\r
9616 }\r
9617 \r
9618 VOID\r
9619 CommentPopDown(void)\r
9620 {\r
9621   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9622   if (commentDialog) {\r
9623     ShowWindow(commentDialog, SW_HIDE);\r
9624   }\r
9625   commentDialogUp = FALSE;\r
9626 }\r
9627 \r
9628 VOID\r
9629 EditCommentPopUp(int index, char *title, char *str)\r
9630 {\r
9631   EitherCommentPopUp(index, title, str, TRUE);\r
9632 }\r
9633 \r
9634 \r
9635 VOID\r
9636 RingBell()\r
9637 {\r
9638   MyPlaySound(&sounds[(int)SoundMove]);\r
9639 }\r
9640 \r
9641 VOID PlayIcsWinSound()\r
9642 {\r
9643   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9644 }\r
9645 \r
9646 VOID PlayIcsLossSound()\r
9647 {\r
9648   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9649 }\r
9650 \r
9651 VOID PlayIcsDrawSound()\r
9652 {\r
9653   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9654 }\r
9655 \r
9656 VOID PlayIcsUnfinishedSound()\r
9657 {\r
9658   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9659 }\r
9660 \r
9661 VOID\r
9662 PlayAlarmSound()\r
9663 {\r
9664   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9665 }\r
9666 \r
9667 \r
9668 VOID\r
9669 EchoOn()\r
9670 {\r
9671   HWND hInput;\r
9672   consoleEcho = TRUE;\r
9673   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9674   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9675   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9676 }\r
9677 \r
9678 \r
9679 VOID\r
9680 EchoOff()\r
9681 {\r
9682   CHARFORMAT cf;\r
9683   HWND hInput;\r
9684   consoleEcho = FALSE;\r
9685   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9686   /* This works OK: set text and background both to the same color */\r
9687   cf = consoleCF;\r
9688   cf.crTextColor = COLOR_ECHOOFF;\r
9689   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9690   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9691 }\r
9692 \r
9693 /* No Raw()...? */\r
9694 \r
9695 void Colorize(ColorClass cc, int continuation)\r
9696 {\r
9697   currentColorClass = cc;\r
9698   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9699   consoleCF.crTextColor = textAttribs[cc].color;\r
9700   consoleCF.dwEffects = textAttribs[cc].effects;\r
9701   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9702 }\r
9703 \r
9704 char *\r
9705 UserName()\r
9706 {\r
9707   static char buf[MSG_SIZ];\r
9708   DWORD bufsiz = MSG_SIZ;\r
9709 \r
9710   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9711         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9712   }\r
9713   if (!GetUserName(buf, &bufsiz)) {\r
9714     /*DisplayError("Error getting user name", GetLastError());*/\r
9715     strcpy(buf, "User");\r
9716   }\r
9717   return buf;\r
9718 }\r
9719 \r
9720 char *\r
9721 HostName()\r
9722 {\r
9723   static char buf[MSG_SIZ];\r
9724   DWORD bufsiz = MSG_SIZ;\r
9725 \r
9726   if (!GetComputerName(buf, &bufsiz)) {\r
9727     /*DisplayError("Error getting host name", GetLastError());*/\r
9728     strcpy(buf, "Unknown");\r
9729   }\r
9730   return buf;\r
9731 }\r
9732 \r
9733 \r
9734 int\r
9735 ClockTimerRunning()\r
9736 {\r
9737   return clockTimerEvent != 0;\r
9738 }\r
9739 \r
9740 int\r
9741 StopClockTimer()\r
9742 {\r
9743   if (clockTimerEvent == 0) return FALSE;\r
9744   KillTimer(hwndMain, clockTimerEvent);\r
9745   clockTimerEvent = 0;\r
9746   return TRUE;\r
9747 }\r
9748 \r
9749 void\r
9750 StartClockTimer(long millisec)\r
9751 {\r
9752   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9753                              (UINT) millisec, NULL);\r
9754 }\r
9755 \r
9756 void\r
9757 DisplayWhiteClock(long timeRemaining, int highlight)\r
9758 {\r
9759   HDC hdc;\r
9760   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9761 \r
9762   if(appData.noGUI) return;\r
9763   hdc = GetDC(hwndMain);\r
9764   if (!IsIconic(hwndMain)) {\r
9765     DisplayAClock(hdc, timeRemaining, highlight, \r
9766                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9767   }\r
9768   if (highlight && iconCurrent == iconBlack) {\r
9769     iconCurrent = iconWhite;\r
9770     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9771     if (IsIconic(hwndMain)) {\r
9772       DrawIcon(hdc, 2, 2, iconCurrent);\r
9773     }\r
9774   }\r
9775   (void) ReleaseDC(hwndMain, hdc);\r
9776   if (hwndConsole)\r
9777     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9778 }\r
9779 \r
9780 void\r
9781 DisplayBlackClock(long timeRemaining, int highlight)\r
9782 {\r
9783   HDC hdc;\r
9784   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9785 \r
9786   if(appData.noGUI) return;\r
9787   hdc = GetDC(hwndMain);\r
9788   if (!IsIconic(hwndMain)) {\r
9789     DisplayAClock(hdc, timeRemaining, highlight, \r
9790                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9791   }\r
9792   if (highlight && iconCurrent == iconWhite) {\r
9793     iconCurrent = iconBlack;\r
9794     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9795     if (IsIconic(hwndMain)) {\r
9796       DrawIcon(hdc, 2, 2, iconCurrent);\r
9797     }\r
9798   }\r
9799   (void) ReleaseDC(hwndMain, hdc);\r
9800   if (hwndConsole)\r
9801     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9802 }\r
9803 \r
9804 \r
9805 int\r
9806 LoadGameTimerRunning()\r
9807 {\r
9808   return loadGameTimerEvent != 0;\r
9809 }\r
9810 \r
9811 int\r
9812 StopLoadGameTimer()\r
9813 {\r
9814   if (loadGameTimerEvent == 0) return FALSE;\r
9815   KillTimer(hwndMain, loadGameTimerEvent);\r
9816   loadGameTimerEvent = 0;\r
9817   return TRUE;\r
9818 }\r
9819 \r
9820 void\r
9821 StartLoadGameTimer(long millisec)\r
9822 {\r
9823   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9824                                 (UINT) millisec, NULL);\r
9825 }\r
9826 \r
9827 void\r
9828 AutoSaveGame()\r
9829 {\r
9830   char *defName;\r
9831   FILE *f;\r
9832   char fileTitle[MSG_SIZ];\r
9833 \r
9834   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9835   f = OpenFileDialog(hwndMain, "a", defName,\r
9836                      appData.oldSaveStyle ? "gam" : "pgn",\r
9837                      GAME_FILT, \r
9838                      "Save Game to File", NULL, fileTitle, NULL);\r
9839   if (f != NULL) {\r
9840     SaveGame(f, 0, "");\r
9841     fclose(f);\r
9842   }\r
9843 }\r
9844 \r
9845 \r
9846 void\r
9847 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9848 {\r
9849   if (delayedTimerEvent != 0) {\r
9850     if (appData.debugMode) {\r
9851       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9852     }\r
9853     KillTimer(hwndMain, delayedTimerEvent);\r
9854     delayedTimerEvent = 0;\r
9855     delayedTimerCallback();\r
9856   }\r
9857   delayedTimerCallback = cb;\r
9858   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9859                                 (UINT) millisec, NULL);\r
9860 }\r
9861 \r
9862 DelayedEventCallback\r
9863 GetDelayedEvent()\r
9864 {\r
9865   if (delayedTimerEvent) {\r
9866     return delayedTimerCallback;\r
9867   } else {\r
9868     return NULL;\r
9869   }\r
9870 }\r
9871 \r
9872 void\r
9873 CancelDelayedEvent()\r
9874 {\r
9875   if (delayedTimerEvent) {\r
9876     KillTimer(hwndMain, delayedTimerEvent);\r
9877     delayedTimerEvent = 0;\r
9878   }\r
9879 }\r
9880 \r
9881 DWORD GetWin32Priority(int nice)\r
9882 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9883 /*\r
9884 REALTIME_PRIORITY_CLASS     0x00000100\r
9885 HIGH_PRIORITY_CLASS         0x00000080\r
9886 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9887 NORMAL_PRIORITY_CLASS       0x00000020\r
9888 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9889 IDLE_PRIORITY_CLASS         0x00000040\r
9890 */\r
9891         if (nice < -15) return 0x00000080;\r
9892         if (nice < 0)   return 0x00008000;\r
9893         if (nice == 0)  return 0x00000020;\r
9894         if (nice < 15)  return 0x00004000;\r
9895         return 0x00000040;\r
9896 }\r
9897 \r
9898 /* Start a child process running the given program.\r
9899    The process's standard output can be read from "from", and its\r
9900    standard input can be written to "to".\r
9901    Exit with fatal error if anything goes wrong.\r
9902    Returns an opaque pointer that can be used to destroy the process\r
9903    later.\r
9904 */\r
9905 int\r
9906 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9907 {\r
9908 #define BUFSIZE 4096\r
9909 \r
9910   HANDLE hChildStdinRd, hChildStdinWr,\r
9911     hChildStdoutRd, hChildStdoutWr;\r
9912   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9913   SECURITY_ATTRIBUTES saAttr;\r
9914   BOOL fSuccess;\r
9915   PROCESS_INFORMATION piProcInfo;\r
9916   STARTUPINFO siStartInfo;\r
9917   ChildProc *cp;\r
9918   char buf[MSG_SIZ];\r
9919   DWORD err;\r
9920 \r
9921   if (appData.debugMode) {\r
9922     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9923   }\r
9924 \r
9925   *pr = NoProc;\r
9926 \r
9927   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9928   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9929   saAttr.bInheritHandle = TRUE;\r
9930   saAttr.lpSecurityDescriptor = NULL;\r
9931 \r
9932   /*\r
9933    * The steps for redirecting child's STDOUT:\r
9934    *     1. Create anonymous pipe to be STDOUT for child.\r
9935    *     2. Create a noninheritable duplicate of read handle,\r
9936    *         and close the inheritable read handle.\r
9937    */\r
9938 \r
9939   /* Create a pipe for the child's STDOUT. */\r
9940   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9941     return GetLastError();\r
9942   }\r
9943 \r
9944   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9945   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9946                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9947                              FALSE,     /* not inherited */\r
9948                              DUPLICATE_SAME_ACCESS);\r
9949   if (! fSuccess) {\r
9950     return GetLastError();\r
9951   }\r
9952   CloseHandle(hChildStdoutRd);\r
9953 \r
9954   /*\r
9955    * The steps for redirecting child's STDIN:\r
9956    *     1. Create anonymous pipe to be STDIN for child.\r
9957    *     2. Create a noninheritable duplicate of write handle,\r
9958    *         and close the inheritable write handle.\r
9959    */\r
9960 \r
9961   /* Create a pipe for the child's STDIN. */\r
9962   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9963     return GetLastError();\r
9964   }\r
9965 \r
9966   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9967   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9968                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9969                              FALSE,     /* not inherited */\r
9970                              DUPLICATE_SAME_ACCESS);\r
9971   if (! fSuccess) {\r
9972     return GetLastError();\r
9973   }\r
9974   CloseHandle(hChildStdinWr);\r
9975 \r
9976   /* Arrange to (1) look in dir for the child .exe file, and\r
9977    * (2) have dir be the child's working directory.  Interpret\r
9978    * dir relative to the directory WinBoard loaded from. */\r
9979   GetCurrentDirectory(MSG_SIZ, buf);\r
9980   SetCurrentDirectory(installDir);\r
9981   SetCurrentDirectory(dir);\r
9982 \r
9983   /* Now create the child process. */\r
9984 \r
9985   siStartInfo.cb = sizeof(STARTUPINFO);\r
9986   siStartInfo.lpReserved = NULL;\r
9987   siStartInfo.lpDesktop = NULL;\r
9988   siStartInfo.lpTitle = NULL;\r
9989   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9990   siStartInfo.cbReserved2 = 0;\r
9991   siStartInfo.lpReserved2 = NULL;\r
9992   siStartInfo.hStdInput = hChildStdinRd;\r
9993   siStartInfo.hStdOutput = hChildStdoutWr;\r
9994   siStartInfo.hStdError = hChildStdoutWr;\r
9995 \r
9996   fSuccess = CreateProcess(NULL,\r
9997                            cmdLine,        /* command line */\r
9998                            NULL,           /* process security attributes */\r
9999                            NULL,           /* primary thread security attrs */\r
10000                            TRUE,           /* handles are inherited */\r
10001                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10002                            NULL,           /* use parent's environment */\r
10003                            NULL,\r
10004                            &siStartInfo, /* STARTUPINFO pointer */\r
10005                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10006 \r
10007   err = GetLastError();\r
10008   SetCurrentDirectory(buf); /* return to prev directory */\r
10009   if (! fSuccess) {\r
10010     return err;\r
10011   }\r
10012 \r
10013   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10014     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10015     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10016   }\r
10017 \r
10018   /* Close the handles we don't need in the parent */\r
10019   CloseHandle(piProcInfo.hThread);\r
10020   CloseHandle(hChildStdinRd);\r
10021   CloseHandle(hChildStdoutWr);\r
10022 \r
10023   /* Prepare return value */\r
10024   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10025   cp->kind = CPReal;\r
10026   cp->hProcess = piProcInfo.hProcess;\r
10027   cp->pid = piProcInfo.dwProcessId;\r
10028   cp->hFrom = hChildStdoutRdDup;\r
10029   cp->hTo = hChildStdinWrDup;\r
10030 \r
10031   *pr = (void *) cp;\r
10032 \r
10033   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10034      2000 where engines sometimes don't see the initial command(s)\r
10035      from WinBoard and hang.  I don't understand how that can happen,\r
10036      but the Sleep is harmless, so I've put it in.  Others have also\r
10037      reported what may be the same problem, so hopefully this will fix\r
10038      it for them too.  */\r
10039   Sleep(500);\r
10040 \r
10041   return NO_ERROR;\r
10042 }\r
10043 \r
10044 \r
10045 void\r
10046 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10047 {\r
10048   ChildProc *cp; int result;\r
10049 \r
10050   cp = (ChildProc *) pr;\r
10051   if (cp == NULL) return;\r
10052 \r
10053   switch (cp->kind) {\r
10054   case CPReal:\r
10055     /* TerminateProcess is considered harmful, so... */\r
10056     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10057     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10058     /* The following doesn't work because the chess program\r
10059        doesn't "have the same console" as WinBoard.  Maybe\r
10060        we could arrange for this even though neither WinBoard\r
10061        nor the chess program uses a console for stdio? */\r
10062     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10063 \r
10064     /* [AS] Special termination modes for misbehaving programs... */\r
10065     if( signal == 9 ) { \r
10066         result = TerminateProcess( cp->hProcess, 0 );\r
10067 \r
10068         if ( appData.debugMode) {\r
10069             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10070         }\r
10071     }\r
10072     else if( signal == 10 ) {\r
10073         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10074 \r
10075         if( dw != WAIT_OBJECT_0 ) {\r
10076             result = TerminateProcess( cp->hProcess, 0 );\r
10077 \r
10078             if ( appData.debugMode) {\r
10079                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10080             }\r
10081 \r
10082         }\r
10083     }\r
10084 \r
10085     CloseHandle(cp->hProcess);\r
10086     break;\r
10087 \r
10088   case CPComm:\r
10089     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10090     break;\r
10091 \r
10092   case CPSock:\r
10093     closesocket(cp->sock);\r
10094     WSACleanup();\r
10095     break;\r
10096 \r
10097   case CPRcmd:\r
10098     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10099     closesocket(cp->sock);\r
10100     closesocket(cp->sock2);\r
10101     WSACleanup();\r
10102     break;\r
10103   }\r
10104   free(cp);\r
10105 }\r
10106 \r
10107 void\r
10108 InterruptChildProcess(ProcRef pr)\r
10109 {\r
10110   ChildProc *cp;\r
10111 \r
10112   cp = (ChildProc *) pr;\r
10113   if (cp == NULL) return;\r
10114   switch (cp->kind) {\r
10115   case CPReal:\r
10116     /* The following doesn't work because the chess program\r
10117        doesn't "have the same console" as WinBoard.  Maybe\r
10118        we could arrange for this even though neither WinBoard\r
10119        nor the chess program uses a console for stdio */\r
10120     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10121     break;\r
10122 \r
10123   case CPComm:\r
10124   case CPSock:\r
10125     /* Can't interrupt */\r
10126     break;\r
10127 \r
10128   case CPRcmd:\r
10129     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10130     break;\r
10131   }\r
10132 }\r
10133 \r
10134 \r
10135 int\r
10136 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10137 {\r
10138   char cmdLine[MSG_SIZ];\r
10139 \r
10140   if (port[0] == NULLCHAR) {\r
10141     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10142   } else {\r
10143     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10144   }\r
10145   return StartChildProcess(cmdLine, "", pr);\r
10146 }\r
10147 \r
10148 \r
10149 /* Code to open TCP sockets */\r
10150 \r
10151 int\r
10152 OpenTCP(char *host, char *port, ProcRef *pr)\r
10153 {\r
10154   ChildProc *cp;\r
10155   int err;\r
10156   SOCKET s;\r
10157   struct sockaddr_in sa, mysa;\r
10158   struct hostent FAR *hp;\r
10159   unsigned short uport;\r
10160   WORD wVersionRequested;\r
10161   WSADATA wsaData;\r
10162 \r
10163   /* Initialize socket DLL */\r
10164   wVersionRequested = MAKEWORD(1, 1);\r
10165   err = WSAStartup(wVersionRequested, &wsaData);\r
10166   if (err != 0) return err;\r
10167 \r
10168   /* Make socket */\r
10169   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10170     err = WSAGetLastError();\r
10171     WSACleanup();\r
10172     return err;\r
10173   }\r
10174 \r
10175   /* Bind local address using (mostly) don't-care values.\r
10176    */\r
10177   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10178   mysa.sin_family = AF_INET;\r
10179   mysa.sin_addr.s_addr = INADDR_ANY;\r
10180   uport = (unsigned short) 0;\r
10181   mysa.sin_port = htons(uport);\r
10182   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10183       == SOCKET_ERROR) {\r
10184     err = WSAGetLastError();\r
10185     WSACleanup();\r
10186     return err;\r
10187   }\r
10188 \r
10189   /* Resolve remote host name */\r
10190   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10191   if (!(hp = gethostbyname(host))) {\r
10192     unsigned int b0, b1, b2, b3;\r
10193 \r
10194     err = WSAGetLastError();\r
10195 \r
10196     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10197       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10198       hp->h_addrtype = AF_INET;\r
10199       hp->h_length = 4;\r
10200       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10201       hp->h_addr_list[0] = (char *) malloc(4);\r
10202       hp->h_addr_list[0][0] = (char) b0;\r
10203       hp->h_addr_list[0][1] = (char) b1;\r
10204       hp->h_addr_list[0][2] = (char) b2;\r
10205       hp->h_addr_list[0][3] = (char) b3;\r
10206     } else {\r
10207       WSACleanup();\r
10208       return err;\r
10209     }\r
10210   }\r
10211   sa.sin_family = hp->h_addrtype;\r
10212   uport = (unsigned short) atoi(port);\r
10213   sa.sin_port = htons(uport);\r
10214   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10215 \r
10216   /* Make connection */\r
10217   if (connect(s, (struct sockaddr *) &sa,\r
10218               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10219     err = WSAGetLastError();\r
10220     WSACleanup();\r
10221     return err;\r
10222   }\r
10223 \r
10224   /* Prepare return value */\r
10225   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10226   cp->kind = CPSock;\r
10227   cp->sock = s;\r
10228   *pr = (ProcRef *) cp;\r
10229 \r
10230   return NO_ERROR;\r
10231 }\r
10232 \r
10233 int\r
10234 OpenCommPort(char *name, ProcRef *pr)\r
10235 {\r
10236   HANDLE h;\r
10237   COMMTIMEOUTS ct;\r
10238   ChildProc *cp;\r
10239   char fullname[MSG_SIZ];\r
10240 \r
10241   if (*name != '\\')\r
10242     sprintf(fullname, "\\\\.\\%s", name);\r
10243   else\r
10244     strcpy(fullname, name);\r
10245 \r
10246   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10247                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10248   if (h == (HANDLE) -1) {\r
10249     return GetLastError();\r
10250   }\r
10251   hCommPort = h;\r
10252 \r
10253   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10254 \r
10255   /* Accumulate characters until a 100ms pause, then parse */\r
10256   ct.ReadIntervalTimeout = 100;\r
10257   ct.ReadTotalTimeoutMultiplier = 0;\r
10258   ct.ReadTotalTimeoutConstant = 0;\r
10259   ct.WriteTotalTimeoutMultiplier = 0;\r
10260   ct.WriteTotalTimeoutConstant = 0;\r
10261   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10262 \r
10263   /* Prepare return value */\r
10264   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10265   cp->kind = CPComm;\r
10266   cp->hFrom = h;\r
10267   cp->hTo = h;\r
10268   *pr = (ProcRef *) cp;\r
10269 \r
10270   return NO_ERROR;\r
10271 }\r
10272 \r
10273 int\r
10274 OpenLoopback(ProcRef *pr)\r
10275 {\r
10276   DisplayFatalError("Not implemented", 0, 1);\r
10277   return NO_ERROR;\r
10278 }\r
10279 \r
10280 \r
10281 int\r
10282 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10283 {\r
10284   ChildProc *cp;\r
10285   int err;\r
10286   SOCKET s, s2, s3;\r
10287   struct sockaddr_in sa, mysa;\r
10288   struct hostent FAR *hp;\r
10289   unsigned short uport;\r
10290   WORD wVersionRequested;\r
10291   WSADATA wsaData;\r
10292   int fromPort;\r
10293   char stderrPortStr[MSG_SIZ];\r
10294 \r
10295   /* Initialize socket DLL */\r
10296   wVersionRequested = MAKEWORD(1, 1);\r
10297   err = WSAStartup(wVersionRequested, &wsaData);\r
10298   if (err != 0) return err;\r
10299 \r
10300   /* Resolve remote host name */\r
10301   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10302   if (!(hp = gethostbyname(host))) {\r
10303     unsigned int b0, b1, b2, b3;\r
10304 \r
10305     err = WSAGetLastError();\r
10306 \r
10307     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10308       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10309       hp->h_addrtype = AF_INET;\r
10310       hp->h_length = 4;\r
10311       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10312       hp->h_addr_list[0] = (char *) malloc(4);\r
10313       hp->h_addr_list[0][0] = (char) b0;\r
10314       hp->h_addr_list[0][1] = (char) b1;\r
10315       hp->h_addr_list[0][2] = (char) b2;\r
10316       hp->h_addr_list[0][3] = (char) b3;\r
10317     } else {\r
10318       WSACleanup();\r
10319       return err;\r
10320     }\r
10321   }\r
10322   sa.sin_family = hp->h_addrtype;\r
10323   uport = (unsigned short) 514;\r
10324   sa.sin_port = htons(uport);\r
10325   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10326 \r
10327   /* Bind local socket to unused "privileged" port address\r
10328    */\r
10329   s = INVALID_SOCKET;\r
10330   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10331   mysa.sin_family = AF_INET;\r
10332   mysa.sin_addr.s_addr = INADDR_ANY;\r
10333   for (fromPort = 1023;; fromPort--) {\r
10334     if (fromPort < 0) {\r
10335       WSACleanup();\r
10336       return WSAEADDRINUSE;\r
10337     }\r
10338     if (s == INVALID_SOCKET) {\r
10339       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10340         err = WSAGetLastError();\r
10341         WSACleanup();\r
10342         return err;\r
10343       }\r
10344     }\r
10345     uport = (unsigned short) fromPort;\r
10346     mysa.sin_port = htons(uport);\r
10347     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10348         == SOCKET_ERROR) {\r
10349       err = WSAGetLastError();\r
10350       if (err == WSAEADDRINUSE) continue;\r
10351       WSACleanup();\r
10352       return err;\r
10353     }\r
10354     if (connect(s, (struct sockaddr *) &sa,\r
10355       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10356       err = WSAGetLastError();\r
10357       if (err == WSAEADDRINUSE) {\r
10358         closesocket(s);\r
10359         s = -1;\r
10360         continue;\r
10361       }\r
10362       WSACleanup();\r
10363       return err;\r
10364     }\r
10365     break;\r
10366   }\r
10367 \r
10368   /* Bind stderr local socket to unused "privileged" port address\r
10369    */\r
10370   s2 = INVALID_SOCKET;\r
10371   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10372   mysa.sin_family = AF_INET;\r
10373   mysa.sin_addr.s_addr = INADDR_ANY;\r
10374   for (fromPort = 1023;; fromPort--) {\r
10375     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10376     if (fromPort < 0) {\r
10377       (void) closesocket(s);\r
10378       WSACleanup();\r
10379       return WSAEADDRINUSE;\r
10380     }\r
10381     if (s2 == INVALID_SOCKET) {\r
10382       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10383         err = WSAGetLastError();\r
10384         closesocket(s);\r
10385         WSACleanup();\r
10386         return err;\r
10387       }\r
10388     }\r
10389     uport = (unsigned short) fromPort;\r
10390     mysa.sin_port = htons(uport);\r
10391     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10392         == SOCKET_ERROR) {\r
10393       err = WSAGetLastError();\r
10394       if (err == WSAEADDRINUSE) continue;\r
10395       (void) closesocket(s);\r
10396       WSACleanup();\r
10397       return err;\r
10398     }\r
10399     if (listen(s2, 1) == SOCKET_ERROR) {\r
10400       err = WSAGetLastError();\r
10401       if (err == WSAEADDRINUSE) {\r
10402         closesocket(s2);\r
10403         s2 = INVALID_SOCKET;\r
10404         continue;\r
10405       }\r
10406       (void) closesocket(s);\r
10407       (void) closesocket(s2);\r
10408       WSACleanup();\r
10409       return err;\r
10410     }\r
10411     break;\r
10412   }\r
10413   prevStderrPort = fromPort; // remember port used\r
10414   sprintf(stderrPortStr, "%d", fromPort);\r
10415 \r
10416   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10417     err = WSAGetLastError();\r
10418     (void) closesocket(s);\r
10419     (void) closesocket(s2);\r
10420     WSACleanup();\r
10421     return err;\r
10422   }\r
10423 \r
10424   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10425     err = WSAGetLastError();\r
10426     (void) closesocket(s);\r
10427     (void) closesocket(s2);\r
10428     WSACleanup();\r
10429     return err;\r
10430   }\r
10431   if (*user == NULLCHAR) user = UserName();\r
10432   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10433     err = WSAGetLastError();\r
10434     (void) closesocket(s);\r
10435     (void) closesocket(s2);\r
10436     WSACleanup();\r
10437     return err;\r
10438   }\r
10439   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10440     err = WSAGetLastError();\r
10441     (void) closesocket(s);\r
10442     (void) closesocket(s2);\r
10443     WSACleanup();\r
10444     return err;\r
10445   }\r
10446 \r
10447   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10448     err = WSAGetLastError();\r
10449     (void) closesocket(s);\r
10450     (void) closesocket(s2);\r
10451     WSACleanup();\r
10452     return err;\r
10453   }\r
10454   (void) closesocket(s2);  /* Stop listening */\r
10455 \r
10456   /* Prepare return value */\r
10457   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10458   cp->kind = CPRcmd;\r
10459   cp->sock = s;\r
10460   cp->sock2 = s3;\r
10461   *pr = (ProcRef *) cp;\r
10462 \r
10463   return NO_ERROR;\r
10464 }\r
10465 \r
10466 \r
10467 InputSourceRef\r
10468 AddInputSource(ProcRef pr, int lineByLine,\r
10469                InputCallback func, VOIDSTAR closure)\r
10470 {\r
10471   InputSource *is, *is2 = NULL;\r
10472   ChildProc *cp = (ChildProc *) pr;\r
10473 \r
10474   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10475   is->lineByLine = lineByLine;\r
10476   is->func = func;\r
10477   is->closure = closure;\r
10478   is->second = NULL;\r
10479   is->next = is->buf;\r
10480   if (pr == NoProc) {\r
10481     is->kind = CPReal;\r
10482     consoleInputSource = is;\r
10483   } else {\r
10484     is->kind = cp->kind;\r
10485     /* \r
10486         [AS] Try to avoid a race condition if the thread is given control too early:\r
10487         we create all threads suspended so that the is->hThread variable can be\r
10488         safely assigned, then let the threads start with ResumeThread.\r
10489     */\r
10490     switch (cp->kind) {\r
10491     case CPReal:\r
10492       is->hFile = cp->hFrom;\r
10493       cp->hFrom = NULL; /* now owned by InputThread */\r
10494       is->hThread =\r
10495         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10496                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10497       break;\r
10498 \r
10499     case CPComm:\r
10500       is->hFile = cp->hFrom;\r
10501       cp->hFrom = NULL; /* now owned by InputThread */\r
10502       is->hThread =\r
10503         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10504                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10505       break;\r
10506 \r
10507     case CPSock:\r
10508       is->sock = cp->sock;\r
10509       is->hThread =\r
10510         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10511                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10512       break;\r
10513 \r
10514     case CPRcmd:\r
10515       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10516       *is2 = *is;\r
10517       is->sock = cp->sock;\r
10518       is->second = is2;\r
10519       is2->sock = cp->sock2;\r
10520       is2->second = is2;\r
10521       is->hThread =\r
10522         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10523                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10524       is2->hThread =\r
10525         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10526                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10527       break;\r
10528     }\r
10529 \r
10530     if( is->hThread != NULL ) {\r
10531         ResumeThread( is->hThread );\r
10532     }\r
10533 \r
10534     if( is2 != NULL && is2->hThread != NULL ) {\r
10535         ResumeThread( is2->hThread );\r
10536     }\r
10537   }\r
10538 \r
10539   return (InputSourceRef) is;\r
10540 }\r
10541 \r
10542 void\r
10543 RemoveInputSource(InputSourceRef isr)\r
10544 {\r
10545   InputSource *is;\r
10546 \r
10547   is = (InputSource *) isr;\r
10548   is->hThread = NULL;  /* tell thread to stop */\r
10549   CloseHandle(is->hThread);\r
10550   if (is->second != NULL) {\r
10551     is->second->hThread = NULL;\r
10552     CloseHandle(is->second->hThread);\r
10553   }\r
10554 }\r
10555 \r
10556 \r
10557 int\r
10558 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10559 {\r
10560   DWORD dOutCount;\r
10561   int outCount = SOCKET_ERROR;\r
10562   ChildProc *cp = (ChildProc *) pr;\r
10563   static OVERLAPPED ovl;\r
10564 \r
10565   if (pr == NoProc) {\r
10566     ConsoleOutput(message, count, FALSE);\r
10567     return count;\r
10568   } \r
10569 \r
10570   if (ovl.hEvent == NULL) {\r
10571     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10572   }\r
10573   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10574 \r
10575   switch (cp->kind) {\r
10576   case CPSock:\r
10577   case CPRcmd:\r
10578     outCount = send(cp->sock, message, count, 0);\r
10579     if (outCount == SOCKET_ERROR) {\r
10580       *outError = WSAGetLastError();\r
10581     } else {\r
10582       *outError = NO_ERROR;\r
10583     }\r
10584     break;\r
10585 \r
10586   case CPReal:\r
10587     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10588                   &dOutCount, NULL)) {\r
10589       *outError = NO_ERROR;\r
10590       outCount = (int) dOutCount;\r
10591     } else {\r
10592       *outError = GetLastError();\r
10593     }\r
10594     break;\r
10595 \r
10596   case CPComm:\r
10597     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10598                             &dOutCount, &ovl);\r
10599     if (*outError == NO_ERROR) {\r
10600       outCount = (int) dOutCount;\r
10601     }\r
10602     break;\r
10603   }\r
10604   return outCount;\r
10605 }\r
10606 \r
10607 int\r
10608 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10609                        long msdelay)\r
10610 {\r
10611   /* Ignore delay, not implemented for WinBoard */\r
10612   return OutputToProcess(pr, message, count, outError);\r
10613 }\r
10614 \r
10615 \r
10616 void\r
10617 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10618                         char *buf, int count, int error)\r
10619 {\r
10620   DisplayFatalError("Not implemented", 0, 1);\r
10621 }\r
10622 \r
10623 /* see wgamelist.c for Game List functions */\r
10624 /* see wedittags.c for Edit Tags functions */\r
10625 \r
10626 \r
10627 VOID\r
10628 ICSInitScript()\r
10629 {\r
10630   FILE *f;\r
10631   char buf[MSG_SIZ];\r
10632   char *dummy;\r
10633 \r
10634   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10635     f = fopen(buf, "r");\r
10636     if (f != NULL) {\r
10637       ProcessICSInitScript(f);\r
10638       fclose(f);\r
10639     }\r
10640   }\r
10641 }\r
10642 \r
10643 \r
10644 VOID\r
10645 StartAnalysisClock()\r
10646 {\r
10647   if (analysisTimerEvent) return;\r
10648   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10649                                         (UINT) 2000, NULL);\r
10650 }\r
10651 \r
10652 LRESULT CALLBACK\r
10653 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10654 {\r
10655   static HANDLE hwndText;\r
10656   RECT rect;\r
10657   static int sizeX, sizeY;\r
10658   int newSizeX, newSizeY, flags;\r
10659   MINMAXINFO *mmi;\r
10660 \r
10661   switch (message) {\r
10662   case WM_INITDIALOG: /* message: initialize dialog box */\r
10663     /* Initialize the dialog items */\r
10664     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10665     SetWindowText(hDlg, analysisTitle);\r
10666     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10667     /* Size and position the dialog */\r
10668     if (!analysisDialog) {\r
10669       analysisDialog = hDlg;\r
10670       flags = SWP_NOZORDER;\r
10671       GetClientRect(hDlg, &rect);\r
10672       sizeX = rect.right;\r
10673       sizeY = rect.bottom;\r
10674       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10675           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10676         WINDOWPLACEMENT wp;\r
10677         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10678         wp.length = sizeof(WINDOWPLACEMENT);\r
10679         wp.flags = 0;\r
10680         wp.showCmd = SW_SHOW;\r
10681         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10682         wp.rcNormalPosition.left = analysisX;\r
10683         wp.rcNormalPosition.right = analysisX + analysisW;\r
10684         wp.rcNormalPosition.top = analysisY;\r
10685         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10686         SetWindowPlacement(hDlg, &wp);\r
10687 \r
10688         GetClientRect(hDlg, &rect);\r
10689         newSizeX = rect.right;\r
10690         newSizeY = rect.bottom;\r
10691         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10692                               newSizeX, newSizeY);\r
10693         sizeX = newSizeX;\r
10694         sizeY = newSizeY;\r
10695       }\r
10696     }\r
10697     return FALSE;\r
10698 \r
10699   case WM_COMMAND: /* message: received a command */\r
10700     switch (LOWORD(wParam)) {\r
10701     case IDCANCEL:\r
10702       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10703           ExitAnalyzeMode();\r
10704           ModeHighlight();\r
10705           return TRUE;\r
10706       }\r
10707       EditGameEvent();\r
10708       return TRUE;\r
10709     default:\r
10710       break;\r
10711     }\r
10712     break;\r
10713 \r
10714   case WM_SIZE:\r
10715     newSizeX = LOWORD(lParam);\r
10716     newSizeY = HIWORD(lParam);\r
10717     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10718     sizeX = newSizeX;\r
10719     sizeY = newSizeY;\r
10720     break;\r
10721 \r
10722   case WM_GETMINMAXINFO:\r
10723     /* Prevent resizing window too small */\r
10724     mmi = (MINMAXINFO *) lParam;\r
10725     mmi->ptMinTrackSize.x = 100;\r
10726     mmi->ptMinTrackSize.y = 100;\r
10727     break;\r
10728   }\r
10729   return FALSE;\r
10730 }\r
10731 \r
10732 VOID\r
10733 AnalysisPopUp(char* title, char* str)\r
10734 {\r
10735   FARPROC lpProc;\r
10736   char *p, *q;\r
10737 \r
10738   /* [AS] */\r
10739   EngineOutputPopUp();\r
10740   return;\r
10741 \r
10742   if (str == NULL) str = "";\r
10743   p = (char *) malloc(2 * strlen(str) + 2);\r
10744   q = p;\r
10745   while (*str) {\r
10746     if (*str == '\n') *q++ = '\r';\r
10747     *q++ = *str++;\r
10748   }\r
10749   *q = NULLCHAR;\r
10750   if (analysisText != NULL) free(analysisText);\r
10751   analysisText = p;\r
10752 \r
10753   if (analysisDialog) {\r
10754     SetWindowText(analysisDialog, title);\r
10755     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10756     ShowWindow(analysisDialog, SW_SHOW);\r
10757   } else {\r
10758     analysisTitle = title;\r
10759     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10760     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10761                  hwndMain, (DLGPROC)lpProc);\r
10762     FreeProcInstance(lpProc);\r
10763   }\r
10764   analysisDialogUp = TRUE;  \r
10765 }\r
10766 \r
10767 VOID\r
10768 AnalysisPopDown()\r
10769 {\r
10770   if (analysisDialog) {\r
10771     ShowWindow(analysisDialog, SW_HIDE);\r
10772   }\r
10773   analysisDialogUp = FALSE;  \r
10774 }\r
10775 \r
10776 \r
10777 VOID\r
10778 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10779 {\r
10780   highlightInfo.sq[0].x = fromX;\r
10781   highlightInfo.sq[0].y = fromY;\r
10782   highlightInfo.sq[1].x = toX;\r
10783   highlightInfo.sq[1].y = toY;\r
10784 }\r
10785 \r
10786 VOID\r
10787 ClearHighlights()\r
10788 {\r
10789   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10790     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10791 }\r
10792 \r
10793 VOID\r
10794 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10795 {\r
10796   premoveHighlightInfo.sq[0].x = fromX;\r
10797   premoveHighlightInfo.sq[0].y = fromY;\r
10798   premoveHighlightInfo.sq[1].x = toX;\r
10799   premoveHighlightInfo.sq[1].y = toY;\r
10800 }\r
10801 \r
10802 VOID\r
10803 ClearPremoveHighlights()\r
10804 {\r
10805   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10806     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10807 }\r
10808 \r
10809 VOID\r
10810 ShutDownFrontEnd()\r
10811 {\r
10812   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10813   DeleteClipboardTempFiles();\r
10814 }\r
10815 \r
10816 void\r
10817 BoardToTop()\r
10818 {\r
10819     if (IsIconic(hwndMain))\r
10820       ShowWindow(hwndMain, SW_RESTORE);\r
10821 \r
10822     SetActiveWindow(hwndMain);\r
10823 }\r
10824 \r
10825 /*\r
10826  * Prototypes for animation support routines\r
10827  */\r
10828 static void ScreenSquare(int column, int row, POINT * pt);\r
10829 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10830      POINT frames[], int * nFrames);\r
10831 \r
10832 \r
10833 void\r
10834 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10835 {       // [HGM] atomic: animate blast wave\r
10836         int i;\r
10837 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10838         explodeInfo.fromX = fromX;\r
10839         explodeInfo.fromY = fromY;\r
10840         explodeInfo.toX = toX;\r
10841         explodeInfo.toY = toY;\r
10842         for(i=1; i<nFrames; i++) {\r
10843             explodeInfo.radius = (i*180)/(nFrames-1);\r
10844             DrawPosition(FALSE, NULL);\r
10845             Sleep(appData.animSpeed);\r
10846         }\r
10847         explodeInfo.radius = 0;\r
10848         DrawPosition(TRUE, NULL);\r
10849 }\r
10850 \r
10851 #define kFactor 4\r
10852 \r
10853 void\r
10854 AnimateMove(board, fromX, fromY, toX, toY)\r
10855      Board board;\r
10856      int fromX;\r
10857      int fromY;\r
10858      int toX;\r
10859      int toY;\r
10860 {\r
10861   ChessSquare piece;\r
10862   POINT start, finish, mid;\r
10863   POINT frames[kFactor * 2 + 1];\r
10864   int nFrames, n;\r
10865 \r
10866   if (!appData.animate) return;\r
10867   if (doingSizing) return;\r
10868   if (fromY < 0 || fromX < 0) return;\r
10869   piece = board[fromY][fromX];\r
10870   if (piece >= EmptySquare) return;\r
10871 \r
10872   ScreenSquare(fromX, fromY, &start);\r
10873   ScreenSquare(toX, toY, &finish);\r
10874 \r
10875   /* All pieces except knights move in straight line */\r
10876   if (piece != WhiteKnight && piece != BlackKnight) {\r
10877     mid.x = start.x + (finish.x - start.x) / 2;\r
10878     mid.y = start.y + (finish.y - start.y) / 2;\r
10879   } else {\r
10880     /* Knight: make diagonal movement then straight */\r
10881     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10882        mid.x = start.x + (finish.x - start.x) / 2;\r
10883        mid.y = finish.y;\r
10884      } else {\r
10885        mid.x = finish.x;\r
10886        mid.y = start.y + (finish.y - start.y) / 2;\r
10887      }\r
10888   }\r
10889   \r
10890   /* Don't use as many frames for very short moves */\r
10891   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10892     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10893   else\r
10894     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10895 \r
10896   animInfo.from.x = fromX;\r
10897   animInfo.from.y = fromY;\r
10898   animInfo.to.x = toX;\r
10899   animInfo.to.y = toY;\r
10900   animInfo.lastpos = start;\r
10901   animInfo.piece = piece;\r
10902   for (n = 0; n < nFrames; n++) {\r
10903     animInfo.pos = frames[n];\r
10904     DrawPosition(FALSE, NULL);\r
10905     animInfo.lastpos = animInfo.pos;\r
10906     Sleep(appData.animSpeed);\r
10907   }\r
10908   animInfo.pos = finish;\r
10909   DrawPosition(FALSE, NULL);\r
10910   animInfo.piece = EmptySquare;\r
10911   if(gameInfo.variant == VariantAtomic && \r
10912      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10913         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10914 }\r
10915 \r
10916 /*      Convert board position to corner of screen rect and color       */\r
10917 \r
10918 static void\r
10919 ScreenSquare(column, row, pt)\r
10920      int column; int row; POINT * pt;\r
10921 {\r
10922   if (flipView) {\r
10923     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10924     pt->y = lineGap + row * (squareSize + lineGap);\r
10925   } else {\r
10926     pt->x = lineGap + column * (squareSize + lineGap);\r
10927     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10928   }\r
10929 }\r
10930 \r
10931 /*      Generate a series of frame coords from start->mid->finish.\r
10932         The movement rate doubles until the half way point is\r
10933         reached, then halves back down to the final destination,\r
10934         which gives a nice slow in/out effect. The algorithmn\r
10935         may seem to generate too many intermediates for short\r
10936         moves, but remember that the purpose is to attract the\r
10937         viewers attention to the piece about to be moved and\r
10938         then to where it ends up. Too few frames would be less\r
10939         noticeable.                                             */\r
10940 \r
10941 static void\r
10942 Tween(start, mid, finish, factor, frames, nFrames)\r
10943      POINT * start; POINT * mid;\r
10944      POINT * finish; int factor;\r
10945      POINT frames[]; int * nFrames;\r
10946 {\r
10947   int n, fraction = 1, count = 0;\r
10948 \r
10949   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10950   for (n = 0; n < factor; n++)\r
10951     fraction *= 2;\r
10952   for (n = 0; n < factor; n++) {\r
10953     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10954     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10955     count ++;\r
10956     fraction = fraction / 2;\r
10957   }\r
10958   \r
10959   /* Midpoint */\r
10960   frames[count] = *mid;\r
10961   count ++;\r
10962   \r
10963   /* Slow out, stepping 1/2, then 1/4, ... */\r
10964   fraction = 2;\r
10965   for (n = 0; n < factor; n++) {\r
10966     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10967     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10968     count ++;\r
10969     fraction = fraction * 2;\r
10970   }\r
10971   *nFrames = count;\r
10972 }\r
10973 \r
10974 void\r
10975 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10976 {\r
10977 #if 0\r
10978     char buf[256];\r
10979 \r
10980     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10981         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10982 \r
10983     OutputDebugString( buf );\r
10984 #endif\r
10985 \r
10986     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10987 \r
10988     EvalGraphSet( first, last, current, pvInfoList );\r
10989 }\r
10990 \r
10991 void SetProgramStats( FrontEndProgramStats * stats )\r
10992 {\r
10993 #if 0\r
10994     char buf[1024];\r
10995 \r
10996     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10997         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10998 \r
10999     OutputDebugString( buf );\r
11000 #endif\r
11001 \r
11002     EngineOutputUpdate( stats );\r
11003 }\r