added a chat window to keep track of multiple conversations
[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 extern HANDLE chatHandle[];\r
104 extern int ics_type;\r
105 \r
106 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
107 VOID NewVariantPopup(HWND hwnd);\r
108 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
109                    /*char*/int promoChar));\r
110 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P(());\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 BOOLEAN saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 \r
159 BoardSize boardSize;\r
160 BOOLEAN chessProgram;\r
161 static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 static int squareSize, lineGap, minorSize;\r
164 static int winWidth, winHeight, winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 HWND hwndMain = NULL;        /* root window*/\r
185 HWND hwndConsole = NULL;\r
186 BOOLEAN alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
201 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 /* [AS] Support for background textures */\r
214 #define BACK_TEXTURE_MODE_DISABLED      0\r
215 #define BACK_TEXTURE_MODE_PLAIN         1\r
216 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 #define oldDialog (_winmajor < 4)\r
229 #endif\r
230 \r
231 char *defaultTextAttribs[] = \r
232 {\r
233   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
234   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
235   COLOR_NONE\r
236 };\r
237 \r
238 typedef struct {\r
239   char *name;\r
240   int squareSize;\r
241   int lineGap;\r
242   int smallLayout;\r
243   int tinyLayout;\r
244   int cliWidth, cliHeight;\r
245 } SizeInfo;\r
246 \r
247 SizeInfo sizeInfo[] = \r
248 {\r
249   { "tiny",     21, 0, 1, 1, 0, 0 },\r
250   { "teeny",    25, 1, 1, 1, 0, 0 },\r
251   { "dinky",    29, 1, 1, 1, 0, 0 },\r
252   { "petite",   33, 1, 1, 1, 0, 0 },\r
253   { "slim",     37, 2, 1, 0, 0, 0 },\r
254   { "small",    40, 2, 1, 0, 0, 0 },\r
255   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
256   { "middling", 49, 2, 0, 0, 0, 0 },\r
257   { "average",  54, 2, 0, 0, 0, 0 },\r
258   { "moderate", 58, 3, 0, 0, 0, 0 },\r
259   { "medium",   64, 3, 0, 0, 0, 0 },\r
260   { "bulky",    72, 3, 0, 0, 0, 0 },\r
261   { "large",    80, 3, 0, 0, 0, 0 },\r
262   { "big",      87, 3, 0, 0, 0, 0 },\r
263   { "huge",     95, 3, 0, 0, 0, 0 },\r
264   { "giant",    108, 3, 0, 0, 0, 0 },\r
265   { "colossal", 116, 4, 0, 0, 0, 0 },\r
266   { "titanic",  129, 4, 0, 0, 0, 0 },\r
267   { NULL, 0, 0, 0, 0, 0, 0 }\r
268 };\r
269 \r
270 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
271 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
272 {\r
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291 };\r
292 \r
293 MyFont *font[NUM_SIZES][NUM_FONTS];\r
294 \r
295 typedef struct {\r
296   char *label;\r
297   int id;\r
298   HWND hwnd;\r
299   WNDPROC wndproc;\r
300 } MyButtonDesc;\r
301 \r
302 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
303 #define N_BUTTONS 5\r
304 \r
305 MyButtonDesc buttonDesc[N_BUTTONS] =\r
306 {\r
307   {"<<", IDM_ToStart, NULL, NULL},\r
308   {"<", IDM_Backward, NULL, NULL},\r
309   {"P", IDM_Pause, NULL, NULL},\r
310   {">", IDM_Forward, NULL, NULL},\r
311   {">>", IDM_ToEnd, NULL, NULL},\r
312 };\r
313 \r
314 int tinyLayout = 0, smallLayout = 0;\r
315 #define MENU_BAR_ITEMS 7\r
316 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
317   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
318   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
319 };\r
320 \r
321 \r
322 MySound sounds[(int)NSoundClasses];\r
323 MyTextAttribs textAttribs[(int)NColorClasses];\r
324 \r
325 MyColorizeAttribs colorizeAttribs[] = {\r
326   { (COLORREF)0, 0, "Shout Text" },\r
327   { (COLORREF)0, 0, "SShout/CShout" },\r
328   { (COLORREF)0, 0, "Channel 1 Text" },\r
329   { (COLORREF)0, 0, "Channel Text" },\r
330   { (COLORREF)0, 0, "Kibitz Text" },\r
331   { (COLORREF)0, 0, "Tell Text" },\r
332   { (COLORREF)0, 0, "Challenge Text" },\r
333   { (COLORREF)0, 0, "Request Text" },\r
334   { (COLORREF)0, 0, "Seek Text" },\r
335   { (COLORREF)0, 0, "Normal Text" },\r
336   { (COLORREF)0, 0, "None" }\r
337 };\r
338 \r
339 \r
340 \r
341 static char *commentTitle;\r
342 static char *commentText;\r
343 static int commentIndex;\r
344 static Boolean editComment = FALSE;\r
345 HWND commentDialog = NULL;\r
346 BOOLEAN commentDialogUp = FALSE;\r
347 static int commentX, commentY, commentH, commentW;\r
348 \r
349 static char *analysisTitle;\r
350 static char *analysisText;\r
351 HWND analysisDialog = NULL;\r
352 BOOLEAN analysisDialogUp = FALSE;\r
353 static int analysisX, analysisY, analysisH, analysisW;\r
354 \r
355 char errorTitle[MSG_SIZ];\r
356 char errorMessage[2*MSG_SIZ];\r
357 HWND errorDialog = NULL;\r
358 BOOLEAN moveErrorMessageUp = FALSE;\r
359 BOOLEAN consoleEcho = TRUE;\r
360 CHARFORMAT consoleCF;\r
361 COLORREF consoleBackgroundColor;\r
362 \r
363 char *programVersion;\r
364 \r
365 #define CPReal 1\r
366 #define CPComm 2\r
367 #define CPSock 3\r
368 #define CPRcmd 4\r
369 typedef int CPKind;\r
370 \r
371 typedef struct {\r
372   CPKind kind;\r
373   HANDLE hProcess;\r
374   DWORD pid;\r
375   HANDLE hTo;\r
376   HANDLE hFrom;\r
377   SOCKET sock;\r
378   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
379 } ChildProc;\r
380 \r
381 #define INPUT_SOURCE_BUF_SIZE 4096\r
382 \r
383 typedef struct _InputSource {\r
384   CPKind kind;\r
385   HANDLE hFile;\r
386   SOCKET sock;\r
387   int lineByLine;\r
388   HANDLE hThread;\r
389   DWORD id;\r
390   char buf[INPUT_SOURCE_BUF_SIZE];\r
391   char *next;\r
392   DWORD count;\r
393   int error;\r
394   InputCallback func;\r
395   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
396   VOIDSTAR closure;\r
397 } InputSource;\r
398 \r
399 InputSource *consoleInputSource;\r
400 \r
401 DCB dcb;\r
402 \r
403 /* forward */\r
404 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
405 VOID ConsoleCreate();\r
406 LRESULT CALLBACK\r
407   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
408 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
409 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
410 VOID ParseCommSettings(char *arg, DCB *dcb);\r
411 LRESULT CALLBACK\r
412   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
413 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
414 void ParseIcsTextMenu(char *icsTextMenuString);\r
415 VOID PopUpMoveDialog(char firstchar);\r
416 VOID PopUpNameDialog(char firstchar);\r
417 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
418 \r
419 /* [AS] */\r
420 int NewGameFRC();\r
421 int GameListOptions();\r
422 \r
423 HWND moveHistoryDialog = NULL;\r
424 BOOLEAN moveHistoryDialogUp = FALSE;\r
425 \r
426 WindowPlacement wpMoveHistory;\r
427 \r
428 HWND evalGraphDialog = NULL;\r
429 BOOLEAN evalGraphDialogUp = FALSE;\r
430 \r
431 WindowPlacement wpEvalGraph;\r
432 \r
433 HWND engineOutputDialog = NULL;\r
434 BOOLEAN engineOutputDialogUp = FALSE;\r
435 \r
436 WindowPlacement wpEngineOutput;\r
437 WindowPlacement wpGameList;\r
438 WindowPlacement wpConsole;\r
439 \r
440 VOID MoveHistoryPopUp();\r
441 VOID MoveHistoryPopDown();\r
442 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 BOOL MoveHistoryIsUp();\r
444 \r
445 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
446 VOID EvalGraphPopUp();\r
447 VOID EvalGraphPopDown();\r
448 BOOL EvalGraphIsUp();\r
449 \r
450 VOID EngineOutputPopUp();\r
451 VOID EngineOutputPopDown();\r
452 BOOL EngineOutputIsUp();\r
453 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
454 \r
455 VOID EngineOptionsPopup(); // [HGM] settings\r
456 \r
457 VOID GothicPopUp(char *title, VariantClass variant);\r
458 /*\r
459  * Setting "frozen" should disable all user input other than deleting\r
460  * the window.  We do this while engines are initializing themselves.\r
461  */\r
462 static int frozen = 0;\r
463 static int oldMenuItemState[MENU_BAR_ITEMS];\r
464 void FreezeUI()\r
465 {\r
466   HMENU hmenu;\r
467   int i;\r
468 \r
469   if (frozen) return;\r
470   frozen = 1;\r
471   hmenu = GetMenu(hwndMain);\r
472   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
473     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
474   }\r
475   DrawMenuBar(hwndMain);\r
476 }\r
477 \r
478 /* Undo a FreezeUI */\r
479 void ThawUI()\r
480 {\r
481   HMENU hmenu;\r
482   int i;\r
483 \r
484   if (!frozen) return;\r
485   frozen = 0;\r
486   hmenu = GetMenu(hwndMain);\r
487   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
488     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
489   }\r
490   DrawMenuBar(hwndMain);\r
491 }\r
492 \r
493 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
494 \r
495 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
496 #ifdef JAWS\r
497 #include "jaws.c"\r
498 #else\r
499 #define JAWS_INIT\r
500 #define JAWS_ALT_INTERCEPT\r
501 #define JAWS_KB_NAVIGATION\r
502 #define JAWS_MENU_ITEMS\r
503 #define JAWS_SILENCE\r
504 #define JAWS_REPLAY\r
505 #define JAWS_ACCEL\r
506 #define JAWS_COPYRIGHT\r
507 #define JAWS_DELETE(X) X\r
508 #define SAYMACHINEMOVE()\r
509 #define SAY(X)\r
510 #endif\r
511 \r
512 /*---------------------------------------------------------------------------*\\r
513  *\r
514  * WinMain\r
515  *\r
516 \*---------------------------------------------------------------------------*/\r
517 \r
518 int APIENTRY\r
519 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
520         LPSTR lpCmdLine, int nCmdShow)\r
521 {\r
522   MSG msg;\r
523   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
524 //  INITCOMMONCONTROLSEX ex;\r
525 \r
526   debugFP = stderr;\r
527 \r
528   LoadLibrary("RICHED32.DLL");\r
529   consoleCF.cbSize = sizeof(CHARFORMAT);\r
530 \r
531   if (!InitApplication(hInstance)) {\r
532     return (FALSE);\r
533   }\r
534   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
535     return (FALSE);\r
536   }\r
537 \r
538   JAWS_INIT\r
539 \r
540 //  InitCommonControlsEx(&ex);\r
541   InitCommonControls();\r
542 \r
543   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
544   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
545   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
546 \r
547   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
548 \r
549   while (GetMessage(&msg, /* message structure */\r
550                     NULL, /* handle of window receiving the message */\r
551                     0,    /* lowest message to examine */\r
552                     0))   /* highest message to examine */\r
553     {\r
554 \r
555       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
556         // [HGM] navigate: switch between all windows with tab\r
557         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
558         int i, currentElement = 0;\r
559 \r
560         // first determine what element of the chain we come from (if any)\r
561         if(appData.icsActive) {\r
562             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
563             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
564         }\r
565         if(engineOutputDialog && EngineOutputIsUp()) {\r
566             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
567             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
568         }\r
569         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
570             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
571         }\r
572         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
573         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
574         if(msg.hwnd == e1)                 currentElement = 2; else\r
575         if(msg.hwnd == e2)                 currentElement = 3; else\r
576         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
577         if(msg.hwnd == mh)                currentElement = 4; else\r
578         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
579         if(msg.hwnd == hText)  currentElement = 5; else\r
580         if(msg.hwnd == hInput) currentElement = 6; else\r
581         for (i = 0; i < N_BUTTONS; i++) {\r
582             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
583         }\r
584 \r
585         // determine where to go to\r
586         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
587           do {\r
588             currentElement = (currentElement + direction) % 7;\r
589             switch(currentElement) {\r
590                 case 0:\r
591                   h = hwndMain; break; // passing this case always makes the loop exit\r
592                 case 1:\r
593                   h = buttonDesc[0].hwnd; break; // could be NULL\r
594                 case 2:\r
595                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
596                   h = e1; break;\r
597                 case 3:\r
598                   if(!EngineOutputIsUp()) continue;\r
599                   h = e2; break;\r
600                 case 4:\r
601                   if(!MoveHistoryIsUp()) continue;\r
602                   h = mh; break;\r
603 //              case 6: // input to eval graph does not seem to get here!\r
604 //                if(!EvalGraphIsUp()) continue;\r
605 //                h = evalGraphDialog; break;\r
606                 case 5:\r
607                   if(!appData.icsActive) continue;\r
608                   SAY("display");\r
609                   h = hText; break;\r
610                 case 6:\r
611                   if(!appData.icsActive) continue;\r
612                   SAY("input");\r
613                   h = hInput; break;\r
614             }\r
615           } while(h == 0);\r
616 \r
617           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
618           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
619           SetFocus(h);\r
620 \r
621           continue; // this message now has been processed\r
622         }\r
623       }\r
624 \r
625       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
626           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
627           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
628           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
629           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
630           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
631           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
632           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
633           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
634           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
635         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
636         for(i=0; i<MAX_CHAT; i++) \r
637             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
638                 done = 1; break;\r
639         }\r
640         if(done) continue; // [HGM] chat: end patch\r
641         TranslateMessage(&msg); /* Translates virtual key codes */\r
642         DispatchMessage(&msg);  /* Dispatches message to window */\r
643       }\r
644     }\r
645 \r
646 \r
647   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
648 }\r
649 \r
650 /*---------------------------------------------------------------------------*\\r
651  *\r
652  * Initialization functions\r
653  *\r
654 \*---------------------------------------------------------------------------*/\r
655 \r
656 void\r
657 SetUserLogo()\r
658 {   // update user logo if necessary\r
659     static char oldUserName[MSG_SIZ], *curName;\r
660 \r
661     if(appData.autoLogo) {\r
662           curName = UserName();\r
663           if(strcmp(curName, oldUserName)) {\r
664                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
665                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
666                 strcpy(oldUserName, curName);\r
667           }\r
668     }\r
669 }\r
670 \r
671 BOOL\r
672 InitApplication(HINSTANCE hInstance)\r
673 {\r
674   WNDCLASS wc;\r
675 \r
676   /* Fill in window class structure with parameters that describe the */\r
677   /* main window. */\r
678 \r
679   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
680   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
681   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
682   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
683   wc.hInstance     = hInstance;         /* Owner of this class */\r
684   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
685   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
686   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
687   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
688   wc.lpszClassName = szAppName;                 /* Name to register as */\r
689 \r
690   /* Register the window class and return success/failure code. */\r
691   if (!RegisterClass(&wc)) return FALSE;\r
692 \r
693   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
694   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
695   wc.cbClsExtra    = 0;\r
696   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
697   wc.hInstance     = hInstance;\r
698   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
699   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
700   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
701   wc.lpszMenuName  = NULL;\r
702   wc.lpszClassName = szConsoleName;\r
703 \r
704   if (!RegisterClass(&wc)) return FALSE;\r
705   return TRUE;\r
706 }\r
707 \r
708 \r
709 /* Set by InitInstance, used by EnsureOnScreen */\r
710 int screenHeight, screenWidth;\r
711 \r
712 void\r
713 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
714 {\r
715 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
716   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
717   if (*x > screenWidth - 32) *x = 0;\r
718   if (*y > screenHeight - 32) *y = 0;\r
719   if (*x < minX) *x = minX;\r
720   if (*y < minY) *y = minY;\r
721 }\r
722 \r
723 BOOL\r
724 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
725 {\r
726   HWND hwnd; /* Main window handle. */\r
727   int ibs;\r
728   WINDOWPLACEMENT wp;\r
729   char *filepart;\r
730 \r
731   hInst = hInstance;    /* Store instance handle in our global variable */\r
732 \r
733   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
734     *filepart = NULLCHAR;\r
735   } else {\r
736     GetCurrentDirectory(MSG_SIZ, installDir);\r
737   }\r
738   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
739   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
740   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
741   if (appData.debugMode) {\r
742     debugFP = fopen(appData.nameOfDebugFile, "w");\r
743     setbuf(debugFP, NULL);\r
744   }\r
745 \r
746   InitBackEnd1();\r
747 \r
748 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
749 //  InitEngineUCI( installDir, &second );\r
750 \r
751   /* Create a main window for this application instance. */\r
752   hwnd = CreateWindow(szAppName, szTitle,\r
753                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
754                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
755                       NULL, NULL, hInstance, NULL);\r
756   hwndMain = hwnd;\r
757 \r
758   /* If window could not be created, return "failure" */\r
759   if (!hwnd) {\r
760     return (FALSE);\r
761   }\r
762 \r
763   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
764   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
765       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
766 \r
767       if (first.programLogo == NULL && appData.debugMode) {\r
768           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
769       }\r
770   } else if(appData.autoLogo) {\r
771       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
772         char buf[MSG_SIZ];\r
773         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
774         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
775       }\r
776   }\r
777 \r
778   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
779       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
780 \r
781       if (second.programLogo == NULL && appData.debugMode) {\r
782           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
783       }\r
784   } else if(appData.autoLogo) {\r
785       char buf[MSG_SIZ];\r
786       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
787         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
788         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
789       } else\r
790       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
791         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
792         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
793       }\r
794   }\r
795 \r
796   SetUserLogo();\r
797 \r
798   iconWhite = LoadIcon(hInstance, "icon_white");\r
799   iconBlack = LoadIcon(hInstance, "icon_black");\r
800   iconCurrent = iconWhite;\r
801   InitDrawingColors();\r
802   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
803   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
804   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
805     /* Compute window size for each board size, and use the largest\r
806        size that fits on this screen as the default. */\r
807     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
808     if (boardSize == (BoardSize)-1 &&\r
809         winH <= screenHeight\r
810            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
811         && winW <= screenWidth) {\r
812       boardSize = (BoardSize)ibs;\r
813     }\r
814   }\r
815 \r
816   InitDrawingSizes(boardSize, 0);\r
817   InitMenuChecks();\r
818   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
819 \r
820   /* [AS] Load textures if specified */\r
821   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
822   \r
823   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
824       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
825       liteBackTextureMode = appData.liteBackTextureMode;\r
826 \r
827       if (liteBackTexture == NULL && appData.debugMode) {\r
828           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
829       }\r
830   }\r
831   \r
832   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
833       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
834       darkBackTextureMode = appData.darkBackTextureMode;\r
835 \r
836       if (darkBackTexture == NULL && appData.debugMode) {\r
837           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
838       }\r
839   }\r
840 \r
841   mysrandom( (unsigned) time(NULL) );\r
842 \r
843   /* [AS] Restore layout */\r
844   if( wpMoveHistory.visible ) {\r
845       MoveHistoryPopUp();\r
846   }\r
847 \r
848   if( wpEvalGraph.visible ) {\r
849       EvalGraphPopUp();\r
850   }\r
851 \r
852   if( wpEngineOutput.visible ) {\r
853       EngineOutputPopUp();\r
854   }\r
855 \r
856   InitBackEnd2();\r
857 \r
858   /* Make the window visible; update its client area; and return "success" */\r
859   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
860   wp.length = sizeof(WINDOWPLACEMENT);\r
861   wp.flags = 0;\r
862   wp.showCmd = nCmdShow;\r
863   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
864   wp.rcNormalPosition.left = boardX;\r
865   wp.rcNormalPosition.right = boardX + winWidth;\r
866   wp.rcNormalPosition.top = boardY;\r
867   wp.rcNormalPosition.bottom = boardY + winHeight;\r
868   SetWindowPlacement(hwndMain, &wp);\r
869 \r
870   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
871                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
872 \r
873   if (hwndConsole) {\r
874 #if AOT_CONSOLE\r
875     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
876                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
877 #endif\r
878     ShowWindow(hwndConsole, nCmdShow);\r
879   }\r
880   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
881 \r
882   return TRUE;\r
883 \r
884 }\r
885 \r
886 \r
887 typedef enum {\r
888   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
889   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
890   ArgSettingsFilename,\r
891   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
892 } ArgType;\r
893 \r
894 typedef struct {\r
895   char *argName;\r
896   ArgType argType;\r
897   /***\r
898   union {\r
899     String *pString;       // ArgString\r
900     int *pInt;             // ArgInt\r
901     float *pFloat;         // ArgFloat\r
902     Boolean *pBoolean;     // ArgBoolean\r
903     COLORREF *pColor;      // ArgColor\r
904     ColorClass cc;         // ArgAttribs\r
905     String *pFilename;     // ArgFilename\r
906     BoardSize *pBoardSize; // ArgBoardSize\r
907     int whichFont;         // ArgFont\r
908     DCB *pDCB;             // ArgCommSettings\r
909     String *pFilename;     // ArgSettingsFilename\r
910   } argLoc;\r
911   ***/\r
912   LPVOID argLoc;\r
913   BOOL save;\r
914 } ArgDescriptor;\r
915 \r
916 int junk;\r
917 ArgDescriptor argDescriptors[] = {\r
918   /* positional arguments */\r
919   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
920   { "", ArgNone, NULL },\r
921   /* keyword arguments */\r
922   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
923   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
924   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
925   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
926   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
927   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
928   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
929   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
930   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
931   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
932   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
933   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
934   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
935   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
936   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
937   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
938   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
939   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
940     FALSE },\r
941   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
942     FALSE },\r
943   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
944     FALSE },\r
945   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
946   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
947     FALSE },\r
948   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
949   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
950   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
951   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
952   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
953   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
954   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
955   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
956   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
957   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
958   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
959   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
960   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
961   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
962   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
963   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
964   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
965   /*!!bitmapDirectory?*/\r
966   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
967   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
968   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
969   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
970   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
971   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
972   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
973   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
974   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
975   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
976   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
977   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
978   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
979   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
980   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
981   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
982   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
983   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
984   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
985   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
986   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
987   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
988   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
989   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
990   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
991   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
992   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
993   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
994   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
995   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
996   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
997   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
998   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
999   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1000   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1001   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1002   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1003   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1004   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1005   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1006   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1007   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1008   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1009   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1010   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1011   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1012   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1013   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1014   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1015   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1016   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1017   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1018   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1019   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1020   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1021   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1022   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1023   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1024   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1025   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1026   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1027   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1028   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1029   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1030   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1031   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1032   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1033   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1034   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1035   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1036   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1037   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1038   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1039   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1040   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1041   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1042   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1043   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1044   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1045   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1046   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1047   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1048   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1049   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1050   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1051   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1052   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1053   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1054   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1055   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1056   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1057   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1058   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1059   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1060     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1061   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1062   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1063   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1064   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1065   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1066   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1067   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1068   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1069     TRUE }, /* must come after all fonts */\r
1070   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1071   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1072     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1073   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1074   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1075   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1076   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1077   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1078   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1079   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1080   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1081   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1082   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1083   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1084   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1085   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1086   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1087   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1088   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1089   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1090   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1091   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1092   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1093   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1094   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1095   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1096   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1097   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1098   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1099   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1100   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1101 #if 0\r
1102   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1103   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1104 #endif\r
1105   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1106   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1107   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1108   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1109   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1110   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1111   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1112   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1113   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1114   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1115   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1116   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1117   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1118   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1119   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1120   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1121   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1122   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1123   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1124   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1125   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1126   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1127   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1128   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1129   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1130   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1131   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1132   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1133   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1134   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1135   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1136   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1137   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1138   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1139   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1140   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1141   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1142   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1143   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1144   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1145   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1146   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1147   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1148   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1149   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1150   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1151   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1152   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1153   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1154   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1155   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1156   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1157   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1158   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1159   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1160   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1161   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1162   { "highlightLastMove", ArgBoolean,\r
1163     (LPVOID) &appData.highlightLastMove, TRUE },\r
1164   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1165   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1166   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1167   { "highlightDragging", ArgBoolean,\r
1168     (LPVOID) &appData.highlightDragging, TRUE },\r
1169   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1170   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1171   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1172   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1173   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1174   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1175   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1176   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1177   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1178   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1179   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1180   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1181   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1182   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1183   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1184   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1185   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1186   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1187   { "soundShout", ArgFilename,\r
1188     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1189   { "soundSShout", ArgFilename,\r
1190     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1191   { "soundChannel1", ArgFilename,\r
1192     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1193   { "soundChannel", ArgFilename,\r
1194     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1195   { "soundKibitz", ArgFilename,\r
1196     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1197   { "soundTell", ArgFilename,\r
1198     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1199   { "soundChallenge", ArgFilename,\r
1200     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1201   { "soundRequest", ArgFilename,\r
1202     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1203   { "soundSeek", ArgFilename,\r
1204     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1205   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1206   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1207   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1208   { "soundIcsLoss", ArgFilename, \r
1209     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1210   { "soundIcsDraw", ArgFilename, \r
1211     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1212   { "soundIcsUnfinished", ArgFilename, \r
1213     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1214   { "soundIcsAlarm", ArgFilename, \r
1215     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1216   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1217   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1218   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1219   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1220   { "reuseChessPrograms", ArgBoolean,\r
1221     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1222   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1223   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1224   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1225   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1226   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1227   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1228   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1229   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1230   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1231   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1232   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1233   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1234   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1235   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1236   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1237     TRUE },\r
1238   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1239     TRUE },\r
1240   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1241   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1242   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1243   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1244   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1245   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1246   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1247   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1248   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1249   /* [AS] New features */\r
1250   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1251   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1252   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1253   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1254   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1255   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1256   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1257   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1258   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1259   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1260   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1261   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1262   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1263   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1264   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1265   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1266   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1267   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1268   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1269   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1270   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1271   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1272   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1273   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1274   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1275   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1276   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1277   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1278   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1279   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1280   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1281   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1282   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1283   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1284   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1285   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1286   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1287   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1288   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1289   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1290   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1291   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1292   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1293   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1294   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1295   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1296   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1297   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1298   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1299   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1300 \r
1301   /* [HGM] board-size, adjudication and misc. options */\r
1302   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1303   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1304   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1305   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1306   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1307   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1308   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1309   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1310   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1311   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1312   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1313   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1314   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1315   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1316   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1317   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1318   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1319   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1320   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1321   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1322   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1323   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1324   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1325   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1326   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1327   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1328   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1329   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1330   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1331   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1332   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1333   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1334   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1335 \r
1336 #ifdef ZIPPY\r
1337   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1338   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1339   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1340   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1341   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1342   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1343   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1344   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1345   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1346   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1347   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1348   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1349   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1350     FALSE },\r
1351   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1352   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1353   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1354   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1355   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1356   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1357   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1358     FALSE },\r
1359   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1360   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1361   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1362   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1363   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1364   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1365   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1366   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1367   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1368   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1369   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1370   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1371   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1372   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1373   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1374   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1375   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1376   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1377   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1378 #endif\r
1379   /* [HGM] options for broadcasting and time odds */\r
1380   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1381   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1382   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1383   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1384   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1385   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1386   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1387   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1388   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1389   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1390   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1391 \r
1392   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1393   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1394   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1395   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1396   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1397   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1398   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1399   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1400   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1401   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1402   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1403   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1404   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1405   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1406   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1407   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1408   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1409   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1410   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1411   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1412   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1413   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1414   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1415   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1416   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1417   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1418   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1419   /* [AS] Layout stuff */\r
1420   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1421   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1422   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1423   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1424   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1425 \r
1426   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1427   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1428   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1429   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1430   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1431 \r
1432   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1433   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1434   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1435   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1436   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1437 \r
1438   { NULL, ArgNone, NULL, FALSE }\r
1439 };\r
1440 \r
1441 \r
1442 /* Kludge for indirection files on command line */\r
1443 char* lastIndirectionFilename;\r
1444 ArgDescriptor argDescriptorIndirection =\r
1445 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1446 \r
1447 \r
1448 VOID\r
1449 ExitArgError(char *msg, char *badArg)\r
1450 {\r
1451   char buf[MSG_SIZ];\r
1452 \r
1453   sprintf(buf, "%s %s", msg, badArg);\r
1454   DisplayFatalError(buf, 0, 2);\r
1455   exit(2);\r
1456 }\r
1457 \r
1458 /* Command line font name parser.  NULL name means do nothing.\r
1459    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1460    For backward compatibility, syntax without the colon is also\r
1461    accepted, but font names with digits in them won't work in that case.\r
1462 */\r
1463 VOID\r
1464 ParseFontName(char *name, MyFontParams *mfp)\r
1465 {\r
1466   char *p, *q;\r
1467   if (name == NULL) return;\r
1468   p = name;\r
1469   q = strchr(p, ':');\r
1470   if (q) {\r
1471     if (q - p >= sizeof(mfp->faceName))\r
1472       ExitArgError("Font name too long:", name);\r
1473     memcpy(mfp->faceName, p, q - p);\r
1474     mfp->faceName[q - p] = NULLCHAR;\r
1475     p = q + 1;\r
1476   } else {\r
1477     q = mfp->faceName;\r
1478     while (*p && !isdigit(*p)) {\r
1479       *q++ = *p++;\r
1480       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1481         ExitArgError("Font name too long:", name);\r
1482     }\r
1483     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1484     *q = NULLCHAR;\r
1485   }\r
1486   if (!*p) ExitArgError("Font point size missing:", name);\r
1487   mfp->pointSize = (float) atof(p);\r
1488   mfp->bold = (strchr(p, 'b') != NULL);\r
1489   mfp->italic = (strchr(p, 'i') != NULL);\r
1490   mfp->underline = (strchr(p, 'u') != NULL);\r
1491   mfp->strikeout = (strchr(p, 's') != NULL);\r
1492 }\r
1493 \r
1494 /* Color name parser.\r
1495    X version accepts X color names, but this one\r
1496    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1497 COLORREF\r
1498 ParseColorName(char *name)\r
1499 {\r
1500   int red, green, blue, count;\r
1501   char buf[MSG_SIZ];\r
1502 \r
1503   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1504   if (count != 3) {\r
1505     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1506       &red, &green, &blue);\r
1507   }\r
1508   if (count != 3) {\r
1509     sprintf(buf, "Can't parse color name %s", name);\r
1510     DisplayError(buf, 0);\r
1511     return RGB(0, 0, 0);\r
1512   }\r
1513   return PALETTERGB(red, green, blue);\r
1514 }\r
1515 \r
1516 \r
1517 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1518 {\r
1519   char *e = argValue;\r
1520   int eff = 0;\r
1521 \r
1522   while (*e) {\r
1523     if (*e == 'b')      eff |= CFE_BOLD;\r
1524     else if (*e == 'i') eff |= CFE_ITALIC;\r
1525     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1526     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1527     else if (*e == '#' || isdigit(*e)) break;\r
1528     e++;\r
1529   }\r
1530   *effects = eff;\r
1531   *color   = ParseColorName(e);\r
1532 }\r
1533 \r
1534 \r
1535 BoardSize\r
1536 ParseBoardSize(char *name)\r
1537 {\r
1538   BoardSize bs = SizeTiny;\r
1539   while (sizeInfo[bs].name != NULL) {\r
1540     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1541     bs++;\r
1542   }\r
1543   ExitArgError("Unrecognized board size value", name);\r
1544   return bs; /* not reached */\r
1545 }\r
1546 \r
1547 \r
1548 char\r
1549 StringGet(void *getClosure)\r
1550 {\r
1551   char **p = (char **) getClosure;\r
1552   return *((*p)++);\r
1553 }\r
1554 \r
1555 char\r
1556 FileGet(void *getClosure)\r
1557 {\r
1558   int c;\r
1559   FILE* f = (FILE*) getClosure;\r
1560 \r
1561   c = getc(f);\r
1562   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1563   if (c == EOF)\r
1564     return NULLCHAR;\r
1565   else\r
1566     return (char) c;\r
1567 }\r
1568 \r
1569 /* Parse settings file named "name". If file found, return the\r
1570    full name in fullname and return TRUE; else return FALSE */\r
1571 BOOLEAN\r
1572 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1573 {\r
1574   char *dummy;\r
1575   FILE *f;\r
1576   int ok; char buf[MSG_SIZ];\r
1577 \r
1578   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1579   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1580     sprintf(buf, "%s.ini", name);\r
1581     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1582   }\r
1583   if (ok) {\r
1584     f = fopen(fullname, "r");\r
1585     if (f != NULL) {\r
1586       ParseArgs(FileGet, f);\r
1587       fclose(f);\r
1588       return TRUE;\r
1589     }\r
1590   }\r
1591   return FALSE;\r
1592 }\r
1593 \r
1594 VOID\r
1595 ParseArgs(GetFunc get, void *cl)\r
1596 {\r
1597   char argName[ARG_MAX];\r
1598   char argValue[ARG_MAX];\r
1599   ArgDescriptor *ad;\r
1600   char start;\r
1601   char *q;\r
1602   int i, octval;\r
1603   char ch;\r
1604   int posarg = 0;\r
1605 \r
1606   ch = get(cl);\r
1607   for (;;) {\r
1608     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1609     if (ch == NULLCHAR) break;\r
1610     if (ch == ';') {\r
1611       /* Comment to end of line */\r
1612       ch = get(cl);\r
1613       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1614       continue;\r
1615     } else if (ch == '/' || ch == '-') {\r
1616       /* Switch */\r
1617       q = argName;\r
1618       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1619              ch != '\n' && ch != '\t') {\r
1620         *q++ = ch;\r
1621         ch = get(cl);\r
1622       }\r
1623       *q = NULLCHAR;\r
1624 \r
1625       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1626         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1627 \r
1628       if (ad->argName == NULL)\r
1629         ExitArgError("Unrecognized argument", argName);\r
1630 \r
1631     } else if (ch == '@') {\r
1632       /* Indirection file */\r
1633       ad = &argDescriptorIndirection;\r
1634       ch = get(cl);\r
1635     } else {\r
1636       /* Positional argument */\r
1637       ad = &argDescriptors[posarg++];\r
1638       strcpy(argName, ad->argName);\r
1639     }\r
1640 \r
1641     if (ad->argType == ArgTrue) {\r
1642       *(Boolean *) ad->argLoc = TRUE;\r
1643       continue;\r
1644     }\r
1645     if (ad->argType == ArgFalse) {\r
1646       *(Boolean *) ad->argLoc = FALSE;\r
1647       continue;\r
1648     }\r
1649 \r
1650     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1651     if (ch == NULLCHAR || ch == '\n') {\r
1652       ExitArgError("No value provided for argument", argName);\r
1653     }\r
1654     q = argValue;\r
1655     if (ch == '{') {\r
1656       // Quoting with { }.  No characters have to (or can) be escaped.\r
1657       // Thus the string cannot contain a '}' character.\r
1658       start = ch;\r
1659       ch = get(cl);\r
1660       while (start) {\r
1661         switch (ch) {\r
1662         case NULLCHAR:\r
1663           start = NULLCHAR;\r
1664           break;\r
1665           \r
1666         case '}':\r
1667           ch = get(cl);\r
1668           start = NULLCHAR;\r
1669           break;\r
1670 \r
1671         default:\r
1672           *q++ = ch;\r
1673           ch = get(cl);\r
1674           break;\r
1675         }\r
1676       }   \r
1677     } else if (ch == '\'' || ch == '"') {\r
1678       // Quoting with ' ' or " ", with \ as escape character.\r
1679       // Inconvenient for long strings that may contain Windows filenames.\r
1680       start = ch;\r
1681       ch = get(cl);\r
1682       while (start) {\r
1683         switch (ch) {\r
1684         case NULLCHAR:\r
1685           start = NULLCHAR;\r
1686           break;\r
1687 \r
1688         default:\r
1689         not_special:\r
1690           *q++ = ch;\r
1691           ch = get(cl);\r
1692           break;\r
1693 \r
1694         case '\'':\r
1695         case '\"':\r
1696           if (ch == start) {\r
1697             ch = get(cl);\r
1698             start = NULLCHAR;\r
1699             break;\r
1700           } else {\r
1701             goto not_special;\r
1702           }\r
1703 \r
1704         case '\\':\r
1705           if (ad->argType == ArgFilename\r
1706               || ad->argType == ArgSettingsFilename) {\r
1707               goto not_special;\r
1708           }\r
1709           ch = get(cl);\r
1710           switch (ch) {\r
1711           case NULLCHAR:\r
1712             ExitArgError("Incomplete \\ escape in value for", argName);\r
1713             break;\r
1714           case 'n':\r
1715             *q++ = '\n';\r
1716             ch = get(cl);\r
1717             break;\r
1718           case 'r':\r
1719             *q++ = '\r';\r
1720             ch = get(cl);\r
1721             break;\r
1722           case 't':\r
1723             *q++ = '\t';\r
1724             ch = get(cl);\r
1725             break;\r
1726           case 'b':\r
1727             *q++ = '\b';\r
1728             ch = get(cl);\r
1729             break;\r
1730           case 'f':\r
1731             *q++ = '\f';\r
1732             ch = get(cl);\r
1733             break;\r
1734           default:\r
1735             octval = 0;\r
1736             for (i = 0; i < 3; i++) {\r
1737               if (ch >= '0' && ch <= '7') {\r
1738                 octval = octval*8 + (ch - '0');\r
1739                 ch = get(cl);\r
1740               } else {\r
1741                 break;\r
1742               }\r
1743             }\r
1744             if (i > 0) {\r
1745               *q++ = (char) octval;\r
1746             } else {\r
1747               *q++ = ch;\r
1748               ch = get(cl);\r
1749             }\r
1750             break;\r
1751           }\r
1752           break;\r
1753         }\r
1754       }\r
1755     } else {\r
1756       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1757         *q++ = ch;\r
1758         ch = get(cl);\r
1759       }\r
1760     }\r
1761     *q = NULLCHAR;\r
1762 \r
1763     switch (ad->argType) {\r
1764     case ArgInt:\r
1765       *(int *) ad->argLoc = atoi(argValue);\r
1766       break;\r
1767 \r
1768     case ArgX:\r
1769       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1770       break;\r
1771 \r
1772     case ArgY:\r
1773       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1774       break;\r
1775 \r
1776     case ArgZ:\r
1777       *(int *) ad->argLoc = atoi(argValue);\r
1778       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1779       break;\r
1780 \r
1781     case ArgFloat:\r
1782       *(float *) ad->argLoc = (float) atof(argValue);\r
1783       break;\r
1784 \r
1785     case ArgString:\r
1786     case ArgFilename:\r
1787       *(char **) ad->argLoc = strdup(argValue);\r
1788       break;\r
1789 \r
1790     case ArgSettingsFilename:\r
1791       {\r
1792         char fullname[MSG_SIZ];\r
1793         if (ParseSettingsFile(argValue, fullname)) {\r
1794           if (ad->argLoc != NULL) {\r
1795             *(char **) ad->argLoc = strdup(fullname);\r
1796           }\r
1797         } else {\r
1798           if (ad->argLoc != NULL) {\r
1799           } else {\r
1800             ExitArgError("Failed to open indirection file", argValue);\r
1801           }\r
1802         }\r
1803       }\r
1804       break;\r
1805 \r
1806     case ArgBoolean:\r
1807       switch (argValue[0]) {\r
1808       case 't':\r
1809       case 'T':\r
1810         *(Boolean *) ad->argLoc = TRUE;\r
1811         break;\r
1812       case 'f':\r
1813       case 'F':\r
1814         *(Boolean *) ad->argLoc = FALSE;\r
1815         break;\r
1816       default:\r
1817         ExitArgError("Unrecognized boolean argument value", argValue);\r
1818         break;\r
1819       }\r
1820       break;\r
1821 \r
1822     case ArgColor:\r
1823       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1824       break;\r
1825 \r
1826     case ArgAttribs: {\r
1827       ColorClass cc = (ColorClass)ad->argLoc;\r
1828       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1829       }\r
1830       break;\r
1831       \r
1832     case ArgBoardSize:\r
1833       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1834       break;\r
1835 \r
1836     case ArgFont:\r
1837       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1838       break;\r
1839 \r
1840     case ArgCommSettings:\r
1841       ParseCommSettings(argValue, &dcb);\r
1842       break;\r
1843 \r
1844     case ArgNone:\r
1845       ExitArgError("Unrecognized argument", argValue);\r
1846       break;\r
1847     case ArgTrue:\r
1848     case ArgFalse: ;\r
1849     }\r
1850   }\r
1851 }\r
1852 \r
1853 VOID\r
1854 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1855 {\r
1856   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1857   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1858   DeleteDC(hdc);\r
1859   lf->lfWidth = 0;\r
1860   lf->lfEscapement = 0;\r
1861   lf->lfOrientation = 0;\r
1862   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1863   lf->lfItalic = mfp->italic;\r
1864   lf->lfUnderline = mfp->underline;\r
1865   lf->lfStrikeOut = mfp->strikeout;\r
1866   lf->lfCharSet = DEFAULT_CHARSET;\r
1867   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1868   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1869   lf->lfQuality = DEFAULT_QUALITY;\r
1870   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1871   strcpy(lf->lfFaceName, mfp->faceName);\r
1872 }\r
1873 \r
1874 VOID\r
1875 CreateFontInMF(MyFont *mf)\r
1876 {\r
1877   LFfromMFP(&mf->lf, &mf->mfp);\r
1878   if (mf->hf) DeleteObject(mf->hf);\r
1879   mf->hf = CreateFontIndirect(&mf->lf);\r
1880 }\r
1881 \r
1882 VOID\r
1883 SetDefaultTextAttribs()\r
1884 {\r
1885   ColorClass cc;\r
1886   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1887     ParseAttribs(&textAttribs[cc].color, \r
1888                  &textAttribs[cc].effects, \r
1889                  defaultTextAttribs[cc]);\r
1890   }\r
1891 }\r
1892 \r
1893 VOID\r
1894 SetDefaultSounds()\r
1895 {\r
1896   ColorClass cc;\r
1897   SoundClass sc;\r
1898   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1899     textAttribs[cc].sound.name = strdup("");\r
1900     textAttribs[cc].sound.data = NULL;\r
1901   }\r
1902   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1903     sounds[sc].name = strdup("");\r
1904     sounds[sc].data = NULL;\r
1905   }\r
1906   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1907 }\r
1908 \r
1909 VOID\r
1910 LoadAllSounds()\r
1911 {\r
1912   ColorClass cc;\r
1913   SoundClass sc;\r
1914   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1915     MyLoadSound(&textAttribs[cc].sound);\r
1916   }\r
1917   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1918     MyLoadSound(&sounds[sc]);\r
1919   }\r
1920 }\r
1921 \r
1922 VOID\r
1923 InitAppData(LPSTR lpCmdLine)\r
1924 {\r
1925   int i, j;\r
1926   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1927   char *dummy, *p;\r
1928 \r
1929   programName = szAppName;\r
1930 \r
1931   /* Initialize to defaults */\r
1932   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1933   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1934   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1935   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1936   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1937   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1938   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1939   SetDefaultTextAttribs();\r
1940   SetDefaultSounds();\r
1941   appData.movesPerSession = MOVES_PER_SESSION;\r
1942   appData.initString = INIT_STRING;\r
1943   appData.secondInitString = INIT_STRING;\r
1944   appData.firstComputerString = COMPUTER_STRING;\r
1945   appData.secondComputerString = COMPUTER_STRING;\r
1946   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1947   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1948   appData.firstPlaysBlack = FALSE;\r
1949   appData.noChessProgram = FALSE;\r
1950   chessProgram = FALSE;\r
1951   appData.firstHost = FIRST_HOST;\r
1952   appData.secondHost = SECOND_HOST;\r
1953   appData.firstDirectory = FIRST_DIRECTORY;\r
1954   appData.secondDirectory = SECOND_DIRECTORY;\r
1955   appData.bitmapDirectory = "";\r
1956   appData.remoteShell = REMOTE_SHELL;\r
1957   appData.remoteUser = "";\r
1958   appData.timeDelay = TIME_DELAY;\r
1959   appData.timeControl = TIME_CONTROL;\r
1960   appData.timeIncrement = TIME_INCREMENT;\r
1961   appData.icsActive = FALSE;\r
1962   appData.icsHost = "";\r
1963   appData.icsPort = ICS_PORT;\r
1964   appData.icsCommPort = ICS_COMM_PORT;\r
1965   appData.icsLogon = ICS_LOGON;\r
1966   appData.icsHelper = "";\r
1967   appData.useTelnet = FALSE;\r
1968   appData.telnetProgram = TELNET_PROGRAM;\r
1969   appData.gateway = "";\r
1970   appData.loadGameFile = "";\r
1971   appData.loadGameIndex = 0;\r
1972   appData.saveGameFile = "";\r
1973   appData.autoSaveGames = FALSE;\r
1974   appData.loadPositionFile = "";\r
1975   appData.loadPositionIndex = 1;\r
1976   appData.savePositionFile = "";\r
1977   appData.matchMode = FALSE;\r
1978   appData.matchGames = 0;\r
1979   appData.monoMode = FALSE;\r
1980   appData.debugMode = FALSE;\r
1981   appData.clockMode = TRUE;\r
1982   boardSize = (BoardSize) -1; /* determine by screen size */\r
1983   appData.Iconic = FALSE; /*unused*/\r
1984   appData.searchTime = "";\r
1985   appData.searchDepth = 0;\r
1986   appData.showCoords = FALSE;\r
1987   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1988   appData.autoCallFlag = FALSE;\r
1989   appData.flipView = FALSE;\r
1990   appData.autoFlipView = TRUE;\r
1991   appData.cmailGameName = "";\r
1992   appData.alwaysPromoteToQueen = FALSE;\r
1993   appData.oldSaveStyle = FALSE;\r
1994   appData.quietPlay = FALSE;\r
1995   appData.showThinking = FALSE;\r
1996   appData.ponderNextMove = TRUE;\r
1997   appData.periodicUpdates = TRUE;\r
1998   appData.popupExitMessage = TRUE;\r
1999   appData.popupMoveErrors = FALSE;\r
2000   appData.autoObserve = FALSE;\r
2001   appData.autoComment = FALSE;\r
2002   appData.animate = TRUE;\r
2003   appData.animSpeed = 10;\r
2004   appData.animateDragging = TRUE;\r
2005   appData.highlightLastMove = TRUE;\r
2006   appData.getMoveList = TRUE;\r
2007   appData.testLegality = TRUE;\r
2008   appData.premove = TRUE;\r
2009   appData.premoveWhite = FALSE;\r
2010   appData.premoveWhiteText = "";\r
2011   appData.premoveBlack = FALSE;\r
2012   appData.premoveBlackText = "";\r
2013   appData.icsAlarm = TRUE;\r
2014   appData.icsAlarmTime = 5000;\r
2015   appData.autoRaiseBoard = TRUE;\r
2016   appData.localLineEditing = TRUE;\r
2017   appData.colorize = TRUE;\r
2018   appData.reuseFirst = TRUE;\r
2019   appData.reuseSecond = TRUE;\r
2020   appData.blindfold = FALSE;\r
2021   appData.icsEngineAnalyze = FALSE;\r
2022   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2023   dcb.DCBlength = sizeof(DCB);\r
2024   dcb.BaudRate = 9600;\r
2025   dcb.fBinary = TRUE;\r
2026   dcb.fParity = FALSE;\r
2027   dcb.fOutxCtsFlow = FALSE;\r
2028   dcb.fOutxDsrFlow = FALSE;\r
2029   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2030   dcb.fDsrSensitivity = FALSE;\r
2031   dcb.fTXContinueOnXoff = TRUE;\r
2032   dcb.fOutX = FALSE;\r
2033   dcb.fInX = FALSE;\r
2034   dcb.fNull = FALSE;\r
2035   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2036   dcb.fAbortOnError = FALSE;\r
2037   dcb.ByteSize = 7;\r
2038   dcb.Parity = SPACEPARITY;\r
2039   dcb.StopBits = ONESTOPBIT;\r
2040   settingsFileName = SETTINGS_FILE;\r
2041   saveSettingsOnExit = TRUE;\r
2042   boardX = CW_USEDEFAULT;\r
2043   boardY = CW_USEDEFAULT;\r
2044   analysisX = CW_USEDEFAULT; \r
2045   analysisY = CW_USEDEFAULT; \r
2046   analysisW = CW_USEDEFAULT;\r
2047   analysisH = CW_USEDEFAULT;\r
2048   commentX = CW_USEDEFAULT; \r
2049   commentY = CW_USEDEFAULT; \r
2050   commentW = CW_USEDEFAULT;\r
2051   commentH = CW_USEDEFAULT;\r
2052   editTagsX = CW_USEDEFAULT; \r
2053   editTagsY = CW_USEDEFAULT; \r
2054   editTagsW = CW_USEDEFAULT;\r
2055   editTagsH = CW_USEDEFAULT;\r
2056   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2057   icsNames = ICS_NAMES;\r
2058   firstChessProgramNames = FCP_NAMES;\r
2059   secondChessProgramNames = SCP_NAMES;\r
2060   appData.initialMode = "";\r
2061   appData.variant = "normal";\r
2062   appData.firstProtocolVersion = PROTOVER;\r
2063   appData.secondProtocolVersion = PROTOVER;\r
2064   appData.showButtonBar = TRUE;\r
2065 \r
2066    /* [AS] New properties (see comments in header file) */\r
2067   appData.firstScoreIsAbsolute = FALSE;\r
2068   appData.secondScoreIsAbsolute = FALSE;\r
2069   appData.saveExtendedInfoInPGN = FALSE;\r
2070   appData.hideThinkingFromHuman = FALSE;\r
2071   appData.liteBackTextureFile = "";\r
2072   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2073   appData.darkBackTextureFile = "";\r
2074   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2075   appData.renderPiecesWithFont = "";\r
2076   appData.fontToPieceTable = "";\r
2077   appData.fontBackColorWhite = 0;\r
2078   appData.fontForeColorWhite = 0;\r
2079   appData.fontBackColorBlack = 0;\r
2080   appData.fontForeColorBlack = 0;\r
2081   appData.fontPieceSize = 80;\r
2082   appData.overrideLineGap = 1;\r
2083   appData.adjudicateLossThreshold = 0;\r
2084   appData.delayBeforeQuit = 0;\r
2085   appData.delayAfterQuit = 0;\r
2086   appData.nameOfDebugFile = "winboard.debug";\r
2087   appData.pgnEventHeader = "Computer Chess Game";\r
2088   appData.defaultFrcPosition = -1;\r
2089   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2090   appData.saveOutOfBookInfo = TRUE;\r
2091   appData.showEvalInMoveHistory = TRUE;\r
2092   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2093   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2094   appData.highlightMoveWithArrow = FALSE;\r
2095   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2096   appData.useStickyWindows = TRUE;\r
2097   appData.adjudicateDrawMoves = 0;\r
2098   appData.autoDisplayComment = TRUE;\r
2099   appData.autoDisplayTags = TRUE;\r
2100   appData.firstIsUCI = FALSE;\r
2101   appData.secondIsUCI = FALSE;\r
2102   appData.firstHasOwnBookUCI = TRUE;\r
2103   appData.secondHasOwnBookUCI = TRUE;\r
2104   appData.polyglotDir = "";\r
2105   appData.usePolyglotBook = FALSE;\r
2106   appData.polyglotBook = "";\r
2107   appData.defaultHashSize = 64;\r
2108   appData.defaultCacheSizeEGTB = 4;\r
2109   appData.defaultPathEGTB = "c:\\egtb";\r
2110   appData.firstOptions = "";\r
2111   appData.secondOptions = "";\r
2112 \r
2113   InitWindowPlacement( &wpGameList );\r
2114   InitWindowPlacement( &wpMoveHistory );\r
2115   InitWindowPlacement( &wpEvalGraph );\r
2116   InitWindowPlacement( &wpEngineOutput );\r
2117   InitWindowPlacement( &wpConsole );\r
2118 \r
2119   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2120   appData.NrFiles      = -1;\r
2121   appData.NrRanks      = -1;\r
2122   appData.holdingsSize = -1;\r
2123   appData.testClaims   = FALSE;\r
2124   appData.checkMates   = FALSE;\r
2125   appData.materialDraws= FALSE;\r
2126   appData.trivialDraws = FALSE;\r
2127   appData.ruleMoves    = 51;\r
2128   appData.drawRepeats  = 6;\r
2129   appData.matchPause   = 10000;\r
2130   appData.alphaRank    = FALSE;\r
2131   appData.allWhite     = FALSE;\r
2132   appData.upsideDown   = FALSE;\r
2133   appData.serverPause  = 15;\r
2134   appData.serverMovesName   = NULL;\r
2135   appData.suppressLoadMoves = FALSE;\r
2136   appData.firstTimeOdds  = 1;\r
2137   appData.secondTimeOdds = 1;\r
2138   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2139   appData.secondAccumulateTC = 1;\r
2140   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2141   appData.secondNPS = -1;\r
2142   appData.engineComments = 1;\r
2143   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2144   appData.egtFormats = "";\r
2145 \r
2146 #ifdef ZIPPY\r
2147   appData.zippyTalk = ZIPPY_TALK;\r
2148   appData.zippyPlay = ZIPPY_PLAY;\r
2149   appData.zippyLines = ZIPPY_LINES;\r
2150   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2151   appData.zippyPassword = ZIPPY_PASSWORD;\r
2152   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2153   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2154   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2155   appData.zippyUseI = ZIPPY_USE_I;\r
2156   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2157   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2158   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2159   appData.zippyGameStart = ZIPPY_GAME_START;\r
2160   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2161   appData.zippyAbort = ZIPPY_ABORT;\r
2162   appData.zippyVariants = ZIPPY_VARIANTS;\r
2163   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2164   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2165 #endif\r
2166 \r
2167   /* Point font array elements to structures and\r
2168      parse default font names */\r
2169   for (i=0; i<NUM_FONTS; i++) {\r
2170     for (j=0; j<NUM_SIZES; j++) {\r
2171       font[j][i] = &fontRec[j][i];\r
2172       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2173     }\r
2174   }\r
2175   \r
2176   /* Parse default settings file if any */\r
2177   if (ParseSettingsFile(settingsFileName, buf)) {\r
2178     settingsFileName = strdup(buf);\r
2179   }\r
2180 \r
2181   /* Parse command line */\r
2182   ParseArgs(StringGet, &lpCmdLine);\r
2183 \r
2184   /* [HGM] make sure board size is acceptable */\r
2185   if(appData.NrFiles > BOARD_SIZE ||\r
2186      appData.NrRanks > BOARD_SIZE   )\r
2187       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2188 \r
2189   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2190    * with options from the command line, we now make an even higher priority\r
2191    * overrule by WB options attached to the engine command line. This so that\r
2192    * tournament managers can use WB options (such as /timeOdds) that follow\r
2193    * the engines.\r
2194    */\r
2195   if(appData.firstChessProgram != NULL) {\r
2196       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2197       static char *f = "first";\r
2198       char buf[MSG_SIZ], *q = buf;\r
2199       if(p != NULL) { // engine command line contains WinBoard options\r
2200           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2201           ParseArgs(StringGet, &q);\r
2202           p[-1] = 0; // cut them offengine command line\r
2203       }\r
2204   }\r
2205   // now do same for second chess program\r
2206   if(appData.secondChessProgram != NULL) {\r
2207       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2208       static char *s = "second";\r
2209       char buf[MSG_SIZ], *q = buf;\r
2210       if(p != NULL) { // engine command line contains WinBoard options\r
2211           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2212           ParseArgs(StringGet, &q);\r
2213           p[-1] = 0; // cut them offengine command line\r
2214       }\r
2215   }\r
2216 \r
2217 \r
2218   /* Propagate options that affect others */\r
2219   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2220   if (appData.icsActive || appData.noChessProgram) {\r
2221      chessProgram = FALSE;  /* not local chess program mode */\r
2222   }\r
2223 \r
2224   /* Open startup dialog if needed */\r
2225   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2226       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2227       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2228                         *appData.secondChessProgram == NULLCHAR))) {\r
2229     FARPROC lpProc;\r
2230     \r
2231     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2232     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2233     FreeProcInstance(lpProc);\r
2234   }\r
2235 \r
2236   /* Make sure save files land in the right (?) directory */\r
2237   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2238     appData.saveGameFile = strdup(buf);\r
2239   }\r
2240   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2241     appData.savePositionFile = strdup(buf);\r
2242   }\r
2243 \r
2244   /* Finish initialization for fonts and sounds */\r
2245   for (i=0; i<NUM_FONTS; i++) {\r
2246     for (j=0; j<NUM_SIZES; j++) {\r
2247       CreateFontInMF(font[j][i]);\r
2248     }\r
2249   }\r
2250   /* xboard, and older WinBoards, controlled the move sound with the\r
2251      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2252      always turn the option on (so that the backend will call us),\r
2253      then let the user turn the sound off by setting it to silence if\r
2254      desired.  To accommodate old winboard.ini files saved by old\r
2255      versions of WinBoard, we also turn off the sound if the option\r
2256      was initially set to false. */\r
2257   if (!appData.ringBellAfterMoves) {\r
2258     sounds[(int)SoundMove].name = strdup("");\r
2259     appData.ringBellAfterMoves = TRUE;\r
2260   }\r
2261   GetCurrentDirectory(MSG_SIZ, currDir);\r
2262   SetCurrentDirectory(installDir);\r
2263   LoadAllSounds();\r
2264   SetCurrentDirectory(currDir);\r
2265 \r
2266   p = icsTextMenuString;\r
2267   if (p[0] == '@') {\r
2268     FILE* f = fopen(p + 1, "r");\r
2269     if (f == NULL) {\r
2270       DisplayFatalError(p + 1, errno, 2);\r
2271       return;\r
2272     }\r
2273     i = fread(buf, 1, sizeof(buf)-1, f);\r
2274     fclose(f);\r
2275     buf[i] = NULLCHAR;\r
2276     p = buf;\r
2277   }\r
2278   ParseIcsTextMenu(strdup(p));\r
2279 }\r
2280 \r
2281 \r
2282 VOID\r
2283 InitMenuChecks()\r
2284 {\r
2285   HMENU hmenu = GetMenu(hwndMain);\r
2286 \r
2287   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2288                         MF_BYCOMMAND|((appData.icsActive &&\r
2289                                        *appData.icsCommPort != NULLCHAR) ?\r
2290                                       MF_ENABLED : MF_GRAYED));\r
2291   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2292                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2293                                      MF_CHECKED : MF_UNCHECKED));\r
2294 }\r
2295 \r
2296 \r
2297 VOID\r
2298 SaveSettings(char* name)\r
2299 {\r
2300   FILE *f;\r
2301   ArgDescriptor *ad;\r
2302   WINDOWPLACEMENT wp;\r
2303   char dir[MSG_SIZ];\r
2304 \r
2305   if (!hwndMain) return;\r
2306 \r
2307   GetCurrentDirectory(MSG_SIZ, dir);\r
2308   SetCurrentDirectory(installDir);\r
2309   f = fopen(name, "w");\r
2310   SetCurrentDirectory(dir);\r
2311   if (f == NULL) {\r
2312     DisplayError(name, errno);\r
2313     return;\r
2314   }\r
2315   fprintf(f, ";\n");\r
2316   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2317   fprintf(f, ";\n");\r
2318   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2319   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2320   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2321   fprintf(f, ";\n");\r
2322 \r
2323   wp.length = sizeof(WINDOWPLACEMENT);\r
2324   GetWindowPlacement(hwndMain, &wp);\r
2325   boardX = wp.rcNormalPosition.left;\r
2326   boardY = wp.rcNormalPosition.top;\r
2327 \r
2328   if (hwndConsole) {\r
2329     GetWindowPlacement(hwndConsole, &wp);\r
2330     wpConsole.x = wp.rcNormalPosition.left;\r
2331     wpConsole.y = wp.rcNormalPosition.top;\r
2332     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2333     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2334   }\r
2335 \r
2336   if (analysisDialog) {\r
2337     GetWindowPlacement(analysisDialog, &wp);\r
2338     analysisX = wp.rcNormalPosition.left;\r
2339     analysisY = wp.rcNormalPosition.top;\r
2340     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2341     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2342   }\r
2343 \r
2344   if (commentDialog) {\r
2345     GetWindowPlacement(commentDialog, &wp);\r
2346     commentX = wp.rcNormalPosition.left;\r
2347     commentY = wp.rcNormalPosition.top;\r
2348     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2349     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2350   }\r
2351 \r
2352   if (editTagsDialog) {\r
2353     GetWindowPlacement(editTagsDialog, &wp);\r
2354     editTagsX = wp.rcNormalPosition.left;\r
2355     editTagsY = wp.rcNormalPosition.top;\r
2356     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2357     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2358   }\r
2359 \r
2360   if (gameListDialog) {\r
2361     GetWindowPlacement(gameListDialog, &wp);\r
2362     wpGameList.x = wp.rcNormalPosition.left;\r
2363     wpGameList.y = wp.rcNormalPosition.top;\r
2364     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2365     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2366   }\r
2367 \r
2368   /* [AS] Move history */\r
2369   wpMoveHistory.visible = MoveHistoryIsUp();\r
2370   \r
2371   if( moveHistoryDialog ) {\r
2372     GetWindowPlacement(moveHistoryDialog, &wp);\r
2373     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2374     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2375     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2376     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2377   }\r
2378 \r
2379   /* [AS] Eval graph */\r
2380   wpEvalGraph.visible = EvalGraphIsUp();\r
2381 \r
2382   if( evalGraphDialog ) {\r
2383     GetWindowPlacement(evalGraphDialog, &wp);\r
2384     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2385     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2386     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2387     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2388   }\r
2389 \r
2390   /* [AS] Engine output */\r
2391   wpEngineOutput.visible = EngineOutputIsUp();\r
2392 \r
2393   if( engineOutputDialog ) {\r
2394     GetWindowPlacement(engineOutputDialog, &wp);\r
2395     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2396     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2397     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2398     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2399   }\r
2400 \r
2401   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2402     if (!ad->save) continue;\r
2403     switch (ad->argType) {\r
2404     case ArgString:\r
2405       {\r
2406         char *p = *(char **)ad->argLoc;\r
2407         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2408           /* Quote multiline values or \-containing values\r
2409              with { } if possible */\r
2410           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2411         } else {\r
2412           /* Else quote with " " */\r
2413           fprintf(f, "/%s=\"", ad->argName);\r
2414           while (*p) {\r
2415             if (*p == '\n') fprintf(f, "\n");\r
2416             else if (*p == '\r') fprintf(f, "\\r");\r
2417             else if (*p == '\t') fprintf(f, "\\t");\r
2418             else if (*p == '\b') fprintf(f, "\\b");\r
2419             else if (*p == '\f') fprintf(f, "\\f");\r
2420             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2421             else if (*p == '\"') fprintf(f, "\\\"");\r
2422             else if (*p == '\\') fprintf(f, "\\\\");\r
2423             else putc(*p, f);\r
2424             p++;\r
2425           }\r
2426           fprintf(f, "\"\n");\r
2427         }\r
2428       }\r
2429       break;\r
2430     case ArgInt:\r
2431     case ArgZ:\r
2432       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2433       break;\r
2434     case ArgX:\r
2435       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2436       break;\r
2437     case ArgY:\r
2438       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2439       break;\r
2440     case ArgFloat:\r
2441       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2442       break;\r
2443     case ArgBoolean:\r
2444       fprintf(f, "/%s=%s\n", ad->argName, \r
2445         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2446       break;\r
2447     case ArgTrue:\r
2448       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2449       break;\r
2450     case ArgFalse:\r
2451       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2452       break;\r
2453     case ArgColor:\r
2454       {\r
2455         COLORREF color = *(COLORREF *)ad->argLoc;\r
2456         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2457           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2458       }\r
2459       break;\r
2460     case ArgAttribs:\r
2461       {\r
2462         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2463         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2464           (ta->effects & CFE_BOLD) ? "b" : "",\r
2465           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2466           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2467           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2468           (ta->effects) ? " " : "",\r
2469           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2470       }\r
2471       break;\r
2472     case ArgFilename:\r
2473       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2474         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2475       } else {\r
2476         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2477       }\r
2478       break;\r
2479     case ArgBoardSize:\r
2480       fprintf(f, "/%s=%s\n", ad->argName,\r
2481               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2482       break;\r
2483     case ArgFont:\r
2484       {\r
2485         int bs;\r
2486         for (bs=0; bs<NUM_SIZES; bs++) {\r
2487           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2488           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2489           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2490             ad->argName, mfp->faceName, mfp->pointSize,\r
2491             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2492             mfp->bold ? "b" : "",\r
2493             mfp->italic ? "i" : "",\r
2494             mfp->underline ? "u" : "",\r
2495             mfp->strikeout ? "s" : "");\r
2496         }\r
2497       }\r
2498       break;\r
2499     case ArgCommSettings:\r
2500       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2501     case ArgNone:\r
2502     case ArgSettingsFilename: ;\r
2503     }\r
2504   }\r
2505   fclose(f);\r
2506 }\r
2507 \r
2508 \r
2509 \r
2510 /*---------------------------------------------------------------------------*\\r
2511  *\r
2512  * GDI board drawing routines\r
2513  *\r
2514 \*---------------------------------------------------------------------------*/\r
2515 \r
2516 /* [AS] Draw square using background texture */\r
2517 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2518 {\r
2519     XFORM   x;\r
2520 \r
2521     if( mode == 0 ) {\r
2522         return; /* Should never happen! */\r
2523     }\r
2524 \r
2525     SetGraphicsMode( dst, GM_ADVANCED );\r
2526 \r
2527     switch( mode ) {\r
2528     case 1:\r
2529         /* Identity */\r
2530         break;\r
2531     case 2:\r
2532         /* X reflection */\r
2533         x.eM11 = -1.0;\r
2534         x.eM12 = 0;\r
2535         x.eM21 = 0;\r
2536         x.eM22 = 1.0;\r
2537         x.eDx = (FLOAT) dw + dx - 1;\r
2538         x.eDy = 0;\r
2539         dx = 0;\r
2540         SetWorldTransform( dst, &x );\r
2541         break;\r
2542     case 3:\r
2543         /* Y reflection */\r
2544         x.eM11 = 1.0;\r
2545         x.eM12 = 0;\r
2546         x.eM21 = 0;\r
2547         x.eM22 = -1.0;\r
2548         x.eDx = 0;\r
2549         x.eDy = (FLOAT) dh + dy - 1;\r
2550         dy = 0;\r
2551         SetWorldTransform( dst, &x );\r
2552         break;\r
2553     case 4:\r
2554         /* X/Y flip */\r
2555         x.eM11 = 0;\r
2556         x.eM12 = 1.0;\r
2557         x.eM21 = 1.0;\r
2558         x.eM22 = 0;\r
2559         x.eDx = (FLOAT) dx;\r
2560         x.eDy = (FLOAT) dy;\r
2561         dx = 0;\r
2562         dy = 0;\r
2563         SetWorldTransform( dst, &x );\r
2564         break;\r
2565     }\r
2566 \r
2567     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2568 \r
2569     x.eM11 = 1.0;\r
2570     x.eM12 = 0;\r
2571     x.eM21 = 0;\r
2572     x.eM22 = 1.0;\r
2573     x.eDx = 0;\r
2574     x.eDy = 0;\r
2575     SetWorldTransform( dst, &x );\r
2576 \r
2577     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2578 }\r
2579 \r
2580 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2581 enum {\r
2582     PM_WP = (int) WhitePawn, \r
2583     PM_WN = (int) WhiteKnight, \r
2584     PM_WB = (int) WhiteBishop, \r
2585     PM_WR = (int) WhiteRook, \r
2586     PM_WQ = (int) WhiteQueen, \r
2587     PM_WF = (int) WhiteFerz, \r
2588     PM_WW = (int) WhiteWazir, \r
2589     PM_WE = (int) WhiteAlfil, \r
2590     PM_WM = (int) WhiteMan, \r
2591     PM_WO = (int) WhiteCannon, \r
2592     PM_WU = (int) WhiteUnicorn, \r
2593     PM_WH = (int) WhiteNightrider, \r
2594     PM_WA = (int) WhiteAngel, \r
2595     PM_WC = (int) WhiteMarshall, \r
2596     PM_WAB = (int) WhiteCardinal, \r
2597     PM_WD = (int) WhiteDragon, \r
2598     PM_WL = (int) WhiteLance, \r
2599     PM_WS = (int) WhiteCobra, \r
2600     PM_WV = (int) WhiteFalcon, \r
2601     PM_WSG = (int) WhiteSilver, \r
2602     PM_WG = (int) WhiteGrasshopper, \r
2603     PM_WK = (int) WhiteKing,\r
2604     PM_BP = (int) BlackPawn, \r
2605     PM_BN = (int) BlackKnight, \r
2606     PM_BB = (int) BlackBishop, \r
2607     PM_BR = (int) BlackRook, \r
2608     PM_BQ = (int) BlackQueen, \r
2609     PM_BF = (int) BlackFerz, \r
2610     PM_BW = (int) BlackWazir, \r
2611     PM_BE = (int) BlackAlfil, \r
2612     PM_BM = (int) BlackMan,\r
2613     PM_BO = (int) BlackCannon, \r
2614     PM_BU = (int) BlackUnicorn, \r
2615     PM_BH = (int) BlackNightrider, \r
2616     PM_BA = (int) BlackAngel, \r
2617     PM_BC = (int) BlackMarshall, \r
2618     PM_BG = (int) BlackGrasshopper, \r
2619     PM_BAB = (int) BlackCardinal,\r
2620     PM_BD = (int) BlackDragon,\r
2621     PM_BL = (int) BlackLance,\r
2622     PM_BS = (int) BlackCobra,\r
2623     PM_BV = (int) BlackFalcon,\r
2624     PM_BSG = (int) BlackSilver,\r
2625     PM_BK = (int) BlackKing\r
2626 };\r
2627 \r
2628 static HFONT hPieceFont = NULL;\r
2629 static HBITMAP hPieceMask[(int) EmptySquare];\r
2630 static HBITMAP hPieceFace[(int) EmptySquare];\r
2631 static int fontBitmapSquareSize = 0;\r
2632 static char pieceToFontChar[(int) EmptySquare] =\r
2633                               { 'p', 'n', 'b', 'r', 'q', \r
2634                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2635                       'k', 'o', 'm', 'v', 't', 'w', \r
2636                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2637                                                               'l' };\r
2638 \r
2639 extern BOOL SetCharTable( char *table, const char * map );\r
2640 /* [HGM] moved to backend.c */\r
2641 \r
2642 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2643 {\r
2644     HBRUSH hbrush;\r
2645     BYTE r1 = GetRValue( color );\r
2646     BYTE g1 = GetGValue( color );\r
2647     BYTE b1 = GetBValue( color );\r
2648     BYTE r2 = r1 / 2;\r
2649     BYTE g2 = g1 / 2;\r
2650     BYTE b2 = b1 / 2;\r
2651     RECT rc;\r
2652 \r
2653     /* Create a uniform background first */\r
2654     hbrush = CreateSolidBrush( color );\r
2655     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2656     FillRect( hdc, &rc, hbrush );\r
2657     DeleteObject( hbrush );\r
2658     \r
2659     if( mode == 1 ) {\r
2660         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2661         int steps = squareSize / 2;\r
2662         int i;\r
2663 \r
2664         for( i=0; i<steps; i++ ) {\r
2665             BYTE r = r1 - (r1-r2) * i / steps;\r
2666             BYTE g = g1 - (g1-g2) * i / steps;\r
2667             BYTE b = b1 - (b1-b2) * i / steps;\r
2668 \r
2669             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2670             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2671             FillRect( hdc, &rc, hbrush );\r
2672             DeleteObject(hbrush);\r
2673         }\r
2674     }\r
2675     else if( mode == 2 ) {\r
2676         /* Diagonal gradient, good more or less for every piece */\r
2677         POINT triangle[3];\r
2678         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2679         HBRUSH hbrush_old;\r
2680         int steps = squareSize;\r
2681         int i;\r
2682 \r
2683         triangle[0].x = squareSize - steps;\r
2684         triangle[0].y = squareSize;\r
2685         triangle[1].x = squareSize;\r
2686         triangle[1].y = squareSize;\r
2687         triangle[2].x = squareSize;\r
2688         triangle[2].y = squareSize - steps;\r
2689 \r
2690         for( i=0; i<steps; i++ ) {\r
2691             BYTE r = r1 - (r1-r2) * i / steps;\r
2692             BYTE g = g1 - (g1-g2) * i / steps;\r
2693             BYTE b = b1 - (b1-b2) * i / steps;\r
2694 \r
2695             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2696             hbrush_old = SelectObject( hdc, hbrush );\r
2697             Polygon( hdc, triangle, 3 );\r
2698             SelectObject( hdc, hbrush_old );\r
2699             DeleteObject(hbrush);\r
2700             triangle[0].x++;\r
2701             triangle[2].y++;\r
2702         }\r
2703 \r
2704         SelectObject( hdc, hpen );\r
2705     }\r
2706 }\r
2707 \r
2708 /*\r
2709     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2710     seems to work ok. The main problem here is to find the "inside" of a chess\r
2711     piece: follow the steps as explained below.\r
2712 */\r
2713 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2714 {\r
2715     HBITMAP hbm;\r
2716     HBITMAP hbm_old;\r
2717     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2718     RECT rc;\r
2719     SIZE sz;\r
2720     POINT pt;\r
2721     int backColor = whitePieceColor; \r
2722     int foreColor = blackPieceColor;\r
2723     \r
2724     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2725         backColor = appData.fontBackColorWhite;\r
2726         foreColor = appData.fontForeColorWhite;\r
2727     }\r
2728     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2729         backColor = appData.fontBackColorBlack;\r
2730         foreColor = appData.fontForeColorBlack;\r
2731     }\r
2732 \r
2733     /* Mask */\r
2734     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2735 \r
2736     hbm_old = SelectObject( hdc, hbm );\r
2737 \r
2738     rc.left = 0;\r
2739     rc.top = 0;\r
2740     rc.right = squareSize;\r
2741     rc.bottom = squareSize;\r
2742 \r
2743     /* Step 1: background is now black */\r
2744     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2745 \r
2746     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2747 \r
2748     pt.x = (squareSize - sz.cx) / 2;\r
2749     pt.y = (squareSize - sz.cy) / 2;\r
2750 \r
2751     SetBkMode( hdc, TRANSPARENT );\r
2752     SetTextColor( hdc, chroma );\r
2753     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2754     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2755 \r
2756     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2757     /* Step 3: the area outside the piece is filled with white */\r
2758 //    FloodFill( hdc, 0, 0, chroma );\r
2759     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2760     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2761     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2762     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2763     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2764     /* \r
2765         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2766         but if the start point is not inside the piece we're lost!\r
2767         There should be a better way to do this... if we could create a region or path\r
2768         from the fill operation we would be fine for example.\r
2769     */\r
2770 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2771     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2772 \r
2773     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2774         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2775         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2776 \r
2777         SelectObject( dc2, bm2 );\r
2778         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2779         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2780         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2781         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2782         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2783 \r
2784         DeleteDC( dc2 );\r
2785         DeleteObject( bm2 );\r
2786     }\r
2787 \r
2788     SetTextColor( hdc, 0 );\r
2789     /* \r
2790         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2791         draw the piece again in black for safety.\r
2792     */\r
2793     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2794 \r
2795     SelectObject( hdc, hbm_old );\r
2796 \r
2797     if( hPieceMask[index] != NULL ) {\r
2798         DeleteObject( hPieceMask[index] );\r
2799     }\r
2800 \r
2801     hPieceMask[index] = hbm;\r
2802 \r
2803     /* Face */\r
2804     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2805 \r
2806     SelectObject( hdc, hbm );\r
2807 \r
2808     {\r
2809         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2810         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2811         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2812 \r
2813         SelectObject( dc1, hPieceMask[index] );\r
2814         SelectObject( dc2, bm2 );\r
2815         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2816         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2817         \r
2818         /* \r
2819             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2820             the piece background and deletes (makes transparent) the rest.\r
2821             Thanks to that mask, we are free to paint the background with the greates\r
2822             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2823             We use this, to make gradients and give the pieces a "roundish" look.\r
2824         */\r
2825         SetPieceBackground( hdc, backColor, 2 );\r
2826         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2827 \r
2828         DeleteDC( dc2 );\r
2829         DeleteDC( dc1 );\r
2830         DeleteObject( bm2 );\r
2831     }\r
2832 \r
2833     SetTextColor( hdc, foreColor );\r
2834     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2835 \r
2836     SelectObject( hdc, hbm_old );\r
2837 \r
2838     if( hPieceFace[index] != NULL ) {\r
2839         DeleteObject( hPieceFace[index] );\r
2840     }\r
2841 \r
2842     hPieceFace[index] = hbm;\r
2843 }\r
2844 \r
2845 static int TranslatePieceToFontPiece( int piece )\r
2846 {\r
2847     switch( piece ) {\r
2848     case BlackPawn:\r
2849         return PM_BP;\r
2850     case BlackKnight:\r
2851         return PM_BN;\r
2852     case BlackBishop:\r
2853         return PM_BB;\r
2854     case BlackRook:\r
2855         return PM_BR;\r
2856     case BlackQueen:\r
2857         return PM_BQ;\r
2858     case BlackKing:\r
2859         return PM_BK;\r
2860     case WhitePawn:\r
2861         return PM_WP;\r
2862     case WhiteKnight:\r
2863         return PM_WN;\r
2864     case WhiteBishop:\r
2865         return PM_WB;\r
2866     case WhiteRook:\r
2867         return PM_WR;\r
2868     case WhiteQueen:\r
2869         return PM_WQ;\r
2870     case WhiteKing:\r
2871         return PM_WK;\r
2872 \r
2873     case BlackAngel:\r
2874         return PM_BA;\r
2875     case BlackMarshall:\r
2876         return PM_BC;\r
2877     case BlackFerz:\r
2878         return PM_BF;\r
2879     case BlackNightrider:\r
2880         return PM_BH;\r
2881     case BlackAlfil:\r
2882         return PM_BE;\r
2883     case BlackWazir:\r
2884         return PM_BW;\r
2885     case BlackUnicorn:\r
2886         return PM_BU;\r
2887     case BlackCannon:\r
2888         return PM_BO;\r
2889     case BlackGrasshopper:\r
2890         return PM_BG;\r
2891     case BlackMan:\r
2892         return PM_BM;\r
2893     case BlackSilver:\r
2894         return PM_BSG;\r
2895     case BlackLance:\r
2896         return PM_BL;\r
2897     case BlackFalcon:\r
2898         return PM_BV;\r
2899     case BlackCobra:\r
2900         return PM_BS;\r
2901     case BlackCardinal:\r
2902         return PM_BAB;\r
2903     case BlackDragon:\r
2904         return PM_BD;\r
2905 \r
2906     case WhiteAngel:\r
2907         return PM_WA;\r
2908     case WhiteMarshall:\r
2909         return PM_WC;\r
2910     case WhiteFerz:\r
2911         return PM_WF;\r
2912     case WhiteNightrider:\r
2913         return PM_WH;\r
2914     case WhiteAlfil:\r
2915         return PM_WE;\r
2916     case WhiteWazir:\r
2917         return PM_WW;\r
2918     case WhiteUnicorn:\r
2919         return PM_WU;\r
2920     case WhiteCannon:\r
2921         return PM_WO;\r
2922     case WhiteGrasshopper:\r
2923         return PM_WG;\r
2924     case WhiteMan:\r
2925         return PM_WM;\r
2926     case WhiteSilver:\r
2927         return PM_WSG;\r
2928     case WhiteLance:\r
2929         return PM_WL;\r
2930     case WhiteFalcon:\r
2931         return PM_WV;\r
2932     case WhiteCobra:\r
2933         return PM_WS;\r
2934     case WhiteCardinal:\r
2935         return PM_WAB;\r
2936     case WhiteDragon:\r
2937         return PM_WD;\r
2938     }\r
2939 \r
2940     return 0;\r
2941 }\r
2942 \r
2943 void CreatePiecesFromFont()\r
2944 {\r
2945     LOGFONT lf;\r
2946     HDC hdc_window = NULL;\r
2947     HDC hdc = NULL;\r
2948     HFONT hfont_old;\r
2949     int fontHeight;\r
2950     int i;\r
2951 \r
2952     if( fontBitmapSquareSize < 0 ) {\r
2953         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2954         return;\r
2955     }\r
2956 \r
2957     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2958         fontBitmapSquareSize = -1;\r
2959         return;\r
2960     }\r
2961 \r
2962     if( fontBitmapSquareSize != squareSize ) {\r
2963         hdc_window = GetDC( hwndMain );\r
2964         hdc = CreateCompatibleDC( hdc_window );\r
2965 \r
2966         if( hPieceFont != NULL ) {\r
2967             DeleteObject( hPieceFont );\r
2968         }\r
2969         else {\r
2970             for( i=0; i<=(int)BlackKing; i++ ) {\r
2971                 hPieceMask[i] = NULL;\r
2972                 hPieceFace[i] = NULL;\r
2973             }\r
2974         }\r
2975 \r
2976         fontHeight = 75;\r
2977 \r
2978         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2979             fontHeight = appData.fontPieceSize;\r
2980         }\r
2981 \r
2982         fontHeight = (fontHeight * squareSize) / 100;\r
2983 \r
2984         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2985         lf.lfWidth = 0;\r
2986         lf.lfEscapement = 0;\r
2987         lf.lfOrientation = 0;\r
2988         lf.lfWeight = FW_NORMAL;\r
2989         lf.lfItalic = 0;\r
2990         lf.lfUnderline = 0;\r
2991         lf.lfStrikeOut = 0;\r
2992         lf.lfCharSet = DEFAULT_CHARSET;\r
2993         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2994         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2995         lf.lfQuality = PROOF_QUALITY;\r
2996         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2997         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2998         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2999 \r
3000         hPieceFont = CreateFontIndirect( &lf );\r
3001 \r
3002         if( hPieceFont == NULL ) {\r
3003             fontBitmapSquareSize = -2;\r
3004         }\r
3005         else {\r
3006             /* Setup font-to-piece character table */\r
3007             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3008                 /* No (or wrong) global settings, try to detect the font */\r
3009                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3010                     /* Alpha */\r
3011                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3012                 }\r
3013                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3014                     /* DiagramTT* family */\r
3015                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3016                 }\r
3017                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3018                     /* Fairy symbols */\r
3019                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3020                 }\r
3021                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3022                     /* Good Companion (Some characters get warped as literal :-( */\r
3023                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3024                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3025                     SetCharTable(pieceToFontChar, s);\r
3026                 }\r
3027                 else {\r
3028                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3029                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3030                 }\r
3031             }\r
3032 \r
3033             /* Create bitmaps */\r
3034             hfont_old = SelectObject( hdc, hPieceFont );\r
3035 #if 0\r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3048 \r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3070             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3071             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3072             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3073             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3074             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3075             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3076             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3077             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3078             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3079             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3080             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3081 #else\r
3082             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3083                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3084                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3085 #endif\r
3086             SelectObject( hdc, hfont_old );\r
3087 \r
3088             fontBitmapSquareSize = squareSize;\r
3089         }\r
3090     }\r
3091 \r
3092     if( hdc != NULL ) {\r
3093         DeleteDC( hdc );\r
3094     }\r
3095 \r
3096     if( hdc_window != NULL ) {\r
3097         ReleaseDC( hwndMain, hdc_window );\r
3098     }\r
3099 }\r
3100 \r
3101 HBITMAP\r
3102 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3103 {\r
3104   char name[128];\r
3105 \r
3106   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3107   if (gameInfo.event &&\r
3108       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3109       strcmp(name, "k80s") == 0) {\r
3110     strcpy(name, "tim");\r
3111   }\r
3112   return LoadBitmap(hinst, name);\r
3113 }\r
3114 \r
3115 \r
3116 /* Insert a color into the program's logical palette\r
3117    structure.  This code assumes the given color is\r
3118    the result of the RGB or PALETTERGB macro, and it\r
3119    knows how those macros work (which is documented).\r
3120 */\r
3121 VOID\r
3122 InsertInPalette(COLORREF color)\r
3123 {\r
3124   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3125 \r
3126   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3127     DisplayFatalError("Too many colors", 0, 1);\r
3128     pLogPal->palNumEntries--;\r
3129     return;\r
3130   }\r
3131 \r
3132   pe->peFlags = (char) 0;\r
3133   pe->peRed = (char) (0xFF & color);\r
3134   pe->peGreen = (char) (0xFF & (color >> 8));\r
3135   pe->peBlue = (char) (0xFF & (color >> 16));\r
3136   return;\r
3137 }\r
3138 \r
3139 \r
3140 VOID\r
3141 InitDrawingColors()\r
3142 {\r
3143   if (pLogPal == NULL) {\r
3144     /* Allocate enough memory for a logical palette with\r
3145      * PALETTESIZE entries and set the size and version fields\r
3146      * of the logical palette structure.\r
3147      */\r
3148     pLogPal = (NPLOGPALETTE)\r
3149       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3150                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3151     pLogPal->palVersion    = 0x300;\r
3152   }\r
3153   pLogPal->palNumEntries = 0;\r
3154 \r
3155   InsertInPalette(lightSquareColor);\r
3156   InsertInPalette(darkSquareColor);\r
3157   InsertInPalette(whitePieceColor);\r
3158   InsertInPalette(blackPieceColor);\r
3159   InsertInPalette(highlightSquareColor);\r
3160   InsertInPalette(premoveHighlightColor);\r
3161 \r
3162   /*  create a logical color palette according the information\r
3163    *  in the LOGPALETTE structure.\r
3164    */\r
3165   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3166 \r
3167   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3168   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3169   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3170   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3171   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3172   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3173   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3174   /* [AS] Force rendering of the font-based pieces */\r
3175   if( fontBitmapSquareSize > 0 ) {\r
3176     fontBitmapSquareSize = 0;\r
3177   }\r
3178 }\r
3179 \r
3180 \r
3181 int\r
3182 BoardWidth(int boardSize, int n)\r
3183 { /* [HGM] argument n added to allow different width and height */\r
3184   int lineGap = sizeInfo[boardSize].lineGap;\r
3185 \r
3186   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3187       lineGap = appData.overrideLineGap;\r
3188   }\r
3189 \r
3190   return (n + 1) * lineGap +\r
3191           n * sizeInfo[boardSize].squareSize;\r
3192 }\r
3193 \r
3194 /* Respond to board resize by dragging edge */\r
3195 VOID\r
3196 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3197 {\r
3198   BoardSize newSize = NUM_SIZES - 1;\r
3199   static int recurse = 0;\r
3200   if (IsIconic(hwndMain)) return;\r
3201   if (recurse > 0) return;\r
3202   recurse++;\r
3203   while (newSize > 0) {\r
3204         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3205         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3206            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3207     newSize--;\r
3208   } \r
3209   boardSize = newSize;\r
3210   InitDrawingSizes(boardSize, flags);\r
3211   recurse--;\r
3212 }\r
3213 \r
3214 \r
3215 \r
3216 VOID\r
3217 InitDrawingSizes(BoardSize boardSize, int flags)\r
3218 {\r
3219   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3220   ChessSquare piece;\r
3221   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3222   HDC hdc;\r
3223   SIZE clockSize, messageSize;\r
3224   HFONT oldFont;\r
3225   char buf[MSG_SIZ];\r
3226   char *str;\r
3227   HMENU hmenu = GetMenu(hwndMain);\r
3228   RECT crect, wrect, oldRect;\r
3229   int offby;\r
3230   LOGBRUSH logbrush;\r
3231 \r
3232   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3233   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3234 \r
3235   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3236   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3237 \r
3238   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3239   oldRect.top = boardY;\r
3240   oldRect.right = boardX + winWidth;\r
3241   oldRect.bottom = boardY + winHeight;\r
3242 \r
3243   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3244   smallLayout = sizeInfo[boardSize].smallLayout;\r
3245   squareSize = sizeInfo[boardSize].squareSize;\r
3246   lineGap = sizeInfo[boardSize].lineGap;\r
3247   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3248 \r
3249   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3250       lineGap = appData.overrideLineGap;\r
3251   }\r
3252 \r
3253   if (tinyLayout != oldTinyLayout) {\r
3254     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3255     if (tinyLayout) {\r
3256       style &= ~WS_SYSMENU;\r
3257       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3258                  "&Minimize\tCtrl+F4");\r
3259     } else {\r
3260       style |= WS_SYSMENU;\r
3261       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3262     }\r
3263     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3264 \r
3265     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3266       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3267         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3268     }\r
3269     DrawMenuBar(hwndMain);\r
3270   }\r
3271 \r
3272   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3273   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3274 \r
3275   /* Get text area sizes */\r
3276   hdc = GetDC(hwndMain);\r
3277   if (appData.clockMode) {\r
3278     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3279   } else {\r
3280     sprintf(buf, "White");\r
3281   }\r
3282   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3283   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3284   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3285   str = "We only care about the height here";\r
3286   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3287   SelectObject(hdc, oldFont);\r
3288   ReleaseDC(hwndMain, hdc);\r
3289 \r
3290   /* Compute where everything goes */\r
3291   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3292         /* [HGM] logo: if either logo is on, reserve space for it */\r
3293         logoHeight =  2*clockSize.cy;\r
3294         leftLogoRect.left   = OUTER_MARGIN;\r
3295         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3296         leftLogoRect.top    = OUTER_MARGIN;\r
3297         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3298 \r
3299         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3300         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3301         rightLogoRect.top    = OUTER_MARGIN;\r
3302         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3303 \r
3304 \r
3305     whiteRect.left = leftLogoRect.right;\r
3306     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3307     whiteRect.top = OUTER_MARGIN;\r
3308     whiteRect.bottom = whiteRect.top + logoHeight;\r
3309 \r
3310     blackRect.right = rightLogoRect.left;\r
3311     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3312     blackRect.top = whiteRect.top;\r
3313     blackRect.bottom = whiteRect.bottom;\r
3314   } else {\r
3315     whiteRect.left = OUTER_MARGIN;\r
3316     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3317     whiteRect.top = OUTER_MARGIN;\r
3318     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3319 \r
3320     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3321     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3322     blackRect.top = whiteRect.top;\r
3323     blackRect.bottom = whiteRect.bottom;\r
3324   }\r
3325 \r
3326   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3327   if (appData.showButtonBar) {\r
3328     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3329       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3330   } else {\r
3331     messageRect.right = OUTER_MARGIN + boardWidth;\r
3332   }\r
3333   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3334   messageRect.bottom = messageRect.top + messageSize.cy;\r
3335 \r
3336   boardRect.left = OUTER_MARGIN;\r
3337   boardRect.right = boardRect.left + boardWidth;\r
3338   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3339   boardRect.bottom = boardRect.top + boardHeight;\r
3340 \r
3341   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3342   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3343   oldBoardSize = boardSize;\r
3344   oldTinyLayout = tinyLayout;\r
3345   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3346   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3347     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3348   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3349   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3350   winHeight = winH; //       without disturbing window attachments\r
3351   GetWindowRect(hwndMain, &wrect);\r
3352   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3353                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3354 \r
3355   // [HGM] placement: let attached windows follow size change.\r
3356   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3357   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3358   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3359   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3360   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3361 \r
3362   /* compensate if menu bar wrapped */\r
3363   GetClientRect(hwndMain, &crect);\r
3364   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3365   winHeight += offby;\r
3366   switch (flags) {\r
3367   case WMSZ_TOPLEFT:\r
3368     SetWindowPos(hwndMain, NULL, \r
3369                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3370                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3371     break;\r
3372 \r
3373   case WMSZ_TOPRIGHT:\r
3374   case WMSZ_TOP:\r
3375     SetWindowPos(hwndMain, NULL, \r
3376                  wrect.left, wrect.bottom - winHeight, \r
3377                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3378     break;\r
3379 \r
3380   case WMSZ_BOTTOMLEFT:\r
3381   case WMSZ_LEFT:\r
3382     SetWindowPos(hwndMain, NULL, \r
3383                  wrect.right - winWidth, wrect.top, \r
3384                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3385     break;\r
3386 \r
3387   case WMSZ_BOTTOMRIGHT:\r
3388   case WMSZ_BOTTOM:\r
3389   case WMSZ_RIGHT:\r
3390   default:\r
3391     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3392                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3393     break;\r
3394   }\r
3395 \r
3396   hwndPause = NULL;\r
3397   for (i = 0; i < N_BUTTONS; i++) {\r
3398     if (buttonDesc[i].hwnd != NULL) {\r
3399       DestroyWindow(buttonDesc[i].hwnd);\r
3400       buttonDesc[i].hwnd = NULL;\r
3401     }\r
3402     if (appData.showButtonBar) {\r
3403       buttonDesc[i].hwnd =\r
3404         CreateWindow("BUTTON", buttonDesc[i].label,\r
3405                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3406                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3407                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3408                      (HMENU) buttonDesc[i].id,\r
3409                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3410       if (tinyLayout) {\r
3411         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3412                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3413                     MAKELPARAM(FALSE, 0));\r
3414       }\r
3415       if (buttonDesc[i].id == IDM_Pause)\r
3416         hwndPause = buttonDesc[i].hwnd;\r
3417       buttonDesc[i].wndproc = (WNDPROC)\r
3418         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3419     }\r
3420   }\r
3421   if (gridPen != NULL) DeleteObject(gridPen);\r
3422   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3423   if (premovePen != NULL) DeleteObject(premovePen);\r
3424   if (lineGap != 0) {\r
3425     logbrush.lbStyle = BS_SOLID;\r
3426     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3427     gridPen =\r
3428       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3429                    lineGap, &logbrush, 0, NULL);\r
3430     logbrush.lbColor = highlightSquareColor;\r
3431     highlightPen =\r
3432       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3433                    lineGap, &logbrush, 0, NULL);\r
3434 \r
3435     logbrush.lbColor = premoveHighlightColor; \r
3436     premovePen =\r
3437       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3438                    lineGap, &logbrush, 0, NULL);\r
3439 \r
3440     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3441     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3442       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3443       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3444         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3445       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3446         BOARD_WIDTH * (squareSize + lineGap);\r
3447       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3448     }\r
3449     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3450       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3451       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3452         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3453         lineGap / 2 + (i * (squareSize + lineGap));\r
3454       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3455         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3456       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3457     }\r
3458   }\r
3459 \r
3460   /* [HGM] Licensing requirement */\r
3461 #ifdef GOTHIC\r
3462   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3463 #endif\r
3464 #ifdef FALCON\r
3465   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3466 #endif\r
3467   GothicPopUp( "", VariantNormal);\r
3468 \r
3469 \r
3470 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3471 \r
3472   /* Load piece bitmaps for this board size */\r
3473   for (i=0; i<=2; i++) {\r
3474     for (piece = WhitePawn;\r
3475          (int) piece < (int) BlackPawn;\r
3476          piece = (ChessSquare) ((int) piece + 1)) {\r
3477       if (pieceBitmap[i][piece] != NULL)\r
3478         DeleteObject(pieceBitmap[i][piece]);\r
3479     }\r
3480   }\r
3481 \r
3482   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3483   // Orthodox Chess pieces\r
3484   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3485   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3486   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3487   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3488   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3489   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3490   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3491   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3492   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3493   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3494   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3495   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3496   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3497   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3498   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3499   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3500     // in Shogi, Hijack the unused Queen for Lance\r
3501     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3502     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3503     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3504   } else {\r
3505     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3506     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3507     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3508   }\r
3509 \r
3510   if(squareSize <= 72 && squareSize >= 33) { \r
3511     /* A & C are available in most sizes now */\r
3512     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3513       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3514       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3515       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3516       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3517       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3518       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3519       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3520       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3521       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3522       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3523       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3524       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3525     } else { // Smirf-like\r
3526       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3527       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3528       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3529     }\r
3530     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3531       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3532       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3533       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3534     } else { // WinBoard standard\r
3535       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3536       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3537       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3538     }\r
3539   }\r
3540 \r
3541 \r
3542   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3543     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3544     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3545     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3546     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3547     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3548     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3549     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3550     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3551     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3552     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3553     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3554     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3555     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3556     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3557     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3558     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3559     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3560     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3561     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3562     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3563     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3564     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3565     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3566     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3567     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3568     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3569     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3570     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3571     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3572     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3573 \r
3574     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3575       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3576       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3577       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3578       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3579       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3580       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3581       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3582       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3583       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3584       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3585       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3586       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3587     } else {\r
3588       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3589       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3590       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3591       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3592       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3593       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3594       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3595       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3596       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3597       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3598       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3599       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3600     }\r
3601 \r
3602   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3603     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3604     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3605     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3606     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3607     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3608     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3609     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3610     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3611     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3612     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3613     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3614     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3615     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3616     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3617   }\r
3618 \r
3619 \r
3620   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3621   /* special Shogi support in this size */\r
3622   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3623       for (piece = WhitePawn;\r
3624            (int) piece < (int) BlackPawn;\r
3625            piece = (ChessSquare) ((int) piece + 1)) {\r
3626         if (pieceBitmap[i][piece] != NULL)\r
3627           DeleteObject(pieceBitmap[i][piece]);\r
3628       }\r
3629     }\r
3630   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3631   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3632   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3633   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3634   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3635   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3636   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3637   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3638   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3639   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3640   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3641   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3642   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3643   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3644   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3645   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3646   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3647   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3648   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3649   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3650   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3651   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3652   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3653   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3654   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3655   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3656   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3657   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3658   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3659   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3660   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3661   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3662   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3663   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3664   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3665   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3666   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3667   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3668   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3669   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3670   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3671   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3672   minorSize = 0;\r
3673   }\r
3674 }\r
3675 \r
3676 HBITMAP\r
3677 PieceBitmap(ChessSquare p, int kind)\r
3678 {\r
3679   if ((int) p >= (int) BlackPawn)\r
3680     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3681 \r
3682   return pieceBitmap[kind][(int) p];\r
3683 }\r
3684 \r
3685 /***************************************************************/\r
3686 \r
3687 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3688 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3689 /*\r
3690 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3691 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3692 */\r
3693 \r
3694 VOID\r
3695 SquareToPos(int row, int column, int * x, int * y)\r
3696 {\r
3697   if (flipView) {\r
3698     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3699     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3700   } else {\r
3701     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3702     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3703   }\r
3704 }\r
3705 \r
3706 VOID\r
3707 DrawCoordsOnDC(HDC hdc)\r
3708 {\r
3709   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
3710   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
3711   char str[2] = { NULLCHAR, NULLCHAR };\r
3712   int oldMode, oldAlign, x, y, start, i;\r
3713   HFONT oldFont;\r
3714   HBRUSH oldBrush;\r
3715 \r
3716   if (!appData.showCoords)\r
3717     return;\r
3718 \r
3719   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3720 \r
3721   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3722   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3723   oldAlign = GetTextAlign(hdc);\r
3724   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3725 \r
3726   y = boardRect.top + lineGap;\r
3727   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3728 \r
3729   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3730   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3731     str[0] = files[start + i];\r
3732     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3733     y += squareSize + lineGap;\r
3734   }\r
3735 \r
3736   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3737 \r
3738   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3739   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3740     str[0] = ranks[start + i];\r
3741     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3742     x += squareSize + lineGap;\r
3743   }    \r
3744 \r
3745   SelectObject(hdc, oldBrush);\r
3746   SetBkMode(hdc, oldMode);\r
3747   SetTextAlign(hdc, oldAlign);\r
3748   SelectObject(hdc, oldFont);\r
3749 }\r
3750 \r
3751 VOID\r
3752 DrawGridOnDC(HDC hdc)\r
3753 {\r
3754   HPEN oldPen;\r
3755  \r
3756   if (lineGap != 0) {\r
3757     oldPen = SelectObject(hdc, gridPen);\r
3758     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3759     SelectObject(hdc, oldPen);\r
3760   }\r
3761 }\r
3762 \r
3763 #define HIGHLIGHT_PEN 0\r
3764 #define PREMOVE_PEN   1\r
3765 \r
3766 VOID\r
3767 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3768 {\r
3769   int x1, y1;\r
3770   HPEN oldPen, hPen;\r
3771   if (lineGap == 0) return;\r
3772   if (flipView) {\r
3773     x1 = boardRect.left +\r
3774       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3775     y1 = boardRect.top +\r
3776       lineGap/2 + y * (squareSize + lineGap);\r
3777   } else {\r
3778     x1 = boardRect.left +\r
3779       lineGap/2 + x * (squareSize + lineGap);\r
3780     y1 = boardRect.top +\r
3781       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3782   }\r
3783   hPen = pen ? premovePen : highlightPen;\r
3784   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3785   MoveToEx(hdc, x1, y1, NULL);\r
3786   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3787   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3788   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3789   LineTo(hdc, x1, y1);\r
3790   SelectObject(hdc, oldPen);\r
3791 }\r
3792 \r
3793 VOID\r
3794 DrawHighlightsOnDC(HDC hdc)\r
3795 {\r
3796   int i;\r
3797   for (i=0; i<2; i++) {\r
3798     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3799       DrawHighlightOnDC(hdc, TRUE,\r
3800                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3801                         HIGHLIGHT_PEN);\r
3802   }\r
3803   for (i=0; i<2; i++) {\r
3804     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3805         premoveHighlightInfo.sq[i].y >= 0) {\r
3806         DrawHighlightOnDC(hdc, TRUE,\r
3807                           premoveHighlightInfo.sq[i].x, \r
3808                           premoveHighlightInfo.sq[i].y,\r
3809                           PREMOVE_PEN);\r
3810     }\r
3811   }\r
3812 }\r
3813 \r
3814 /* Note: sqcolor is used only in monoMode */\r
3815 /* Note that this code is largely duplicated in woptions.c,\r
3816    function DrawSampleSquare, so that needs to be updated too */\r
3817 VOID\r
3818 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3819 {\r
3820   HBITMAP oldBitmap;\r
3821   HBRUSH oldBrush;\r
3822   int tmpSize;\r
3823 \r
3824   if (appData.blindfold) return;\r
3825 \r
3826   /* [AS] Use font-based pieces if needed */\r
3827   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3828     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3829     CreatePiecesFromFont();\r
3830 \r
3831     if( fontBitmapSquareSize == squareSize ) {\r
3832         int index = TranslatePieceToFontPiece(piece);\r
3833 \r
3834         SelectObject( tmphdc, hPieceMask[ index ] );\r
3835 \r
3836         BitBlt( hdc,\r
3837             x, y,\r
3838             squareSize, squareSize,\r
3839             tmphdc,\r
3840             0, 0,\r
3841             SRCAND );\r
3842 \r
3843         SelectObject( tmphdc, hPieceFace[ index ] );\r
3844 \r
3845         BitBlt( hdc,\r
3846             x, y,\r
3847             squareSize, squareSize,\r
3848             tmphdc,\r
3849             0, 0,\r
3850             SRCPAINT );\r
3851 \r
3852         return;\r
3853     }\r
3854   }\r
3855 \r
3856   if (appData.monoMode) {\r
3857     SelectObject(tmphdc, PieceBitmap(piece, \r
3858       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3859     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3860            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3861   } else {\r
3862     tmpSize = squareSize;\r
3863     if(minorSize &&\r
3864         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3865          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3866       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3867       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3868       x += (squareSize - minorSize)>>1;\r
3869       y += squareSize - minorSize - 2;\r
3870       tmpSize = minorSize;\r
3871     }\r
3872     if (color || appData.allWhite ) {\r
3873       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3874       if( color )\r
3875               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3876       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3877       if(appData.upsideDown && color==flipView)\r
3878         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3879       else\r
3880         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3881 #if 0\r
3882       /* Use black piece color for outline of white pieces */\r
3883       /* Not sure this looks really good (though xboard does it).\r
3884          Maybe better to have another selectable color, default black */\r
3885       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3886       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3887       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3888 #else\r
3889       /* Use black for outline of white pieces */\r
3890       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3891       if(appData.upsideDown && color==flipView)\r
3892         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3893       else\r
3894         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3895 #endif\r
3896     } else {\r
3897 #if 0\r
3898       /* Use white piece color for details of black pieces */\r
3899       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3900          WHITE_PIECE ones aren't always the right shape. */\r
3901       /* Not sure this looks really good (though xboard does it).\r
3902          Maybe better to have another selectable color, default medium gray? */\r
3903       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3904       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3905       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3906       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3907       SelectObject(hdc, blackPieceBrush);\r
3908       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3909 #else\r
3910       /* Use square color for details of black pieces */\r
3911       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3912       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3913       if(appData.upsideDown && !flipView)\r
3914         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3915       else\r
3916         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3917 #endif\r
3918     }\r
3919     SelectObject(hdc, oldBrush);\r
3920     SelectObject(tmphdc, oldBitmap);\r
3921   }\r
3922 }\r
3923 \r
3924 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3925 int GetBackTextureMode( int algo )\r
3926 {\r
3927     int result = BACK_TEXTURE_MODE_DISABLED;\r
3928 \r
3929     switch( algo ) \r
3930     {\r
3931         case BACK_TEXTURE_MODE_PLAIN:\r
3932             result = 1; /* Always use identity map */\r
3933             break;\r
3934         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3935             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3936             break;\r
3937     }\r
3938 \r
3939     return result;\r
3940 }\r
3941 \r
3942 /* \r
3943     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3944     to handle redraws cleanly (as random numbers would always be different).\r
3945 */\r
3946 VOID RebuildTextureSquareInfo()\r
3947 {\r
3948     BITMAP bi;\r
3949     int lite_w = 0;\r
3950     int lite_h = 0;\r
3951     int dark_w = 0;\r
3952     int dark_h = 0;\r
3953     int row;\r
3954     int col;\r
3955 \r
3956     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3957 \r
3958     if( liteBackTexture != NULL ) {\r
3959         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3960             lite_w = bi.bmWidth;\r
3961             lite_h = bi.bmHeight;\r
3962         }\r
3963     }\r
3964 \r
3965     if( darkBackTexture != NULL ) {\r
3966         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3967             dark_w = bi.bmWidth;\r
3968             dark_h = bi.bmHeight;\r
3969         }\r
3970     }\r
3971 \r
3972     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3973         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3974             if( (col + row) & 1 ) {\r
3975                 /* Lite square */\r
3976                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3977                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3978                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3979                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3980                 }\r
3981             }\r
3982             else {\r
3983                 /* Dark square */\r
3984                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3985                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3986                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3987                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3988                 }\r
3989             }\r
3990         }\r
3991     }\r
3992 }\r
3993 \r
3994 /* [AS] Arrow highlighting support */\r
3995 \r
3996 static int A_WIDTH = 5; /* Width of arrow body */\r
3997 \r
3998 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3999 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
4000 \r
4001 static double Sqr( double x )\r
4002 {\r
4003     return x*x;\r
4004 }\r
4005 \r
4006 static int Round( double x )\r
4007 {\r
4008     return (int) (x + 0.5);\r
4009 }\r
4010 \r
4011 /* Draw an arrow between two points using current settings */\r
4012 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4013 {\r
4014     POINT arrow[7];\r
4015     double dx, dy, j, k, x, y;\r
4016 \r
4017     if( d_x == s_x ) {\r
4018         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4019 \r
4020         arrow[0].x = s_x + A_WIDTH;\r
4021         arrow[0].y = s_y;\r
4022 \r
4023         arrow[1].x = s_x + A_WIDTH;\r
4024         arrow[1].y = d_y - h;\r
4025 \r
4026         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4027         arrow[2].y = d_y - h;\r
4028 \r
4029         arrow[3].x = d_x;\r
4030         arrow[3].y = d_y;\r
4031 \r
4032         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4033         arrow[4].y = d_y - h;\r
4034 \r
4035         arrow[5].x = s_x - A_WIDTH;\r
4036         arrow[5].y = d_y - h;\r
4037 \r
4038         arrow[6].x = s_x - A_WIDTH;\r
4039         arrow[6].y = s_y;\r
4040     }\r
4041     else if( d_y == s_y ) {\r
4042         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4043 \r
4044         arrow[0].x = s_x;\r
4045         arrow[0].y = s_y + A_WIDTH;\r
4046 \r
4047         arrow[1].x = d_x - w;\r
4048         arrow[1].y = s_y + A_WIDTH;\r
4049 \r
4050         arrow[2].x = d_x - w;\r
4051         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4052 \r
4053         arrow[3].x = d_x;\r
4054         arrow[3].y = d_y;\r
4055 \r
4056         arrow[4].x = d_x - w;\r
4057         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4058 \r
4059         arrow[5].x = d_x - w;\r
4060         arrow[5].y = s_y - A_WIDTH;\r
4061 \r
4062         arrow[6].x = s_x;\r
4063         arrow[6].y = s_y - A_WIDTH;\r
4064     }\r
4065     else {\r
4066         /* [AS] Needed a lot of paper for this! :-) */\r
4067         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4068         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4069   \r
4070         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4071 \r
4072         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4073 \r
4074         x = s_x;\r
4075         y = s_y;\r
4076 \r
4077         arrow[0].x = Round(x - j);\r
4078         arrow[0].y = Round(y + j*dx);\r
4079 \r
4080         arrow[1].x = Round(x + j);\r
4081         arrow[1].y = Round(y - j*dx);\r
4082 \r
4083         if( d_x > s_x ) {\r
4084             x = (double) d_x - k;\r
4085             y = (double) d_y - k*dy;\r
4086         }\r
4087         else {\r
4088             x = (double) d_x + k;\r
4089             y = (double) d_y + k*dy;\r
4090         }\r
4091 \r
4092         arrow[2].x = Round(x + j);\r
4093         arrow[2].y = Round(y - j*dx);\r
4094 \r
4095         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4096         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4097 \r
4098         arrow[4].x = d_x;\r
4099         arrow[4].y = d_y;\r
4100 \r
4101         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4102         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4103 \r
4104         arrow[6].x = Round(x - j);\r
4105         arrow[6].y = Round(y + j*dx);\r
4106     }\r
4107 \r
4108     Polygon( hdc, arrow, 7 );\r
4109 }\r
4110 \r
4111 /* [AS] Draw an arrow between two squares */\r
4112 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4113 {\r
4114     int s_x, s_y, d_x, d_y;\r
4115     HPEN hpen;\r
4116     HPEN holdpen;\r
4117     HBRUSH hbrush;\r
4118     HBRUSH holdbrush;\r
4119     LOGBRUSH stLB;\r
4120 \r
4121     if( s_col == d_col && s_row == d_row ) {\r
4122         return;\r
4123     }\r
4124 \r
4125     /* Get source and destination points */\r
4126     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4127     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4128 \r
4129     if( d_y > s_y ) {\r
4130         d_y += squareSize / 4;\r
4131     }\r
4132     else if( d_y < s_y ) {\r
4133         d_y += 3 * squareSize / 4;\r
4134     }\r
4135     else {\r
4136         d_y += squareSize / 2;\r
4137     }\r
4138 \r
4139     if( d_x > s_x ) {\r
4140         d_x += squareSize / 4;\r
4141     }\r
4142     else if( d_x < s_x ) {\r
4143         d_x += 3 * squareSize / 4;\r
4144     }\r
4145     else {\r
4146         d_x += squareSize / 2;\r
4147     }\r
4148 \r
4149     s_x += squareSize / 2;\r
4150     s_y += squareSize / 2;\r
4151 \r
4152     /* Adjust width */\r
4153     A_WIDTH = squareSize / 14;\r
4154 \r
4155     /* Draw */\r
4156     stLB.lbStyle = BS_SOLID;\r
4157     stLB.lbColor = appData.highlightArrowColor;\r
4158     stLB.lbHatch = 0;\r
4159 \r
4160     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4161     holdpen = SelectObject( hdc, hpen );\r
4162     hbrush = CreateBrushIndirect( &stLB );\r
4163     holdbrush = SelectObject( hdc, hbrush );\r
4164 \r
4165     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4166 \r
4167     SelectObject( hdc, holdpen );\r
4168     SelectObject( hdc, holdbrush );\r
4169     DeleteObject( hpen );\r
4170     DeleteObject( hbrush );\r
4171 }\r
4172 \r
4173 BOOL HasHighlightInfo()\r
4174 {\r
4175     BOOL result = FALSE;\r
4176 \r
4177     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4178         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4179     {\r
4180         result = TRUE;\r
4181     }\r
4182 \r
4183     return result;\r
4184 }\r
4185 \r
4186 BOOL IsDrawArrowEnabled()\r
4187 {\r
4188     BOOL result = FALSE;\r
4189 \r
4190     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4191         result = TRUE;\r
4192     }\r
4193 \r
4194     return result;\r
4195 }\r
4196 \r
4197 VOID DrawArrowHighlight( HDC hdc )\r
4198 {\r
4199     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4200         DrawArrowBetweenSquares( hdc,\r
4201             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4202             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4203     }\r
4204 }\r
4205 \r
4206 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4207 {\r
4208     HRGN result = NULL;\r
4209 \r
4210     if( HasHighlightInfo() ) {\r
4211         int x1, y1, x2, y2;\r
4212         int sx, sy, dx, dy;\r
4213 \r
4214         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4215         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4216 \r
4217         sx = MIN( x1, x2 );\r
4218         sy = MIN( y1, y2 );\r
4219         dx = MAX( x1, x2 ) + squareSize;\r
4220         dy = MAX( y1, y2 ) + squareSize;\r
4221 \r
4222         result = CreateRectRgn( sx, sy, dx, dy );\r
4223     }\r
4224 \r
4225     return result;\r
4226 }\r
4227 \r
4228 /*\r
4229     Warning: this function modifies the behavior of several other functions. \r
4230     \r
4231     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4232     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4233     repaint is scattered all over the place, which is not good for features such as\r
4234     "arrow highlighting" that require a full repaint of the board.\r
4235 \r
4236     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4237     user interaction, when speed is not so important) but especially to avoid errors\r
4238     in the displayed graphics.\r
4239 \r
4240     In such patched places, I always try refer to this function so there is a single\r
4241     place to maintain knowledge.\r
4242     \r
4243     To restore the original behavior, just return FALSE unconditionally.\r
4244 */\r
4245 BOOL IsFullRepaintPreferrable()\r
4246 {\r
4247     BOOL result = FALSE;\r
4248 \r
4249     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4250         /* Arrow may appear on the board */\r
4251         result = TRUE;\r
4252     }\r
4253 \r
4254     return result;\r
4255 }\r
4256 \r
4257 /* \r
4258     This function is called by DrawPosition to know whether a full repaint must\r
4259     be forced or not.\r
4260 \r
4261     Only DrawPosition may directly call this function, which makes use of \r
4262     some state information. Other function should call DrawPosition specifying \r
4263     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4264 */\r
4265 BOOL DrawPositionNeedsFullRepaint()\r
4266 {\r
4267     BOOL result = FALSE;\r
4268 \r
4269     /* \r
4270         Probably a slightly better policy would be to trigger a full repaint\r
4271         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4272         but animation is fast enough that it's difficult to notice.\r
4273     */\r
4274     if( animInfo.piece == EmptySquare ) {\r
4275         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4276             result = TRUE;\r
4277         }\r
4278     }\r
4279 \r
4280     return result;\r
4281 }\r
4282 \r
4283 VOID\r
4284 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4285 {\r
4286   int row, column, x, y, square_color, piece_color;\r
4287   ChessSquare piece;\r
4288   HBRUSH oldBrush;\r
4289   HDC texture_hdc = NULL;\r
4290 \r
4291   /* [AS] Initialize background textures if needed */\r
4292   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4293       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4294       if( backTextureSquareSize != squareSize \r
4295        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4296           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4297           backTextureSquareSize = squareSize;\r
4298           RebuildTextureSquareInfo();\r
4299       }\r
4300 \r
4301       texture_hdc = CreateCompatibleDC( hdc );\r
4302   }\r
4303 \r
4304   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4305     for (column = 0; column < BOARD_WIDTH; column++) {\r
4306   \r
4307       SquareToPos(row, column, &x, &y);\r
4308 \r
4309       piece = board[row][column];\r
4310 \r
4311       square_color = ((column + row) % 2) == 1;\r
4312       if( gameInfo.variant == VariantXiangqi ) {\r
4313           square_color = !InPalace(row, column);\r
4314           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4315           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4316       }\r
4317       piece_color = (int) piece < (int) BlackPawn;\r
4318 \r
4319 \r
4320       /* [HGM] holdings file: light square or black */\r
4321       if(column == BOARD_LEFT-2) {\r
4322             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4323                 square_color = 1;\r
4324             else {\r
4325                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4326                 continue;\r
4327             }\r
4328       } else\r
4329       if(column == BOARD_RGHT + 1 ) {\r
4330             if( row < gameInfo.holdingsSize )\r
4331                 square_color = 1;\r
4332             else {\r
4333                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4334                 continue;\r
4335             }\r
4336       }\r
4337       if(column == BOARD_LEFT-1 ) /* left align */\r
4338             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4339       else if( column == BOARD_RGHT) /* right align */\r
4340             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4341       else\r
4342       if (appData.monoMode) {\r
4343         if (piece == EmptySquare) {\r
4344           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4345                  square_color ? WHITENESS : BLACKNESS);\r
4346         } else {\r
4347           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4348         }\r
4349       } \r
4350       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4351           /* [AS] Draw the square using a texture bitmap */\r
4352           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4353           int r = row, c = column; // [HGM] do not flip board in flipView\r
4354           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4355 \r
4356           DrawTile( x, y, \r
4357               squareSize, squareSize, \r
4358               hdc, \r
4359               texture_hdc,\r
4360               backTextureSquareInfo[r][c].mode,\r
4361               backTextureSquareInfo[r][c].x,\r
4362               backTextureSquareInfo[r][c].y );\r
4363 \r
4364           SelectObject( texture_hdc, hbm );\r
4365 \r
4366           if (piece != EmptySquare) {\r
4367               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4368           }\r
4369       }\r
4370       else {\r
4371         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4372 \r
4373         oldBrush = SelectObject(hdc, brush );\r
4374         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4375         SelectObject(hdc, oldBrush);\r
4376         if (piece != EmptySquare)\r
4377           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4378       }\r
4379     }\r
4380   }\r
4381 \r
4382   if( texture_hdc != NULL ) {\r
4383     DeleteDC( texture_hdc );\r
4384   }\r
4385 }\r
4386 \r
4387 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4388 void fputDW(FILE *f, int x)\r
4389 {\r
4390         fputc(x     & 255, f);\r
4391         fputc(x>>8  & 255, f);\r
4392         fputc(x>>16 & 255, f);\r
4393         fputc(x>>24 & 255, f);\r
4394 }\r
4395 \r
4396 #define MAX_CLIPS 200   /* more than enough */\r
4397 \r
4398 VOID\r
4399 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4400 {\r
4401 //  HBITMAP bufferBitmap;\r
4402   BITMAP bi;\r
4403 //  RECT Rect;\r
4404   HDC tmphdc;\r
4405   HBITMAP hbm;\r
4406   int w = 100, h = 50;\r
4407 \r
4408   if(logo == NULL) return;\r
4409 //  GetClientRect(hwndMain, &Rect);\r
4410 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4411 //                                      Rect.bottom-Rect.top+1);\r
4412   tmphdc = CreateCompatibleDC(hdc);\r
4413   hbm = SelectObject(tmphdc, logo);\r
4414   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4415             w = bi.bmWidth;\r
4416             h = bi.bmHeight;\r
4417   }\r
4418   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4419                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4420   SelectObject(tmphdc, hbm);\r
4421   DeleteDC(tmphdc);\r
4422 }\r
4423 \r
4424 VOID\r
4425 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4426 {\r
4427   static Board lastReq, lastDrawn;\r
4428   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4429   static int lastDrawnFlipView = 0;\r
4430   static int lastReqValid = 0, lastDrawnValid = 0;\r
4431   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4432   HDC tmphdc;\r
4433   HDC hdcmem;\r
4434   HBITMAP bufferBitmap;\r
4435   HBITMAP oldBitmap;\r
4436   RECT Rect;\r
4437   HRGN clips[MAX_CLIPS];\r
4438   ChessSquare dragged_piece = EmptySquare;\r
4439 \r
4440   /* I'm undecided on this - this function figures out whether a full\r
4441    * repaint is necessary on its own, so there's no real reason to have the\r
4442    * caller tell it that.  I think this can safely be set to FALSE - but\r
4443    * if we trust the callers not to request full repaints unnessesarily, then\r
4444    * we could skip some clipping work.  In other words, only request a full\r
4445    * redraw when the majority of pieces have changed positions (ie. flip, \r
4446    * gamestart and similar)  --Hawk\r
4447    */\r
4448   Boolean fullrepaint = repaint;\r
4449 \r
4450   if( DrawPositionNeedsFullRepaint() ) {\r
4451       fullrepaint = TRUE;\r
4452   }\r
4453 \r
4454 #if 0\r
4455   if( fullrepaint ) {\r
4456       static int repaint_count = 0;\r
4457       char buf[128];\r
4458 \r
4459       repaint_count++;\r
4460       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4461       OutputDebugString( buf );\r
4462   }\r
4463 #endif\r
4464 \r
4465   if (board == NULL) {\r
4466     if (!lastReqValid) {\r
4467       return;\r
4468     }\r
4469     board = lastReq;\r
4470   } else {\r
4471     CopyBoard(lastReq, board);\r
4472     lastReqValid = 1;\r
4473   }\r
4474 \r
4475   if (doingSizing) {\r
4476     return;\r
4477   }\r
4478 \r
4479   if (IsIconic(hwndMain)) {\r
4480     return;\r
4481   }\r
4482 \r
4483   if (hdc == NULL) {\r
4484     hdc = GetDC(hwndMain);\r
4485     if (!appData.monoMode) {\r
4486       SelectPalette(hdc, hPal, FALSE);\r
4487       RealizePalette(hdc);\r
4488     }\r
4489     releaseDC = TRUE;\r
4490   } else {\r
4491     releaseDC = FALSE;\r
4492   }\r
4493 \r
4494 #if 0\r
4495   fprintf(debugFP, "*******************************\n"\r
4496                    "repaint = %s\n"\r
4497                    "dragInfo.from (%d,%d)\n"\r
4498                    "dragInfo.start (%d,%d)\n"\r
4499                    "dragInfo.pos (%d,%d)\n"\r
4500                    "dragInfo.lastpos (%d,%d)\n", \r
4501                     repaint ? "TRUE" : "FALSE",\r
4502                     dragInfo.from.x, dragInfo.from.y, \r
4503                     dragInfo.start.x, dragInfo.start.y,\r
4504                     dragInfo.pos.x, dragInfo.pos.y,\r
4505                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4506   fprintf(debugFP, "prev:  ");\r
4507   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4508     for (column = 0; column < BOARD_WIDTH; column++) {\r
4509       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4510     }\r
4511   }\r
4512   fprintf(debugFP, "\n");\r
4513   fprintf(debugFP, "board: ");\r
4514   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4515     for (column = 0; column < BOARD_WIDTH; column++) {\r
4516       fprintf(debugFP, "%d ", board[row][column]);\r
4517     }\r
4518   }\r
4519   fprintf(debugFP, "\n");\r
4520   fflush(debugFP);\r
4521 #endif\r
4522 \r
4523   /* Create some work-DCs */\r
4524   hdcmem = CreateCompatibleDC(hdc);\r
4525   tmphdc = CreateCompatibleDC(hdc);\r
4526 \r
4527   /* If dragging is in progress, we temporarely remove the piece */\r
4528   /* [HGM] or temporarily decrease count if stacked              */\r
4529   /*       !! Moved to before board compare !!                   */\r
4530   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4531     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4532     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4533             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4534         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4535     } else \r
4536     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4537             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4538         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4539     } else \r
4540         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4541   }\r
4542 \r
4543   /* Figure out which squares need updating by comparing the \r
4544    * newest board with the last drawn board and checking if\r
4545    * flipping has changed.\r
4546    */\r
4547   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4548     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4549       for (column = 0; column < BOARD_WIDTH; column++) {\r
4550         if (lastDrawn[row][column] != board[row][column]) {\r
4551           SquareToPos(row, column, &x, &y);\r
4552           clips[num_clips++] =\r
4553             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4554         }\r
4555       }\r
4556     }\r
4557     for (i=0; i<2; i++) {\r
4558       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4559           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4560         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4561             lastDrawnHighlight.sq[i].y >= 0) {\r
4562           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4563                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4564           clips[num_clips++] =\r
4565             CreateRectRgn(x - lineGap, y - lineGap, \r
4566                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4567         }\r
4568         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4569           SquareToPos(highlightInfo.sq[i].y, highlightInfo.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       }\r
4575     }\r
4576     for (i=0; i<2; i++) {\r
4577       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4578           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4579         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4580             lastDrawnPremove.sq[i].y >= 0) {\r
4581           SquareToPos(lastDrawnPremove.sq[i].y,\r
4582                       lastDrawnPremove.sq[i].x, &x, &y);\r
4583           clips[num_clips++] =\r
4584             CreateRectRgn(x - lineGap, y - lineGap, \r
4585                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4586         }\r
4587         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4588             premoveHighlightInfo.sq[i].y >= 0) {\r
4589           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4590                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4591           clips[num_clips++] =\r
4592             CreateRectRgn(x - lineGap, y - lineGap, \r
4593                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4594         }\r
4595       }\r
4596     }\r
4597   } else {\r
4598     fullrepaint = TRUE;\r
4599   }\r
4600 \r
4601   /* Create a buffer bitmap - this is the actual bitmap\r
4602    * being written to.  When all the work is done, we can\r
4603    * copy it to the real DC (the screen).  This avoids\r
4604    * the problems with flickering.\r
4605    */\r
4606   GetClientRect(hwndMain, &Rect);\r
4607   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4608                                         Rect.bottom-Rect.top+1);\r
4609   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4610   if (!appData.monoMode) {\r
4611     SelectPalette(hdcmem, hPal, FALSE);\r
4612   }\r
4613 \r
4614   /* Create clips for dragging */\r
4615   if (!fullrepaint) {\r
4616     if (dragInfo.from.x >= 0) {\r
4617       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4618       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4619     }\r
4620     if (dragInfo.start.x >= 0) {\r
4621       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4622       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4623     }\r
4624     if (dragInfo.pos.x >= 0) {\r
4625       x = dragInfo.pos.x - squareSize / 2;\r
4626       y = dragInfo.pos.y - squareSize / 2;\r
4627       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4628     }\r
4629     if (dragInfo.lastpos.x >= 0) {\r
4630       x = dragInfo.lastpos.x - squareSize / 2;\r
4631       y = dragInfo.lastpos.y - squareSize / 2;\r
4632       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4633     }\r
4634   }\r
4635 \r
4636   /* Are we animating a move?  \r
4637    * If so, \r
4638    *   - remove the piece from the board (temporarely)\r
4639    *   - calculate the clipping region\r
4640    */\r
4641   if (!fullrepaint) {\r
4642     if (animInfo.piece != EmptySquare) {\r
4643       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4644       x = boardRect.left + animInfo.lastpos.x;\r
4645       y = boardRect.top + animInfo.lastpos.y;\r
4646       x2 = boardRect.left + animInfo.pos.x;\r
4647       y2 = boardRect.top + animInfo.pos.y;\r
4648       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4649       /* Slight kludge.  The real problem is that after AnimateMove is\r
4650          done, the position on the screen does not match lastDrawn.\r
4651          This currently causes trouble only on e.p. captures in\r
4652          atomic, where the piece moves to an empty square and then\r
4653          explodes.  The old and new positions both had an empty square\r
4654          at the destination, but animation has drawn a piece there and\r
4655          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4656       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4657     }\r
4658   }\r
4659 \r
4660   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4661   if (num_clips == 0)\r
4662     fullrepaint = TRUE;\r
4663 \r
4664   /* Set clipping on the memory DC */\r
4665   if (!fullrepaint) {\r
4666     SelectClipRgn(hdcmem, clips[0]);\r
4667     for (x = 1; x < num_clips; x++) {\r
4668       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4669         abort();  // this should never ever happen!\r
4670     }\r
4671   }\r
4672 \r
4673   /* Do all the drawing to the memory DC */\r
4674   if(explodeInfo.radius) { // [HGM] atomic\r
4675         HBRUSH oldBrush;\r
4676         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4677         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4678         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4679         x += squareSize/2;\r
4680         y += squareSize/2;\r
4681         if(!fullrepaint) {\r
4682           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4683           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4684         }\r
4685         DrawGridOnDC(hdcmem);\r
4686         DrawHighlightsOnDC(hdcmem);\r
4687         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4688         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4689         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4690         SelectObject(hdcmem, oldBrush);\r
4691   } else {\r
4692     DrawGridOnDC(hdcmem);\r
4693     DrawHighlightsOnDC(hdcmem);\r
4694     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4695   }\r
4696   if(logoHeight) {\r
4697         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4698         if(appData.autoLogo) {\r
4699           \r
4700           switch(gameMode) { // pick logos based on game mode\r
4701             case IcsObserving:\r
4702                 whiteLogo = second.programLogo; // ICS logo\r
4703                 blackLogo = second.programLogo;\r
4704             default:\r
4705                 break;\r
4706             case IcsPlayingWhite:\r
4707                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4708                 blackLogo = second.programLogo; // ICS logo\r
4709                 break;\r
4710             case IcsPlayingBlack:\r
4711                 whiteLogo = second.programLogo; // ICS logo\r
4712                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4713                 break;\r
4714             case TwoMachinesPlay:\r
4715                 if(first.twoMachinesColor[0] == 'b') {\r
4716                     whiteLogo = second.programLogo;\r
4717                     blackLogo = first.programLogo;\r
4718                 }\r
4719                 break;\r
4720             case MachinePlaysWhite:\r
4721                 blackLogo = userLogo;\r
4722                 break;\r
4723             case MachinePlaysBlack:\r
4724                 whiteLogo = userLogo;\r
4725                 blackLogo = first.programLogo;\r
4726           }\r
4727         }\r
4728         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4729         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4730   }\r
4731 \r
4732   if( appData.highlightMoveWithArrow ) {\r
4733     DrawArrowHighlight(hdcmem);\r
4734   }\r
4735 \r
4736   DrawCoordsOnDC(hdcmem);\r
4737 \r
4738   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4739                  /* to make sure lastDrawn contains what is actually drawn */\r
4740 \r
4741   /* Put the dragged piece back into place and draw it (out of place!) */\r
4742     if (dragged_piece != EmptySquare) {\r
4743     /* [HGM] or restack */\r
4744     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4745                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4746     else\r
4747     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4748                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4749     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4750     x = dragInfo.pos.x - squareSize / 2;\r
4751     y = dragInfo.pos.y - squareSize / 2;\r
4752     DrawPieceOnDC(hdcmem, dragged_piece,\r
4753                   ((int) dragged_piece < (int) BlackPawn), \r
4754                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4755   }   \r
4756   \r
4757   /* Put the animated piece back into place and draw it */\r
4758   if (animInfo.piece != EmptySquare) {\r
4759     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4760     x = boardRect.left + animInfo.pos.x;\r
4761     y = boardRect.top + animInfo.pos.y;\r
4762     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4763                   ((int) animInfo.piece < (int) BlackPawn),\r
4764                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4765   }\r
4766 \r
4767   /* Release the bufferBitmap by selecting in the old bitmap \r
4768    * and delete the memory DC\r
4769    */\r
4770   SelectObject(hdcmem, oldBitmap);\r
4771   DeleteDC(hdcmem);\r
4772 \r
4773   /* Set clipping on the target DC */\r
4774   if (!fullrepaint) {\r
4775     SelectClipRgn(hdc, clips[0]);\r
4776     for (x = 1; x < num_clips; x++) {\r
4777       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4778         abort();   // this should never ever happen!\r
4779     } \r
4780   }\r
4781 \r
4782   /* Copy the new bitmap onto the screen in one go.\r
4783    * This way we avoid any flickering\r
4784    */\r
4785   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4786   BitBlt(hdc, boardRect.left, boardRect.top,\r
4787          boardRect.right - boardRect.left,\r
4788          boardRect.bottom - boardRect.top,\r
4789          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4790   if(saveDiagFlag) { \r
4791     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4792     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4793 \r
4794     GetObject(bufferBitmap, sizeof(b), &b);\r
4795     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4796         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4797         bih.biWidth = b.bmWidth;\r
4798         bih.biHeight = b.bmHeight;\r
4799         bih.biPlanes = 1;\r
4800         bih.biBitCount = b.bmBitsPixel;\r
4801         bih.biCompression = 0;\r
4802         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4803         bih.biXPelsPerMeter = 0;\r
4804         bih.biYPelsPerMeter = 0;\r
4805         bih.biClrUsed = 0;\r
4806         bih.biClrImportant = 0;\r
4807 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4808 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4809         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4810 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4811 \r
4812 #if 1\r
4813         wb = b.bmWidthBytes;\r
4814         // count colors\r
4815         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4816                 int k = ((int*) pData)[i];\r
4817                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4818                 if(j >= 16) break;\r
4819                 color[j] = k;\r
4820                 if(j >= nrColors) nrColors = j+1;\r
4821         }\r
4822         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4823                 INT p = 0;\r
4824                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4825                     for(w=0; w<(wb>>2); w+=2) {\r
4826                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4827                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4828                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4829                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4830                         pData[p++] = m | j<<4;\r
4831                     }\r
4832                     while(p&3) pData[p++] = 0;\r
4833                 }\r
4834                 fac = 3;\r
4835                 wb = ((wb+31)>>5)<<2;\r
4836         }\r
4837         // write BITMAPFILEHEADER\r
4838         fprintf(diagFile, "BM");\r
4839         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4840         fputDW(diagFile, 0);\r
4841         fputDW(diagFile, 0x36 + (fac?64:0));\r
4842         // write BITMAPINFOHEADER\r
4843         fputDW(diagFile, 40);\r
4844         fputDW(diagFile, b.bmWidth);\r
4845         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4846         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4847         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4848         fputDW(diagFile, 0);\r
4849         fputDW(diagFile, 0);\r
4850         fputDW(diagFile, 0);\r
4851         fputDW(diagFile, 0);\r
4852         fputDW(diagFile, 0);\r
4853         fputDW(diagFile, 0);\r
4854         // write color table\r
4855         if(fac)\r
4856         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4857         // write bitmap data\r
4858         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4859                 fputc(pData[i], diagFile);\r
4860 #endif\r
4861      }\r
4862   }\r
4863 \r
4864   SelectObject(tmphdc, oldBitmap);\r
4865 \r
4866   /* Massive cleanup */\r
4867   for (x = 0; x < num_clips; x++)\r
4868     DeleteObject(clips[x]);\r
4869 \r
4870   DeleteDC(tmphdc);\r
4871   DeleteObject(bufferBitmap);\r
4872 \r
4873   if (releaseDC) \r
4874     ReleaseDC(hwndMain, hdc);\r
4875   \r
4876   if (lastDrawnFlipView != flipView) {\r
4877     if (flipView)\r
4878       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4879     else\r
4880       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4881   }\r
4882 \r
4883 /*  CopyBoard(lastDrawn, board);*/\r
4884   lastDrawnHighlight = highlightInfo;\r
4885   lastDrawnPremove   = premoveHighlightInfo;\r
4886   lastDrawnFlipView = flipView;\r
4887   lastDrawnValid = 1;\r
4888 }\r
4889 \r
4890 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4891 int\r
4892 SaveDiagram(f)\r
4893      FILE *f;\r
4894 {\r
4895     saveDiagFlag = 1; diagFile = f;\r
4896     HDCDrawPosition(NULL, TRUE, NULL);\r
4897 \r
4898     saveDiagFlag = 0;\r
4899 \r
4900 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4901     \r
4902     fclose(f);\r
4903     return TRUE;\r
4904 }\r
4905 \r
4906 \r
4907 /*---------------------------------------------------------------------------*\\r
4908 | CLIENT PAINT PROCEDURE\r
4909 |   This is the main event-handler for the WM_PAINT message.\r
4910 |\r
4911 \*---------------------------------------------------------------------------*/\r
4912 VOID\r
4913 PaintProc(HWND hwnd)\r
4914 {\r
4915   HDC         hdc;\r
4916   PAINTSTRUCT ps;\r
4917   HFONT       oldFont;\r
4918 \r
4919   if((hdc = BeginPaint(hwnd, &ps))) {\r
4920     if (IsIconic(hwnd)) {\r
4921       DrawIcon(hdc, 2, 2, iconCurrent);\r
4922     } else {\r
4923       if (!appData.monoMode) {\r
4924         SelectPalette(hdc, hPal, FALSE);\r
4925         RealizePalette(hdc);\r
4926       }\r
4927       HDCDrawPosition(hdc, 1, NULL);\r
4928       oldFont =\r
4929         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4930       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4931                  ETO_CLIPPED|ETO_OPAQUE,\r
4932                  &messageRect, messageText, strlen(messageText), NULL);\r
4933       SelectObject(hdc, oldFont);\r
4934       DisplayBothClocks();\r
4935     }\r
4936     EndPaint(hwnd,&ps);\r
4937   }\r
4938 \r
4939   return;\r
4940 }\r
4941 \r
4942 \r
4943 /*\r
4944  * If the user selects on a border boundary, return -1; if off the board,\r
4945  *   return -2.  Otherwise map the event coordinate to the square.\r
4946  * The offset boardRect.left or boardRect.top must already have been\r
4947  *   subtracted from x.\r
4948  */\r
4949 int\r
4950 EventToSquare(int x)\r
4951 {\r
4952   if (x <= 0)\r
4953     return -2;\r
4954   if (x < lineGap)\r
4955     return -1;\r
4956   x -= lineGap;\r
4957   if ((x % (squareSize + lineGap)) >= squareSize)\r
4958     return -1;\r
4959   x /= (squareSize + lineGap);\r
4960   if (x >= BOARD_SIZE)\r
4961     return -2;\r
4962   return x;\r
4963 }\r
4964 \r
4965 typedef struct {\r
4966   char piece;\r
4967   int command;\r
4968   char* name;\r
4969 } DropEnable;\r
4970 \r
4971 DropEnable dropEnables[] = {\r
4972   { 'P', DP_Pawn, "Pawn" },\r
4973   { 'N', DP_Knight, "Knight" },\r
4974   { 'B', DP_Bishop, "Bishop" },\r
4975   { 'R', DP_Rook, "Rook" },\r
4976   { 'Q', DP_Queen, "Queen" },\r
4977 };\r
4978 \r
4979 VOID\r
4980 SetupDropMenu(HMENU hmenu)\r
4981 {\r
4982   int i, count, enable;\r
4983   char *p;\r
4984   extern char white_holding[], black_holding[];\r
4985   char item[MSG_SIZ];\r
4986 \r
4987   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4988     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4989                dropEnables[i].piece);\r
4990     count = 0;\r
4991     while (p && *p++ == dropEnables[i].piece) count++;\r
4992     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4993     enable = count > 0 || !appData.testLegality\r
4994       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4995                       && !appData.icsActive);\r
4996     ModifyMenu(hmenu, dropEnables[i].command,\r
4997                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4998                dropEnables[i].command, item);\r
4999   }\r
5000 }\r
5001 \r
5002 /* Event handler for mouse messages */\r
5003 VOID\r
5004 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5005 {\r
5006   int x, y;\r
5007   POINT pt;\r
5008   static int recursive = 0;\r
5009   HMENU hmenu;\r
5010 //  BOOLEAN needsRedraw = FALSE;\r
5011   BOOLEAN saveAnimate;\r
5012   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5013   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5014   ChessMove moveType;\r
5015 \r
5016   if (recursive) {\r
5017     if (message == WM_MBUTTONUP) {\r
5018       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5019          to the middle button: we simulate pressing the left button too!\r
5020          */\r
5021       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5022       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5023     }\r
5024     return;\r
5025   }\r
5026   recursive++;\r
5027   \r
5028   pt.x = LOWORD(lParam);\r
5029   pt.y = HIWORD(lParam);\r
5030   x = EventToSquare(pt.x - boardRect.left);\r
5031   y = EventToSquare(pt.y - boardRect.top);\r
5032   if (!flipView && y >= 0) {\r
5033     y = BOARD_HEIGHT - 1 - y;\r
5034   }\r
5035   if (flipView && x >= 0) {\r
5036     x = BOARD_WIDTH - 1 - x;\r
5037   }\r
5038 \r
5039   switch (message) {\r
5040   case WM_LBUTTONDOWN:\r
5041     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5042         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5043         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5044         if(gameInfo.holdingsWidth && \r
5045                 (WhiteOnMove(currentMove) \r
5046                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5047                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5048             // click in right holdings, for determining promotion piece\r
5049             ChessSquare p = boards[currentMove][y][x];\r
5050             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5051             if(p != EmptySquare) {\r
5052                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5053                 fromX = fromY = -1;\r
5054                 break;\r
5055             }\r
5056         }\r
5057         DrawPosition(FALSE, boards[currentMove]);\r
5058         break;\r
5059     }\r
5060     ErrorPopDown();\r
5061     sameAgain = FALSE;\r
5062     if (y == -2) {\r
5063       /* Downclick vertically off board; check if on clock */\r
5064       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5065         if (gameMode == EditPosition) {\r
5066           SetWhiteToPlayEvent();\r
5067         } else if (gameMode == IcsPlayingBlack ||\r
5068                    gameMode == MachinePlaysWhite) {\r
5069           CallFlagEvent();\r
5070         } else if (gameMode == EditGame) {\r
5071           AdjustClock(flipClock, -1);\r
5072         }\r
5073       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5074         if (gameMode == EditPosition) {\r
5075           SetBlackToPlayEvent();\r
5076         } else if (gameMode == IcsPlayingWhite ||\r
5077                    gameMode == MachinePlaysBlack) {\r
5078           CallFlagEvent();\r
5079         } else if (gameMode == EditGame) {\r
5080           AdjustClock(!flipClock, -1);\r
5081         }\r
5082       }\r
5083       if (!appData.highlightLastMove) {\r
5084         ClearHighlights();\r
5085         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5086       }\r
5087       fromX = fromY = -1;\r
5088       dragInfo.start.x = dragInfo.start.y = -1;\r
5089       dragInfo.from = dragInfo.start;\r
5090       break;\r
5091     } else if (x < 0 || y < 0\r
5092       /* [HGM] block clicks between board and holdings */\r
5093               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5094               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5095               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5096         /* EditPosition, empty square, or different color piece;\r
5097            click-click move is possible */\r
5098                                ) {\r
5099       break;\r
5100     } else if (fromX == x && fromY == y) {\r
5101       /* Downclick on same square again */\r
5102       ClearHighlights();\r
5103       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5104       sameAgain = TRUE;  \r
5105     } else if (fromX != -1 &&\r
5106                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5107                                                                         ) {\r
5108       /* Downclick on different square. */\r
5109       /* [HGM] if on holdings file, should count as new first click ! */\r
5110       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5111         toX = x;\r
5112         toY = y;\r
5113         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5114            to make sure move is legal before showing promotion popup */\r
5115         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5116         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5117                 fromX = fromY = -1; \r
5118                 ClearHighlights();\r
5119                 DrawPosition(FALSE, boards[currentMove]);\r
5120                 break; \r
5121         } else \r
5122         if(moveType != ImpossibleMove) {\r
5123           if(moveType == IllegalMove) {\r
5124                 ;\r
5125           } else\r
5126           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5127           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5128             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5129               appData.alwaysPromoteToQueen)) {\r
5130                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5131                   if (!appData.highlightLastMove) {\r
5132                       ClearHighlights();\r
5133                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5134                   }\r
5135           } else\r
5136           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5137                   SetHighlights(fromX, fromY, toX, toY);\r
5138                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5139                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5140                      If promotion to Q is legal, all are legal! */\r
5141                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5142                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5143                     // kludge to temporarily execute move on display, without promoting yet\r
5144                     promotionChoice = TRUE;\r
5145                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5146                     boards[currentMove][toY][toX] = p;\r
5147                     DrawPosition(FALSE, boards[currentMove]);\r
5148                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5149                     boards[currentMove][toY][toX] = q;\r
5150                   } else\r
5151                   PromotionPopup(hwnd);\r
5152           } else {       /* not a promotion */\r
5153              if (appData.animate || appData.highlightLastMove) {\r
5154                  SetHighlights(fromX, fromY, toX, toY);\r
5155              } else {\r
5156                  ClearHighlights();\r
5157              }\r
5158              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5159              if (appData.animate && !appData.highlightLastMove) {\r
5160                   ClearHighlights();\r
5161                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5162              }\r
5163           }\r
5164           fromX = fromY = -1;\r
5165           break;\r
5166         }\r
5167         if (gotPremove) {\r
5168             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5169             DrawPosition(forceFullRepaint || FALSE, NULL);\r
5170         } else ClearHighlights();\r
5171         fromX = fromY = -1;\r
5172         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5173     }\r
5174     /* First downclick, or restart on a square with same color piece */\r
5175     if (!frozen && OKToStartUserMove(x, y)) {\r
5176       fromX = x;\r
5177       fromY = y;\r
5178       dragInfo.lastpos = pt;\r
5179       dragInfo.from.x = fromX;\r
5180       dragInfo.from.y = fromY;\r
5181       dragInfo.start = dragInfo.from;\r
5182       SetCapture(hwndMain);\r
5183     } else {\r
5184       fromX = fromY = -1;\r
5185       dragInfo.start.x = dragInfo.start.y = -1;\r
5186       dragInfo.from = dragInfo.start;\r
5187       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5188     }\r
5189     break;\r
5190 \r
5191   case WM_LBUTTONUP:\r
5192     ReleaseCapture();\r
5193     if (fromX == -1) break;\r
5194     if (x == fromX && y == fromY) {\r
5195       dragInfo.from.x = dragInfo.from.y = -1;\r
5196       /* Upclick on same square */\r
5197       if (sameAgain) {\r
5198         /* Clicked same square twice: abort click-click move */\r
5199         fromX = fromY = -1;\r
5200         gotPremove = 0;\r
5201         ClearPremoveHighlights();\r
5202       } else {\r
5203         /* First square clicked: start click-click move */\r
5204         SetHighlights(fromX, fromY, -1, -1);\r
5205       }\r
5206       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5207     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5208       /* Errant click; ignore */\r
5209       break;\r
5210     } else {\r
5211       /* Finish drag move. */\r
5212     if (appData.debugMode) {\r
5213         fprintf(debugFP, "release\n");\r
5214     }\r
5215       dragInfo.from.x = dragInfo.from.y = -1;\r
5216       toX = x;\r
5217       toY = y;\r
5218       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5219       appData.animate = appData.animate && !appData.animateDragging;\r
5220       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5221       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5222                 fromX = fromY = -1; \r
5223                 ClearHighlights();\r
5224                 DrawPosition(FALSE, boards[currentMove]);\r
5225                 appData.animate = saveAnimate;\r
5226                 break; \r
5227       } else \r
5228       if(moveType != ImpossibleMove) {\r
5229           /* [HGM] use move type to determine if move is promotion.\r
5230              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5231           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5232             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5233               appData.alwaysPromoteToQueen)) \r
5234                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5235           else \r
5236           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5237                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5238                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5239                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5240                     // kludge to temporarily execute move on display, wthout promotng yet\r
5241                     promotionChoice = TRUE;\r
5242                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5243                     boards[currentMove][toY][toX] = p;\r
5244                     DrawPosition(FALSE, boards[currentMove]);\r
5245                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5246                     boards[currentMove][toY][toX] = q;\r
5247                     appData.animate = saveAnimate;\r
5248                     break;\r
5249                   } else\r
5250                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5251           } else {\r
5252             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5253                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5254                                         moveType == WhiteCapturesEnPassant || \r
5255                                         moveType == BlackCapturesEnPassant   ) )\r
5256                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5257             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5258           }\r
5259       }\r
5260       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5261       appData.animate = saveAnimate;\r
5262       fromX = fromY = -1;\r
5263       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5264         ClearHighlights();\r
5265       }\r
5266       if (appData.animate || appData.animateDragging ||\r
5267           appData.highlightDragging || gotPremove) {\r
5268         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5269       }\r
5270     }\r
5271     dragInfo.start.x = dragInfo.start.y = -1; \r
5272     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5273     break;\r
5274 \r
5275   case WM_MOUSEMOVE:\r
5276     if ((appData.animateDragging || appData.highlightDragging)\r
5277         && (wParam & MK_LBUTTON)\r
5278         && dragInfo.from.x >= 0) \r
5279     {\r
5280       BOOL full_repaint = FALSE;\r
5281 \r
5282       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5283       if (appData.animateDragging) {\r
5284         dragInfo.pos = pt;\r
5285       }\r
5286       if (appData.highlightDragging) {\r
5287         SetHighlights(fromX, fromY, x, y);\r
5288         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5289             full_repaint = TRUE;\r
5290         }\r
5291       }\r
5292       \r
5293       DrawPosition( full_repaint, NULL);\r
5294       \r
5295       dragInfo.lastpos = dragInfo.pos;\r
5296     }\r
5297     break;\r
5298 \r
5299   case WM_MOUSEWHEEL: // [DM]\r
5300     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5301        /* Mouse Wheel is being rolled forward\r
5302         * Play moves forward\r
5303         */\r
5304        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5305                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5306        /* Mouse Wheel is being rolled backward\r
5307         * Play moves backward\r
5308         */\r
5309        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5310                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5311     }\r
5312     break;\r
5313 \r
5314   case WM_MBUTTONDOWN:\r
5315   case WM_RBUTTONDOWN:\r
5316     ErrorPopDown();\r
5317     ReleaseCapture();\r
5318     fromX = fromY = -1;\r
5319     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5320     dragInfo.start.x = dragInfo.start.y = -1;\r
5321     dragInfo.from = dragInfo.start;\r
5322     dragInfo.lastpos = dragInfo.pos;\r
5323     if (appData.highlightDragging) {\r
5324       ClearHighlights();\r
5325     }\r
5326     if(y == -2) {\r
5327       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5328       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5329           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5330       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5331           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5332       }\r
5333     }\r
5334     DrawPosition(TRUE, NULL);\r
5335 \r
5336     switch (gameMode) {\r
5337     case EditPosition:\r
5338     case IcsExamining:\r
5339       if (x < 0 || y < 0) break;\r
5340       fromX = x;\r
5341       fromY = y;\r
5342       if (message == WM_MBUTTONDOWN) {\r
5343         buttonCount = 3;  /* even if system didn't think so */\r
5344         if (wParam & MK_SHIFT) \r
5345           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5346         else\r
5347           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5348       } else { /* message == WM_RBUTTONDOWN */\r
5349 #if 0\r
5350         if (buttonCount == 3) {\r
5351           if (wParam & MK_SHIFT) \r
5352             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5353           else\r
5354             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5355         } else {\r
5356           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5357         }\r
5358 #else\r
5359         /* Just have one menu, on the right button.  Windows users don't\r
5360            think to try the middle one, and sometimes other software steals\r
5361            it, or it doesn't really exist. */\r
5362         if(gameInfo.variant != VariantShogi)\r
5363             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5364         else\r
5365             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5366 #endif\r
5367       }\r
5368       break;\r
5369     case IcsPlayingWhite:\r
5370     case IcsPlayingBlack:\r
5371     case EditGame:\r
5372     case MachinePlaysWhite:\r
5373     case MachinePlaysBlack:\r
5374       if (appData.testLegality &&\r
5375           gameInfo.variant != VariantBughouse &&\r
5376           gameInfo.variant != VariantCrazyhouse) break;\r
5377       if (x < 0 || y < 0) break;\r
5378       fromX = x;\r
5379       fromY = y;\r
5380       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5381       SetupDropMenu(hmenu);\r
5382       MenuPopup(hwnd, pt, hmenu, -1);\r
5383       break;\r
5384     default:\r
5385       break;\r
5386     }\r
5387     break;\r
5388   }\r
5389 \r
5390   recursive--;\r
5391 }\r
5392 \r
5393 /* Preprocess messages for buttons in main window */\r
5394 LRESULT CALLBACK\r
5395 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5396 {\r
5397   int id = GetWindowLong(hwnd, GWL_ID);\r
5398   int i, dir;\r
5399 \r
5400   for (i=0; i<N_BUTTONS; i++) {\r
5401     if (buttonDesc[i].id == id) break;\r
5402   }\r
5403   if (i == N_BUTTONS) return 0;\r
5404   switch (message) {\r
5405   case WM_KEYDOWN:\r
5406     switch (wParam) {\r
5407     case VK_LEFT:\r
5408     case VK_RIGHT:\r
5409       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5410       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5411       return TRUE;\r
5412     }\r
5413     break;\r
5414   case WM_CHAR:\r
5415     switch (wParam) {\r
5416     case '\r':\r
5417       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5418       return TRUE;\r
5419     default:\r
5420       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5421         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5422         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5423         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5424         SetFocus(h);\r
5425         SendMessage(h, WM_CHAR, wParam, lParam);\r
5426         return TRUE;\r
5427       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5428         PopUpMoveDialog((char)wParam);\r
5429       }\r
5430       break;\r
5431     }\r
5432     break;\r
5433   }\r
5434   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5435 }\r
5436 \r
5437 /* Process messages for Promotion dialog box */\r
5438 LRESULT CALLBACK\r
5439 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5440 {\r
5441   char promoChar;\r
5442 \r
5443   switch (message) {\r
5444   case WM_INITDIALOG: /* message: initialize dialog box */\r
5445     /* Center the dialog over the application window */\r
5446     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5447     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5448       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5449        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5450                SW_SHOW : SW_HIDE);\r
5451     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5452     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5453        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5454          PieceToChar(WhiteAngel) != '~') ||\r
5455         (PieceToChar(BlackAngel) >= 'A' &&\r
5456          PieceToChar(BlackAngel) != '~')   ) ?\r
5457                SW_SHOW : SW_HIDE);\r
5458     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5459        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5460          PieceToChar(WhiteMarshall) != '~') ||\r
5461         (PieceToChar(BlackMarshall) >= 'A' &&\r
5462          PieceToChar(BlackMarshall) != '~')   ) ?\r
5463                SW_SHOW : SW_HIDE);\r
5464     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5465     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5466        gameInfo.variant != VariantShogi ?\r
5467                SW_SHOW : SW_HIDE);\r
5468     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5469        gameInfo.variant != VariantShogi ?\r
5470                SW_SHOW : SW_HIDE);\r
5471     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5472        gameInfo.variant == VariantShogi ?\r
5473                SW_SHOW : SW_HIDE);\r
5474     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5475        gameInfo.variant == VariantShogi ?\r
5476                SW_SHOW : SW_HIDE);\r
5477     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5478        gameInfo.variant == VariantSuper ?\r
5479                SW_SHOW : SW_HIDE);\r
5480     return TRUE;\r
5481 \r
5482   case WM_COMMAND: /* message: received a command */\r
5483     switch (LOWORD(wParam)) {\r
5484     case IDCANCEL:\r
5485       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5486       ClearHighlights();\r
5487       DrawPosition(FALSE, NULL);\r
5488       return TRUE;\r
5489     case PB_King:\r
5490       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5491       break;\r
5492     case PB_Queen:\r
5493       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5494       break;\r
5495     case PB_Rook:\r
5496       promoChar = PieceToChar(BlackRook);\r
5497       break;\r
5498     case PB_Bishop:\r
5499       promoChar = PieceToChar(BlackBishop);\r
5500       break;\r
5501     case PB_Chancellor:\r
5502       promoChar = PieceToChar(BlackMarshall);\r
5503       break;\r
5504     case PB_Archbishop:\r
5505       promoChar = PieceToChar(BlackAngel);\r
5506       break;\r
5507     case PB_Knight:\r
5508       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5509       break;\r
5510     default:\r
5511       return FALSE;\r
5512     }\r
5513     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5514     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5515        only show the popup when we are already sure the move is valid or\r
5516        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5517        will figure out it is a promotion from the promoChar. */\r
5518     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5519     if (!appData.highlightLastMove) {\r
5520       ClearHighlights();\r
5521       DrawPosition(FALSE, NULL);\r
5522     }\r
5523     return TRUE;\r
5524   }\r
5525   return FALSE;\r
5526 }\r
5527 \r
5528 /* Pop up promotion dialog */\r
5529 VOID\r
5530 PromotionPopup(HWND hwnd)\r
5531 {\r
5532   FARPROC lpProc;\r
5533 \r
5534   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5535   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5536     hwnd, (DLGPROC)lpProc);\r
5537   FreeProcInstance(lpProc);\r
5538 }\r
5539 \r
5540 /* Toggle ShowThinking */\r
5541 VOID\r
5542 ToggleShowThinking()\r
5543 {\r
5544   appData.showThinking = !appData.showThinking;\r
5545   ShowThinkingEvent();\r
5546 }\r
5547 \r
5548 VOID\r
5549 LoadGameDialog(HWND hwnd, char* title)\r
5550 {\r
5551   UINT number = 0;\r
5552   FILE *f;\r
5553   char fileTitle[MSG_SIZ];\r
5554   f = OpenFileDialog(hwnd, "rb", "",\r
5555                      appData.oldSaveStyle ? "gam" : "pgn",\r
5556                      GAME_FILT,\r
5557                      title, &number, fileTitle, NULL);\r
5558   if (f != NULL) {\r
5559     cmailMsgLoaded = FALSE;\r
5560     if (number == 0) {\r
5561       int error = GameListBuild(f);\r
5562       if (error) {\r
5563         DisplayError("Cannot build game list", error);\r
5564       } else if (!ListEmpty(&gameList) &&\r
5565                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5566         GameListPopUp(f, fileTitle);\r
5567         return;\r
5568       }\r
5569       GameListDestroy();\r
5570       number = 1;\r
5571     }\r
5572     LoadGame(f, number, fileTitle, FALSE);\r
5573   }\r
5574 }\r
5575 \r
5576 VOID\r
5577 ChangedConsoleFont()\r
5578 {\r
5579   CHARFORMAT cfmt;\r
5580   CHARRANGE tmpsel, sel;\r
5581   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5582   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5583   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5584   PARAFORMAT paraf;\r
5585 \r
5586   cfmt.cbSize = sizeof(CHARFORMAT);\r
5587   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5588   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5589   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5590    * size.  This was undocumented in the version of MSVC++ that I had\r
5591    * when I wrote the code, but is apparently documented now.\r
5592    */\r
5593   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5594   cfmt.bCharSet = f->lf.lfCharSet;\r
5595   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5596   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5597   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5598   /* Why are the following seemingly needed too? */\r
5599   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5600   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5601   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5602   tmpsel.cpMin = 0;\r
5603   tmpsel.cpMax = -1; /*999999?*/\r
5604   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5605   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5606   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5607    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5608    */\r
5609   paraf.cbSize = sizeof(paraf);\r
5610   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5611   paraf.dxStartIndent = 0;\r
5612   paraf.dxOffset = WRAP_INDENT;\r
5613   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5614   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5615 }\r
5616 \r
5617 /*---------------------------------------------------------------------------*\\r
5618  *\r
5619  * Window Proc for main window\r
5620  *\r
5621 \*---------------------------------------------------------------------------*/\r
5622 \r
5623 /* Process messages for main window, etc. */\r
5624 LRESULT CALLBACK\r
5625 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5626 {\r
5627   FARPROC lpProc;\r
5628   int wmId, wmEvent;\r
5629   char *defName;\r
5630   FILE *f;\r
5631   UINT number;\r
5632   char fileTitle[MSG_SIZ];\r
5633   char buf[MSG_SIZ];\r
5634   static SnapData sd;\r
5635 \r
5636   switch (message) {\r
5637 \r
5638   case WM_PAINT: /* message: repaint portion of window */\r
5639     PaintProc(hwnd);\r
5640     break;\r
5641 \r
5642   case WM_ERASEBKGND:\r
5643     if (IsIconic(hwnd)) {\r
5644       /* Cheat; change the message */\r
5645       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5646     } else {\r
5647       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5648     }\r
5649     break;\r
5650 \r
5651   case WM_LBUTTONDOWN:\r
5652   case WM_MBUTTONDOWN:\r
5653   case WM_RBUTTONDOWN:\r
5654   case WM_LBUTTONUP:\r
5655   case WM_MBUTTONUP:\r
5656   case WM_RBUTTONUP:\r
5657   case WM_MOUSEMOVE:\r
5658   case WM_MOUSEWHEEL:\r
5659     MouseEvent(hwnd, message, wParam, lParam);\r
5660     break;\r
5661 \r
5662   JAWS_KB_NAVIGATION\r
5663 \r
5664   case WM_CHAR:\r
5665     \r
5666     JAWS_ALT_INTERCEPT\r
5667 \r
5668     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5669         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5670         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5671         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5672         SetFocus(h);\r
5673         SendMessage(h, message, wParam, lParam);\r
5674     } else if(lParam != KF_REPEAT) {\r
5675         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5676                 PopUpMoveDialog((char)wParam);\r
5677         } else if((char)wParam == 003) CopyGameToClipboard();\r
5678          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5679     }\r
5680 \r
5681     break;\r
5682 \r
5683   case WM_PALETTECHANGED:\r
5684     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5685       int nnew;\r
5686       HDC hdc = GetDC(hwndMain);\r
5687       SelectPalette(hdc, hPal, TRUE);\r
5688       nnew = RealizePalette(hdc);\r
5689       if (nnew > 0) {\r
5690         paletteChanged = TRUE;\r
5691 #if 0\r
5692         UpdateColors(hdc);\r
5693 #else\r
5694         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5695 #endif\r
5696       }\r
5697       ReleaseDC(hwnd, hdc);\r
5698     }\r
5699     break;\r
5700 \r
5701   case WM_QUERYNEWPALETTE:\r
5702     if (!appData.monoMode /*&& paletteChanged*/) {\r
5703       int nnew;\r
5704       HDC hdc = GetDC(hwndMain);\r
5705       paletteChanged = FALSE;\r
5706       SelectPalette(hdc, hPal, FALSE);\r
5707       nnew = RealizePalette(hdc);\r
5708       if (nnew > 0) {\r
5709         InvalidateRect(hwnd, &boardRect, FALSE);\r
5710       }\r
5711       ReleaseDC(hwnd, hdc);\r
5712       return TRUE;\r
5713     }\r
5714     return FALSE;\r
5715 \r
5716   case WM_COMMAND: /* message: command from application menu */\r
5717     wmId    = LOWORD(wParam);\r
5718     wmEvent = HIWORD(wParam);\r
5719 \r
5720     switch (wmId) {\r
5721     case IDM_NewGame:\r
5722       ResetGameEvent();\r
5723       AnalysisPopDown();\r
5724       SAY("new game enter a move to play against the computer with white");\r
5725       break;\r
5726 \r
5727     case IDM_NewGameFRC:\r
5728       if( NewGameFRC() == 0 ) {\r
5729         ResetGameEvent();\r
5730         AnalysisPopDown();\r
5731       }\r
5732       break;\r
5733 \r
5734     case IDM_NewVariant:\r
5735       NewVariantPopup(hwnd);\r
5736       break;\r
5737 \r
5738     case IDM_LoadGame:\r
5739       LoadGameDialog(hwnd, "Load Game from File");\r
5740       break;\r
5741 \r
5742     case IDM_LoadNextGame:\r
5743       ReloadGame(1);\r
5744       break;\r
5745 \r
5746     case IDM_LoadPrevGame:\r
5747       ReloadGame(-1);\r
5748       break;\r
5749 \r
5750     case IDM_ReloadGame:\r
5751       ReloadGame(0);\r
5752       break;\r
5753 \r
5754     case IDM_LoadPosition:\r
5755       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5756         Reset(FALSE, TRUE);\r
5757       }\r
5758       number = 1;\r
5759       f = OpenFileDialog(hwnd, "rb", "",\r
5760                          appData.oldSaveStyle ? "pos" : "fen",\r
5761                          POSITION_FILT,\r
5762                          "Load Position from File", &number, fileTitle, NULL);\r
5763       if (f != NULL) {\r
5764         LoadPosition(f, number, fileTitle);\r
5765       }\r
5766       break;\r
5767 \r
5768     case IDM_LoadNextPosition:\r
5769       ReloadPosition(1);\r
5770       break;\r
5771 \r
5772     case IDM_LoadPrevPosition:\r
5773       ReloadPosition(-1);\r
5774       break;\r
5775 \r
5776     case IDM_ReloadPosition:\r
5777       ReloadPosition(0);\r
5778       break;\r
5779 \r
5780     case IDM_SaveGame:\r
5781       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5782       f = OpenFileDialog(hwnd, "a", defName,\r
5783                          appData.oldSaveStyle ? "gam" : "pgn",\r
5784                          GAME_FILT,\r
5785                          "Save Game to File", NULL, fileTitle, NULL);\r
5786       if (f != NULL) {\r
5787         SaveGame(f, 0, "");\r
5788       }\r
5789       break;\r
5790 \r
5791     case IDM_SavePosition:\r
5792       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5793       f = OpenFileDialog(hwnd, "a", defName,\r
5794                          appData.oldSaveStyle ? "pos" : "fen",\r
5795                          POSITION_FILT,\r
5796                          "Save Position to File", NULL, fileTitle, NULL);\r
5797       if (f != NULL) {\r
5798         SavePosition(f, 0, "");\r
5799       }\r
5800       break;\r
5801 \r
5802     case IDM_SaveDiagram:\r
5803       defName = "diagram";\r
5804       f = OpenFileDialog(hwnd, "wb", defName,\r
5805                          "bmp",\r
5806                          DIAGRAM_FILT,\r
5807                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5808       if (f != NULL) {\r
5809         SaveDiagram(f);\r
5810       }\r
5811       break;\r
5812 \r
5813     case IDM_CopyGame:\r
5814       CopyGameToClipboard();\r
5815       break;\r
5816 \r
5817     case IDM_PasteGame:\r
5818       PasteGameFromClipboard();\r
5819       break;\r
5820 \r
5821     case IDM_CopyGameListToClipboard:\r
5822       CopyGameListToClipboard();\r
5823       break;\r
5824 \r
5825     /* [AS] Autodetect FEN or PGN data */\r
5826     case IDM_PasteAny:\r
5827       PasteGameOrFENFromClipboard();\r
5828       break;\r
5829 \r
5830     /* [AS] Move history */\r
5831     case IDM_ShowMoveHistory:\r
5832         if( MoveHistoryIsUp() ) {\r
5833             MoveHistoryPopDown();\r
5834         }\r
5835         else {\r
5836             MoveHistoryPopUp();\r
5837         }\r
5838         break;\r
5839 \r
5840     /* [AS] Eval graph */\r
5841     case IDM_ShowEvalGraph:\r
5842         if( EvalGraphIsUp() ) {\r
5843             EvalGraphPopDown();\r
5844         }\r
5845         else {\r
5846             EvalGraphPopUp();\r
5847             SetFocus(hwndMain);\r
5848         }\r
5849         break;\r
5850 \r
5851     /* [AS] Engine output */\r
5852     case IDM_ShowEngineOutput:\r
5853         if( EngineOutputIsUp() ) {\r
5854             EngineOutputPopDown();\r
5855         }\r
5856         else {\r
5857             EngineOutputPopUp();\r
5858         }\r
5859         break;\r
5860 \r
5861     /* [AS] User adjudication */\r
5862     case IDM_UserAdjudication_White:\r
5863         UserAdjudicationEvent( +1 );\r
5864         break;\r
5865 \r
5866     case IDM_UserAdjudication_Black:\r
5867         UserAdjudicationEvent( -1 );\r
5868         break;\r
5869 \r
5870     case IDM_UserAdjudication_Draw:\r
5871         UserAdjudicationEvent( 0 );\r
5872         break;\r
5873 \r
5874     /* [AS] Game list options dialog */\r
5875     case IDM_GameListOptions:\r
5876       GameListOptions();\r
5877       break;\r
5878 \r
5879     case IDM_NewChat:\r
5880       ChatPopUp();\r
5881       break;\r
5882 \r
5883     case IDM_CopyPosition:\r
5884       CopyFENToClipboard();\r
5885       break;\r
5886 \r
5887     case IDM_PastePosition:\r
5888       PasteFENFromClipboard();\r
5889       break;\r
5890 \r
5891     case IDM_MailMove:\r
5892       MailMoveEvent();\r
5893       break;\r
5894 \r
5895     case IDM_ReloadCMailMsg:\r
5896       Reset(TRUE, TRUE);\r
5897       ReloadCmailMsgEvent(FALSE);\r
5898       break;\r
5899 \r
5900     case IDM_Minimize:\r
5901       ShowWindow(hwnd, SW_MINIMIZE);\r
5902       break;\r
5903 \r
5904     case IDM_Exit:\r
5905       ExitEvent(0);\r
5906       break;\r
5907 \r
5908     case IDM_MachineWhite:\r
5909       MachineWhiteEvent();\r
5910       /*\r
5911        * refresh the tags dialog only if it's visible\r
5912        */\r
5913       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5914           char *tags;\r
5915           tags = PGNTags(&gameInfo);\r
5916           TagsPopUp(tags, CmailMsg());\r
5917           free(tags);\r
5918       }\r
5919       SAY("computer starts playing white");\r
5920       break;\r
5921 \r
5922     case IDM_MachineBlack:\r
5923       MachineBlackEvent();\r
5924       /*\r
5925        * refresh the tags dialog only if it's visible\r
5926        */\r
5927       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5928           char *tags;\r
5929           tags = PGNTags(&gameInfo);\r
5930           TagsPopUp(tags, CmailMsg());\r
5931           free(tags);\r
5932       }\r
5933       SAY("computer starts playing black");\r
5934       break;\r
5935 \r
5936     case IDM_TwoMachines:\r
5937       TwoMachinesEvent();\r
5938       /*\r
5939        * refresh the tags dialog only if it's visible\r
5940        */\r
5941       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5942           char *tags;\r
5943           tags = PGNTags(&gameInfo);\r
5944           TagsPopUp(tags, CmailMsg());\r
5945           free(tags);\r
5946       }\r
5947       SAY("programs start playing each other");\r
5948       break;\r
5949 \r
5950     case IDM_AnalysisMode:\r
5951       if (!first.analysisSupport) {\r
5952         sprintf(buf, "%s does not support analysis", first.tidy);\r
5953         DisplayError(buf, 0);\r
5954       } else {\r
5955         SAY("analyzing current position");\r
5956         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5957         if (appData.icsActive) {\r
5958                if (gameMode != IcsObserving) {\r
5959                        sprintf(buf, "You are not observing a game");\r
5960                        DisplayError(buf, 0);\r
5961                        /* secure check */\r
5962                        if (appData.icsEngineAnalyze) {\r
5963                                if (appData.debugMode) \r
5964                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5965                                ExitAnalyzeMode();\r
5966                                ModeHighlight();\r
5967                                break;\r
5968                        }\r
5969                        break;\r
5970                } else {\r
5971                        /* if enable, user want disable icsEngineAnalyze */\r
5972                        if (appData.icsEngineAnalyze) {\r
5973                                ExitAnalyzeMode();\r
5974                                ModeHighlight();\r
5975                                break;\r
5976                        }\r
5977                        appData.icsEngineAnalyze = TRUE;\r
5978                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5979                }\r
5980         } \r
5981         if (!appData.showThinking) ToggleShowThinking();\r
5982         AnalyzeModeEvent();\r
5983       }\r
5984       break;\r
5985 \r
5986     case IDM_AnalyzeFile:\r
5987       if (!first.analysisSupport) {\r
5988         char buf[MSG_SIZ];\r
5989         sprintf(buf, "%s does not support analysis", first.tidy);\r
5990         DisplayError(buf, 0);\r
5991       } else {\r
5992         if (!appData.showThinking) ToggleShowThinking();\r
5993         AnalyzeFileEvent();\r
5994         LoadGameDialog(hwnd, "Analyze Game from File");\r
5995         AnalysisPeriodicEvent(1);\r
5996       }\r
5997       break;\r
5998 \r
5999     case IDM_IcsClient:\r
6000       IcsClientEvent();\r
6001       break;\r
6002 \r
6003     case IDM_EditGame:\r
6004       EditGameEvent();\r
6005       SAY("edit game");\r
6006       break;\r
6007 \r
6008     case IDM_EditPosition:\r
6009       EditPositionEvent();\r
6010       SAY("to set up a position type a FEN");\r
6011       break;\r
6012 \r
6013     case IDM_Training:\r
6014       TrainingEvent();\r
6015       break;\r
6016 \r
6017     case IDM_ShowGameList:\r
6018       ShowGameListProc();\r
6019       break;\r
6020 \r
6021     case IDM_EditTags:\r
6022       EditTagsProc();\r
6023       break;\r
6024 \r
6025     case IDM_EditComment:\r
6026       if (commentDialogUp && editComment) {\r
6027         CommentPopDown();\r
6028       } else {\r
6029         EditCommentEvent();\r
6030       }\r
6031       break;\r
6032 \r
6033     case IDM_Pause:\r
6034       PauseEvent();\r
6035       break;\r
6036 \r
6037     case IDM_Accept:\r
6038       AcceptEvent();\r
6039       break;\r
6040 \r
6041     case IDM_Decline:\r
6042       DeclineEvent();\r
6043       break;\r
6044 \r
6045     case IDM_Rematch:\r
6046       RematchEvent();\r
6047       break;\r
6048 \r
6049     case IDM_CallFlag:\r
6050       CallFlagEvent();\r
6051       break;\r
6052 \r
6053     case IDM_Draw:\r
6054       DrawEvent();\r
6055       break;\r
6056 \r
6057     case IDM_Adjourn:\r
6058       AdjournEvent();\r
6059       break;\r
6060 \r
6061     case IDM_Abort:\r
6062       AbortEvent();\r
6063       break;\r
6064 \r
6065     case IDM_Resign:\r
6066       ResignEvent();\r
6067       break;\r
6068 \r
6069     case IDM_StopObserving:\r
6070       StopObservingEvent();\r
6071       break;\r
6072 \r
6073     case IDM_StopExamining:\r
6074       StopExaminingEvent();\r
6075       break;\r
6076 \r
6077     case IDM_TypeInMove:\r
6078       PopUpMoveDialog('\000');\r
6079       break;\r
6080 \r
6081     case IDM_TypeInName:\r
6082       PopUpNameDialog('\000');\r
6083       break;\r
6084 \r
6085     case IDM_Backward:\r
6086       BackwardEvent();\r
6087       SetFocus(hwndMain);\r
6088       break;\r
6089 \r
6090     JAWS_MENU_ITEMS\r
6091 \r
6092     case IDM_Forward:\r
6093       ForwardEvent();\r
6094       SetFocus(hwndMain);\r
6095       break;\r
6096 \r
6097     case IDM_ToStart:\r
6098       ToStartEvent();\r
6099       SetFocus(hwndMain);\r
6100       break;\r
6101 \r
6102     case IDM_ToEnd:\r
6103       ToEndEvent();\r
6104       SetFocus(hwndMain);\r
6105       break;\r
6106 \r
6107     case IDM_Revert:\r
6108       RevertEvent();\r
6109       break;\r
6110 \r
6111     case IDM_TruncateGame:\r
6112       TruncateGameEvent();\r
6113       break;\r
6114 \r
6115     case IDM_MoveNow:\r
6116       MoveNowEvent();\r
6117       break;\r
6118 \r
6119     case IDM_RetractMove:\r
6120       RetractMoveEvent();\r
6121       break;\r
6122 \r
6123     case IDM_FlipView:\r
6124       flipView = !flipView;\r
6125       DrawPosition(FALSE, NULL);\r
6126       break;\r
6127 \r
6128     case IDM_FlipClock:\r
6129       flipClock = !flipClock;\r
6130       DisplayBothClocks();\r
6131       DrawPosition(FALSE, NULL);\r
6132       break;\r
6133 \r
6134     case IDM_GeneralOptions:\r
6135       GeneralOptionsPopup(hwnd);\r
6136       DrawPosition(TRUE, NULL);\r
6137       break;\r
6138 \r
6139     case IDM_BoardOptions:\r
6140       BoardOptionsPopup(hwnd);\r
6141       break;\r
6142 \r
6143     case IDM_EnginePlayOptions:\r
6144       EnginePlayOptionsPopup(hwnd);\r
6145       break;\r
6146 \r
6147     case IDM_Engine1Options:\r
6148       EngineOptionsPopup(hwnd, &first);\r
6149       break;\r
6150 \r
6151     case IDM_Engine2Options:\r
6152       EngineOptionsPopup(hwnd, &second);\r
6153       break;\r
6154 \r
6155     case IDM_OptionsUCI:\r
6156       UciOptionsPopup(hwnd);\r
6157       break;\r
6158 \r
6159     case IDM_IcsOptions:\r
6160       IcsOptionsPopup(hwnd);\r
6161       break;\r
6162 \r
6163     case IDM_Fonts:\r
6164       FontsOptionsPopup(hwnd);\r
6165       break;\r
6166 \r
6167     case IDM_Sounds:\r
6168       SoundOptionsPopup(hwnd);\r
6169       break;\r
6170 \r
6171     case IDM_CommPort:\r
6172       CommPortOptionsPopup(hwnd);\r
6173       break;\r
6174 \r
6175     case IDM_LoadOptions:\r
6176       LoadOptionsPopup(hwnd);\r
6177       break;\r
6178 \r
6179     case IDM_SaveOptions:\r
6180       SaveOptionsPopup(hwnd);\r
6181       break;\r
6182 \r
6183     case IDM_TimeControl:\r
6184       TimeControlOptionsPopup(hwnd);\r
6185       break;\r
6186 \r
6187     case IDM_SaveSettings:\r
6188       SaveSettings(settingsFileName);\r
6189       break;\r
6190 \r
6191     case IDM_SaveSettingsOnExit:\r
6192       saveSettingsOnExit = !saveSettingsOnExit;\r
6193       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6194                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6195                                          MF_CHECKED : MF_UNCHECKED));\r
6196       break;\r
6197 \r
6198     case IDM_Hint:\r
6199       HintEvent();\r
6200       break;\r
6201 \r
6202     case IDM_Book:\r
6203       BookEvent();\r
6204       break;\r
6205 \r
6206     case IDM_AboutGame:\r
6207       AboutGameEvent();\r
6208       break;\r
6209 \r
6210     case IDM_Debug:\r
6211       appData.debugMode = !appData.debugMode;\r
6212       if (appData.debugMode) {\r
6213         char dir[MSG_SIZ];\r
6214         GetCurrentDirectory(MSG_SIZ, dir);\r
6215         SetCurrentDirectory(installDir);\r
6216         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6217         SetCurrentDirectory(dir);\r
6218         setbuf(debugFP, NULL);\r
6219       } else {\r
6220         fclose(debugFP);\r
6221         debugFP = NULL;\r
6222       }\r
6223       break;\r
6224 \r
6225     case IDM_HELPCONTENTS:\r
6226       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6227           !HtmlHelp(hwnd, "winboard.chm", 0, 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_HELPSEARCH:\r
6235         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6236             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6237         MessageBox (GetFocus(),\r
6238                     "Unable to activate help",\r
6239                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6240       }\r
6241       break;\r
6242 \r
6243     case IDM_HELPHELP:\r
6244       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6245         MessageBox (GetFocus(),\r
6246                     "Unable to activate help",\r
6247                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6248       }\r
6249       break;\r
6250 \r
6251     case IDM_ABOUT:\r
6252       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6253       DialogBox(hInst, \r
6254         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6255         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6256       FreeProcInstance(lpProc);\r
6257       break;\r
6258 \r
6259     case IDM_DirectCommand1:\r
6260       AskQuestionEvent("Direct Command",\r
6261                        "Send to chess program:", "", "1");\r
6262       break;\r
6263     case IDM_DirectCommand2:\r
6264       AskQuestionEvent("Direct Command",\r
6265                        "Send to second chess program:", "", "2");\r
6266       break;\r
6267 \r
6268     case EP_WhitePawn:\r
6269       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6270       fromX = fromY = -1;\r
6271       break;\r
6272 \r
6273     case EP_WhiteKnight:\r
6274       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6275       fromX = fromY = -1;\r
6276       break;\r
6277 \r
6278     case EP_WhiteBishop:\r
6279       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6280       fromX = fromY = -1;\r
6281       break;\r
6282 \r
6283     case EP_WhiteRook:\r
6284       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6285       fromX = fromY = -1;\r
6286       break;\r
6287 \r
6288     case EP_WhiteQueen:\r
6289       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6290       fromX = fromY = -1;\r
6291       break;\r
6292 \r
6293     case EP_WhiteFerz:\r
6294       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6295       fromX = fromY = -1;\r
6296       break;\r
6297 \r
6298     case EP_WhiteWazir:\r
6299       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6300       fromX = fromY = -1;\r
6301       break;\r
6302 \r
6303     case EP_WhiteAlfil:\r
6304       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6305       fromX = fromY = -1;\r
6306       break;\r
6307 \r
6308     case EP_WhiteCannon:\r
6309       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6310       fromX = fromY = -1;\r
6311       break;\r
6312 \r
6313     case EP_WhiteCardinal:\r
6314       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6315       fromX = fromY = -1;\r
6316       break;\r
6317 \r
6318     case EP_WhiteMarshall:\r
6319       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6320       fromX = fromY = -1;\r
6321       break;\r
6322 \r
6323     case EP_WhiteKing:\r
6324       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6325       fromX = fromY = -1;\r
6326       break;\r
6327 \r
6328     case EP_BlackPawn:\r
6329       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6330       fromX = fromY = -1;\r
6331       break;\r
6332 \r
6333     case EP_BlackKnight:\r
6334       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6335       fromX = fromY = -1;\r
6336       break;\r
6337 \r
6338     case EP_BlackBishop:\r
6339       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6340       fromX = fromY = -1;\r
6341       break;\r
6342 \r
6343     case EP_BlackRook:\r
6344       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6345       fromX = fromY = -1;\r
6346       break;\r
6347 \r
6348     case EP_BlackQueen:\r
6349       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6350       fromX = fromY = -1;\r
6351       break;\r
6352 \r
6353     case EP_BlackFerz:\r
6354       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6355       fromX = fromY = -1;\r
6356       break;\r
6357 \r
6358     case EP_BlackWazir:\r
6359       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6360       fromX = fromY = -1;\r
6361       break;\r
6362 \r
6363     case EP_BlackAlfil:\r
6364       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6365       fromX = fromY = -1;\r
6366       break;\r
6367 \r
6368     case EP_BlackCannon:\r
6369       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6370       fromX = fromY = -1;\r
6371       break;\r
6372 \r
6373     case EP_BlackCardinal:\r
6374       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6375       fromX = fromY = -1;\r
6376       break;\r
6377 \r
6378     case EP_BlackMarshall:\r
6379       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6380       fromX = fromY = -1;\r
6381       break;\r
6382 \r
6383     case EP_BlackKing:\r
6384       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6385       fromX = fromY = -1;\r
6386       break;\r
6387 \r
6388     case EP_EmptySquare:\r
6389       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6390       fromX = fromY = -1;\r
6391       break;\r
6392 \r
6393     case EP_ClearBoard:\r
6394       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6395       fromX = fromY = -1;\r
6396       break;\r
6397 \r
6398     case EP_White:\r
6399       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6400       fromX = fromY = -1;\r
6401       break;\r
6402 \r
6403     case EP_Black:\r
6404       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6405       fromX = fromY = -1;\r
6406       break;\r
6407 \r
6408     case EP_Promote:\r
6409       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6410       fromX = fromY = -1;\r
6411       break;\r
6412 \r
6413     case EP_Demote:\r
6414       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6415       fromX = fromY = -1;\r
6416       break;\r
6417 \r
6418     case DP_Pawn:\r
6419       DropMenuEvent(WhitePawn, fromX, fromY);\r
6420       fromX = fromY = -1;\r
6421       break;\r
6422 \r
6423     case DP_Knight:\r
6424       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6425       fromX = fromY = -1;\r
6426       break;\r
6427 \r
6428     case DP_Bishop:\r
6429       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6430       fromX = fromY = -1;\r
6431       break;\r
6432 \r
6433     case DP_Rook:\r
6434       DropMenuEvent(WhiteRook, fromX, fromY);\r
6435       fromX = fromY = -1;\r
6436       break;\r
6437 \r
6438     case DP_Queen:\r
6439       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6440       fromX = fromY = -1;\r
6441       break;\r
6442 \r
6443     default:\r
6444       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6445     }\r
6446     break;\r
6447 \r
6448   case WM_TIMER:\r
6449     switch (wParam) {\r
6450     case CLOCK_TIMER_ID:\r
6451       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6452       clockTimerEvent = 0;\r
6453       DecrementClocks(); /* call into back end */\r
6454       break;\r
6455     case LOAD_GAME_TIMER_ID:\r
6456       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6457       loadGameTimerEvent = 0;\r
6458       AutoPlayGameLoop(); /* call into back end */\r
6459       break;\r
6460     case ANALYSIS_TIMER_ID:\r
6461       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6462                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6463         AnalysisPeriodicEvent(0);\r
6464       } else {\r
6465         KillTimer(hwnd, analysisTimerEvent);\r
6466         analysisTimerEvent = 0;\r
6467       }\r
6468       break;\r
6469     case DELAYED_TIMER_ID:\r
6470       KillTimer(hwnd, delayedTimerEvent);\r
6471       delayedTimerEvent = 0;\r
6472       delayedTimerCallback();\r
6473       break;\r
6474     }\r
6475     break;\r
6476 \r
6477   case WM_USER_Input:\r
6478     InputEvent(hwnd, message, wParam, lParam);\r
6479     break;\r
6480 \r
6481   /* [AS] Also move "attached" child windows */\r
6482   case WM_WINDOWPOSCHANGING:\r
6483 \r
6484     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6485         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6486 \r
6487         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6488             /* Window is moving */\r
6489             RECT rcMain;\r
6490 \r
6491 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6492             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6493             rcMain.right  = boardX + winWidth;\r
6494             rcMain.top    = boardY;\r
6495             rcMain.bottom = boardY + winHeight;\r
6496             \r
6497             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6498             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6499             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6500             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6501             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6502             boardX = lpwp->x;\r
6503             boardY = lpwp->y;\r
6504         }\r
6505     }\r
6506     break;\r
6507 \r
6508   /* [AS] Snapping */\r
6509   case WM_ENTERSIZEMOVE:\r
6510     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6511     if (hwnd == hwndMain) {\r
6512       doingSizing = TRUE;\r
6513       lastSizing = 0;\r
6514     }\r
6515     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6516     break;\r
6517 \r
6518   case WM_SIZING:\r
6519     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6520     if (hwnd == hwndMain) {\r
6521       lastSizing = wParam;\r
6522     }\r
6523     break;\r
6524 \r
6525   case WM_MOVING:\r
6526     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6527       return OnMoving( &sd, hwnd, wParam, lParam );\r
6528 \r
6529   case WM_EXITSIZEMOVE:\r
6530     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6531     if (hwnd == hwndMain) {\r
6532       RECT client;\r
6533       doingSizing = FALSE;\r
6534       InvalidateRect(hwnd, &boardRect, FALSE);\r
6535       GetClientRect(hwnd, &client);\r
6536       ResizeBoard(client.right, client.bottom, lastSizing);\r
6537       lastSizing = 0;\r
6538       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6539     }\r
6540     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6541     break;\r
6542 \r
6543   case WM_DESTROY: /* message: window being destroyed */\r
6544     PostQuitMessage(0);\r
6545     break;\r
6546 \r
6547   case WM_CLOSE:\r
6548     if (hwnd == hwndMain) {\r
6549       ExitEvent(0);\r
6550     }\r
6551     break;\r
6552 \r
6553   default:      /* Passes it on if unprocessed */\r
6554     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6555   }\r
6556   return 0;\r
6557 }\r
6558 \r
6559 /*---------------------------------------------------------------------------*\\r
6560  *\r
6561  * Misc utility routines\r
6562  *\r
6563 \*---------------------------------------------------------------------------*/\r
6564 \r
6565 /*\r
6566  * Decent random number generator, at least not as bad as Windows\r
6567  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6568  */\r
6569 unsigned int randstate;\r
6570 \r
6571 int\r
6572 myrandom(void)\r
6573 {\r
6574   randstate = randstate * 1664525 + 1013904223;\r
6575   return (int) randstate & 0x7fffffff;\r
6576 }\r
6577 \r
6578 void\r
6579 mysrandom(unsigned int seed)\r
6580 {\r
6581   randstate = seed;\r
6582 }\r
6583 \r
6584 \r
6585 /* \r
6586  * returns TRUE if user selects a different color, FALSE otherwise \r
6587  */\r
6588 \r
6589 BOOL\r
6590 ChangeColor(HWND hwnd, COLORREF *which)\r
6591 {\r
6592   static BOOL firstTime = TRUE;\r
6593   static DWORD customColors[16];\r
6594   CHOOSECOLOR cc;\r
6595   COLORREF newcolor;\r
6596   int i;\r
6597   ColorClass ccl;\r
6598 \r
6599   if (firstTime) {\r
6600     /* Make initial colors in use available as custom colors */\r
6601     /* Should we put the compiled-in defaults here instead? */\r
6602     i = 0;\r
6603     customColors[i++] = lightSquareColor & 0xffffff;\r
6604     customColors[i++] = darkSquareColor & 0xffffff;\r
6605     customColors[i++] = whitePieceColor & 0xffffff;\r
6606     customColors[i++] = blackPieceColor & 0xffffff;\r
6607     customColors[i++] = highlightSquareColor & 0xffffff;\r
6608     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6609 \r
6610     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6611       customColors[i++] = textAttribs[ccl].color;\r
6612     }\r
6613     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6614     firstTime = FALSE;\r
6615   }\r
6616 \r
6617   cc.lStructSize = sizeof(cc);\r
6618   cc.hwndOwner = hwnd;\r
6619   cc.hInstance = NULL;\r
6620   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6621   cc.lpCustColors = (LPDWORD) customColors;\r
6622   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6623 \r
6624   if (!ChooseColor(&cc)) return FALSE;\r
6625 \r
6626   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6627   if (newcolor == *which) return FALSE;\r
6628   *which = newcolor;\r
6629   return TRUE;\r
6630 \r
6631   /*\r
6632   InitDrawingColors();\r
6633   InvalidateRect(hwnd, &boardRect, FALSE);\r
6634   */\r
6635 }\r
6636 \r
6637 BOOLEAN\r
6638 MyLoadSound(MySound *ms)\r
6639 {\r
6640   BOOL ok = FALSE;\r
6641   struct stat st;\r
6642   FILE *f;\r
6643 \r
6644   if (ms->data) free(ms->data);\r
6645   ms->data = NULL;\r
6646 \r
6647   switch (ms->name[0]) {\r
6648   case NULLCHAR:\r
6649     /* Silence */\r
6650     ok = TRUE;\r
6651     break;\r
6652   case '$':\r
6653     /* System sound from Control Panel.  Don't preload here. */\r
6654     ok = TRUE;\r
6655     break;\r
6656   case '!':\r
6657     if (ms->name[1] == NULLCHAR) {\r
6658       /* "!" alone = silence */\r
6659       ok = TRUE;\r
6660     } else {\r
6661       /* Builtin wave resource.  Error if not found. */\r
6662       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6663       if (h == NULL) break;\r
6664       ms->data = (void *)LoadResource(hInst, h);\r
6665       if (h == NULL) break;\r
6666       ok = TRUE;\r
6667     }\r
6668     break;\r
6669   default:\r
6670     /* .wav file.  Error if not found. */\r
6671     f = fopen(ms->name, "rb");\r
6672     if (f == NULL) break;\r
6673     if (fstat(fileno(f), &st) < 0) break;\r
6674     ms->data = malloc(st.st_size);\r
6675     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6676     fclose(f);\r
6677     ok = TRUE;\r
6678     break;\r
6679   }\r
6680   if (!ok) {\r
6681     char buf[MSG_SIZ];\r
6682     sprintf(buf, "Error loading sound %s", ms->name);\r
6683     DisplayError(buf, GetLastError());\r
6684   }\r
6685   return ok;\r
6686 }\r
6687 \r
6688 BOOLEAN\r
6689 MyPlaySound(MySound *ms)\r
6690 {\r
6691   BOOLEAN ok = FALSE;\r
6692 \r
6693   switch (ms->name[0]) {\r
6694   case NULLCHAR:\r
6695         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6696     /* Silence */\r
6697     ok = TRUE;\r
6698     break;\r
6699   case '$':\r
6700     /* System sound from Control Panel (deprecated feature).\r
6701        "$" alone or an unset sound name gets default beep (still in use). */\r
6702     if (ms->name[1]) {\r
6703       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6704     }\r
6705     if (!ok) ok = MessageBeep(MB_OK);\r
6706     break; \r
6707   case '!':\r
6708     /* Builtin wave resource, or "!" alone for silence */\r
6709     if (ms->name[1]) {\r
6710       if (ms->data == NULL) return FALSE;\r
6711       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6712     } else {\r
6713       ok = TRUE;\r
6714     }\r
6715     break;\r
6716   default:\r
6717     /* .wav file.  Error if not found. */\r
6718     if (ms->data == NULL) return FALSE;\r
6719     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6720     break;\r
6721   }\r
6722   /* Don't print an error: this can happen innocently if the sound driver\r
6723      is busy; for instance, if another instance of WinBoard is playing\r
6724      a sound at about the same time. */\r
6725 #if 0\r
6726   if (!ok) {\r
6727     char buf[MSG_SIZ];\r
6728     sprintf(buf, "Error playing sound %s", ms->name);\r
6729     DisplayError(buf, GetLastError());\r
6730   }\r
6731 #endif\r
6732   return ok;\r
6733 }\r
6734 \r
6735 \r
6736 LRESULT CALLBACK\r
6737 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6738 {\r
6739   BOOL ok;\r
6740   OPENFILENAME *ofn;\r
6741   static UINT *number; /* gross that this is static */\r
6742 \r
6743   switch (message) {\r
6744   case WM_INITDIALOG: /* message: initialize dialog box */\r
6745     /* Center the dialog over the application window */\r
6746     ofn = (OPENFILENAME *) lParam;\r
6747     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6748       number = (UINT *) ofn->lCustData;\r
6749       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6750     } else {\r
6751       number = NULL;\r
6752     }\r
6753     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6754     return FALSE;  /* Allow for further processing */\r
6755 \r
6756   case WM_COMMAND:\r
6757     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6758       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6759     }\r
6760     return FALSE;  /* Allow for further processing */\r
6761   }\r
6762   return FALSE;\r
6763 }\r
6764 \r
6765 UINT APIENTRY\r
6766 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6767 {\r
6768   static UINT *number;\r
6769   OPENFILENAME *ofname;\r
6770   OFNOTIFY *ofnot;\r
6771   switch (uiMsg) {\r
6772   case WM_INITDIALOG:\r
6773     ofname = (OPENFILENAME *)lParam;\r
6774     number = (UINT *)(ofname->lCustData);\r
6775     break;\r
6776   case WM_NOTIFY:\r
6777     ofnot = (OFNOTIFY *)lParam;\r
6778     if (ofnot->hdr.code == CDN_FILEOK) {\r
6779       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6780     }\r
6781     break;\r
6782   }\r
6783   return 0;\r
6784 }\r
6785 \r
6786 \r
6787 FILE *\r
6788 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6789                char *nameFilt, char *dlgTitle, UINT *number,\r
6790                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6791 {\r
6792   OPENFILENAME openFileName;\r
6793   char buf1[MSG_SIZ];\r
6794   FILE *f;\r
6795 \r
6796   if (fileName == NULL) fileName = buf1;\r
6797   if (defName == NULL) {\r
6798     strcpy(fileName, "*.");\r
6799     strcat(fileName, defExt);\r
6800   } else {\r
6801     strcpy(fileName, defName);\r
6802   }\r
6803   if (fileTitle) strcpy(fileTitle, "");\r
6804   if (number) *number = 0;\r
6805 \r
6806   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6807   openFileName.hwndOwner         = hwnd;\r
6808   openFileName.hInstance         = (HANDLE) hInst;\r
6809   openFileName.lpstrFilter       = nameFilt;\r
6810   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6811   openFileName.nMaxCustFilter    = 0L;\r
6812   openFileName.nFilterIndex      = 1L;\r
6813   openFileName.lpstrFile         = fileName;\r
6814   openFileName.nMaxFile          = MSG_SIZ;\r
6815   openFileName.lpstrFileTitle    = fileTitle;\r
6816   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6817   openFileName.lpstrInitialDir   = NULL;\r
6818   openFileName.lpstrTitle        = dlgTitle;\r
6819   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6820     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6821     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6822     | (oldDialog ? 0 : OFN_EXPLORER);\r
6823   openFileName.nFileOffset       = 0;\r
6824   openFileName.nFileExtension    = 0;\r
6825   openFileName.lpstrDefExt       = defExt;\r
6826   openFileName.lCustData         = (LONG) number;\r
6827   openFileName.lpfnHook          = oldDialog ?\r
6828     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6829   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6830 \r
6831   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6832                         GetOpenFileName(&openFileName)) {\r
6833     /* open the file */\r
6834     f = fopen(openFileName.lpstrFile, write);\r
6835     if (f == NULL) {\r
6836       MessageBox(hwnd, "File open failed", NULL,\r
6837                  MB_OK|MB_ICONEXCLAMATION);\r
6838       return NULL;\r
6839     }\r
6840   } else {\r
6841     int err = CommDlgExtendedError();\r
6842     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6843     return FALSE;\r
6844   }\r
6845   return f;\r
6846 }\r
6847 \r
6848 \r
6849 \r
6850 VOID APIENTRY\r
6851 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6852 {\r
6853   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6854 \r
6855   /*\r
6856    * Get the first pop-up menu in the menu template. This is the\r
6857    * menu that TrackPopupMenu displays.\r
6858    */\r
6859   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6860 \r
6861   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6862 \r
6863   /*\r
6864    * TrackPopup uses screen coordinates, so convert the\r
6865    * coordinates of the mouse click to screen coordinates.\r
6866    */\r
6867   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6868 \r
6869   /* Draw and track the floating pop-up menu. */\r
6870   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6871                  pt.x, pt.y, 0, hwnd, NULL);\r
6872 \r
6873   /* Destroy the menu.*/\r
6874   DestroyMenu(hmenu);\r
6875 }\r
6876    \r
6877 typedef struct {\r
6878   HWND hDlg, hText;\r
6879   int sizeX, sizeY, newSizeX, newSizeY;\r
6880   HDWP hdwp;\r
6881 } ResizeEditPlusButtonsClosure;\r
6882 \r
6883 BOOL CALLBACK\r
6884 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6885 {\r
6886   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6887   RECT rect;\r
6888   POINT pt;\r
6889 \r
6890   if (hChild == cl->hText) return TRUE;\r
6891   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6892   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6893   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6894   ScreenToClient(cl->hDlg, &pt);\r
6895   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6896     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6897   return TRUE;\r
6898 }\r
6899 \r
6900 /* Resize a dialog that has a (rich) edit field filling most of\r
6901    the top, with a row of buttons below */\r
6902 VOID\r
6903 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6904 {\r
6905   RECT rectText;\r
6906   int newTextHeight, newTextWidth;\r
6907   ResizeEditPlusButtonsClosure cl;\r
6908   \r
6909   /*if (IsIconic(hDlg)) return;*/\r
6910   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6911   \r
6912   cl.hdwp = BeginDeferWindowPos(8);\r
6913 \r
6914   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6915   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6916   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6917   if (newTextHeight < 0) {\r
6918     newSizeY += -newTextHeight;\r
6919     newTextHeight = 0;\r
6920   }\r
6921   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6922     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6923 \r
6924   cl.hDlg = hDlg;\r
6925   cl.hText = hText;\r
6926   cl.sizeX = sizeX;\r
6927   cl.sizeY = sizeY;\r
6928   cl.newSizeX = newSizeX;\r
6929   cl.newSizeY = newSizeY;\r
6930   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6931 \r
6932   EndDeferWindowPos(cl.hdwp);\r
6933 }\r
6934 \r
6935 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6936 {\r
6937     RECT    rChild, rParent;\r
6938     int     wChild, hChild, wParent, hParent;\r
6939     int     wScreen, hScreen, xNew, yNew;\r
6940     HDC     hdc;\r
6941 \r
6942     /* Get the Height and Width of the child window */\r
6943     GetWindowRect (hwndChild, &rChild);\r
6944     wChild = rChild.right - rChild.left;\r
6945     hChild = rChild.bottom - rChild.top;\r
6946 \r
6947     /* Get the Height and Width of the parent window */\r
6948     GetWindowRect (hwndParent, &rParent);\r
6949     wParent = rParent.right - rParent.left;\r
6950     hParent = rParent.bottom - rParent.top;\r
6951 \r
6952     /* Get the display limits */\r
6953     hdc = GetDC (hwndChild);\r
6954     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6955     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6956     ReleaseDC(hwndChild, hdc);\r
6957 \r
6958     /* Calculate new X position, then adjust for screen */\r
6959     xNew = rParent.left + ((wParent - wChild) /2);\r
6960     if (xNew < 0) {\r
6961         xNew = 0;\r
6962     } else if ((xNew+wChild) > wScreen) {\r
6963         xNew = wScreen - wChild;\r
6964     }\r
6965 \r
6966     /* Calculate new Y position, then adjust for screen */\r
6967     if( mode == 0 ) {\r
6968         yNew = rParent.top  + ((hParent - hChild) /2);\r
6969     }\r
6970     else {\r
6971         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6972     }\r
6973 \r
6974     if (yNew < 0) {\r
6975         yNew = 0;\r
6976     } else if ((yNew+hChild) > hScreen) {\r
6977         yNew = hScreen - hChild;\r
6978     }\r
6979 \r
6980     /* Set it, and return */\r
6981     return SetWindowPos (hwndChild, NULL,\r
6982                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6983 }\r
6984 \r
6985 /* Center one window over another */\r
6986 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6987 {\r
6988     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6989 }\r
6990 \r
6991 /*---------------------------------------------------------------------------*\\r
6992  *\r
6993  * Startup Dialog functions\r
6994  *\r
6995 \*---------------------------------------------------------------------------*/\r
6996 void\r
6997 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6998 {\r
6999   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7000 \r
7001   while (*cd != NULL) {\r
7002     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
7003     cd++;\r
7004   }\r
7005 }\r
7006 \r
7007 void\r
7008 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
7009 {\r
7010   char buf1[ARG_MAX];\r
7011   int len;\r
7012 \r
7013   if (str[0] == '@') {\r
7014     FILE* f = fopen(str + 1, "r");\r
7015     if (f == NULL) {\r
7016       DisplayFatalError(str + 1, errno, 2);\r
7017       return;\r
7018     }\r
7019     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7020     fclose(f);\r
7021     buf1[len] = NULLCHAR;\r
7022     str = buf1;\r
7023   }\r
7024 \r
7025   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7026 \r
7027   for (;;) {\r
7028     char buf[MSG_SIZ];\r
7029     char *end = strchr(str, '\n');\r
7030     if (end == NULL) return;\r
7031     memcpy(buf, str, end - str);\r
7032     buf[end - str] = NULLCHAR;\r
7033     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7034     str = end + 1;\r
7035   }\r
7036 }\r
7037 \r
7038 void\r
7039 SetStartupDialogEnables(HWND hDlg)\r
7040 {\r
7041   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7042     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7043     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7044   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7045     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7046   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7047     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7048   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7049     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7050   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7051     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7052     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7053     IsDlgButtonChecked(hDlg, OPT_View));\r
7054 }\r
7055 \r
7056 char *\r
7057 QuoteForFilename(char *filename)\r
7058 {\r
7059   int dquote, space;\r
7060   dquote = strchr(filename, '"') != NULL;\r
7061   space = strchr(filename, ' ') != NULL;\r
7062   if (dquote || space) {\r
7063     if (dquote) {\r
7064       return "'";\r
7065     } else {\r
7066       return "\"";\r
7067     }\r
7068   } else {\r
7069     return "";\r
7070   }\r
7071 }\r
7072 \r
7073 VOID\r
7074 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7075 {\r
7076   char buf[MSG_SIZ];\r
7077   char *q;\r
7078 \r
7079   InitComboStringsFromOption(hwndCombo, nthnames);\r
7080   q = QuoteForFilename(nthcp);\r
7081   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7082   if (*nthdir != NULLCHAR) {\r
7083     q = QuoteForFilename(nthdir);\r
7084     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7085   }\r
7086   if (*nthcp == NULLCHAR) {\r
7087     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7088   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7089     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7090     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7091   }\r
7092 }\r
7093 \r
7094 LRESULT CALLBACK\r
7095 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7096 {\r
7097   char buf[MSG_SIZ];\r
7098   HANDLE hwndCombo;\r
7099   char *p;\r
7100 \r
7101   switch (message) {\r
7102   case WM_INITDIALOG:\r
7103     /* Center the dialog */\r
7104     CenterWindow (hDlg, GetDesktopWindow());\r
7105     /* Initialize the dialog items */\r
7106     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7107                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7108                   firstChessProgramNames);\r
7109     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7110                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7111                   secondChessProgramNames);\r
7112     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7113     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7114     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7115     if (*appData.icsHelper != NULLCHAR) {\r
7116       char *q = QuoteForFilename(appData.icsHelper);\r
7117       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7118     }\r
7119     if (*appData.icsHost == NULLCHAR) {\r
7120       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7121       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7122     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7123       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7124       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7125     }\r
7126 \r
7127     if (appData.icsActive) {\r
7128       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7129     }\r
7130     else if (appData.noChessProgram) {\r
7131       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7132     }\r
7133     else {\r
7134       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7135     }\r
7136 \r
7137     SetStartupDialogEnables(hDlg);\r
7138     return TRUE;\r
7139 \r
7140   case WM_COMMAND:\r
7141     switch (LOWORD(wParam)) {\r
7142     case IDOK:\r
7143       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7144         strcpy(buf, "/fcp=");\r
7145         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7146         p = buf;\r
7147         ParseArgs(StringGet, &p);\r
7148         strcpy(buf, "/scp=");\r
7149         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7150         p = buf;\r
7151         ParseArgs(StringGet, &p);\r
7152         appData.noChessProgram = FALSE;\r
7153         appData.icsActive = FALSE;\r
7154       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7155         strcpy(buf, "/ics /icshost=");\r
7156         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7157         p = buf;\r
7158         ParseArgs(StringGet, &p);\r
7159         if (appData.zippyPlay) {\r
7160           strcpy(buf, "/fcp=");\r
7161           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7162           p = buf;\r
7163           ParseArgs(StringGet, &p);\r
7164         }\r
7165       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7166         appData.noChessProgram = TRUE;\r
7167         appData.icsActive = FALSE;\r
7168       } else {\r
7169         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7170                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7171         return TRUE;\r
7172       }\r
7173       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7174         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7175         p = buf;\r
7176         ParseArgs(StringGet, &p);\r
7177       }\r
7178       EndDialog(hDlg, TRUE);\r
7179       return TRUE;\r
7180 \r
7181     case IDCANCEL:\r
7182       ExitEvent(0);\r
7183       return TRUE;\r
7184 \r
7185     case IDM_HELPCONTENTS:\r
7186       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7187         MessageBox (GetFocus(),\r
7188                     "Unable to activate help",\r
7189                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7190       }\r
7191       break;\r
7192 \r
7193     default:\r
7194       SetStartupDialogEnables(hDlg);\r
7195       break;\r
7196     }\r
7197     break;\r
7198   }\r
7199   return FALSE;\r
7200 }\r
7201 \r
7202 /*---------------------------------------------------------------------------*\\r
7203  *\r
7204  * About box dialog functions\r
7205  *\r
7206 \*---------------------------------------------------------------------------*/\r
7207 \r
7208 /* Process messages for "About" dialog box */\r
7209 LRESULT CALLBACK\r
7210 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7211 {\r
7212   switch (message) {\r
7213   case WM_INITDIALOG: /* message: initialize dialog box */\r
7214     /* Center the dialog over the application window */\r
7215     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7216     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7217     JAWS_COPYRIGHT\r
7218     return (TRUE);\r
7219 \r
7220   case WM_COMMAND: /* message: received a command */\r
7221     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7222         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7223       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7224       return (TRUE);\r
7225     }\r
7226     break;\r
7227   }\r
7228   return (FALSE);\r
7229 }\r
7230 \r
7231 /*---------------------------------------------------------------------------*\\r
7232  *\r
7233  * Comment Dialog functions\r
7234  *\r
7235 \*---------------------------------------------------------------------------*/\r
7236 \r
7237 LRESULT CALLBACK\r
7238 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7239 {\r
7240   static HANDLE hwndText = NULL;\r
7241   int len, newSizeX, newSizeY, flags;\r
7242   static int sizeX, sizeY;\r
7243   char *str;\r
7244   RECT rect;\r
7245   MINMAXINFO *mmi;\r
7246 \r
7247   switch (message) {\r
7248   case WM_INITDIALOG: /* message: initialize dialog box */\r
7249     /* Initialize the dialog items */\r
7250     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7251     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7252     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7253     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7254     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7255     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7256     SetWindowText(hDlg, commentTitle);\r
7257     if (editComment) {\r
7258       SetFocus(hwndText);\r
7259     } else {\r
7260       SetFocus(GetDlgItem(hDlg, IDOK));\r
7261     }\r
7262     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7263                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7264                 MAKELPARAM(FALSE, 0));\r
7265     /* Size and position the dialog */\r
7266     if (!commentDialog) {\r
7267       commentDialog = hDlg;\r
7268       flags = SWP_NOZORDER;\r
7269       GetClientRect(hDlg, &rect);\r
7270       sizeX = rect.right;\r
7271       sizeY = rect.bottom;\r
7272       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7273           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7274         WINDOWPLACEMENT wp;\r
7275         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7276         wp.length = sizeof(WINDOWPLACEMENT);\r
7277         wp.flags = 0;\r
7278         wp.showCmd = SW_SHOW;\r
7279         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7280         wp.rcNormalPosition.left = commentX;\r
7281         wp.rcNormalPosition.right = commentX + commentW;\r
7282         wp.rcNormalPosition.top = commentY;\r
7283         wp.rcNormalPosition.bottom = commentY + commentH;\r
7284         SetWindowPlacement(hDlg, &wp);\r
7285 \r
7286         GetClientRect(hDlg, &rect);\r
7287         newSizeX = rect.right;\r
7288         newSizeY = rect.bottom;\r
7289         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7290                               newSizeX, newSizeY);\r
7291         sizeX = newSizeX;\r
7292         sizeY = newSizeY;\r
7293       }\r
7294     }\r
7295     return FALSE;\r
7296 \r
7297   case WM_COMMAND: /* message: received a command */\r
7298     switch (LOWORD(wParam)) {\r
7299     case IDOK:\r
7300       if (editComment) {\r
7301         char *p, *q;\r
7302         /* Read changed options from the dialog box */\r
7303         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7304         len = GetWindowTextLength(hwndText);\r
7305         str = (char *) malloc(len + 1);\r
7306         GetWindowText(hwndText, str, len + 1);\r
7307         p = q = str;\r
7308         while (*q) {\r
7309           if (*q == '\r')\r
7310             q++;\r
7311           else\r
7312             *p++ = *q++;\r
7313         }\r
7314         *p = NULLCHAR;\r
7315         ReplaceComment(commentIndex, str);\r
7316         free(str);\r
7317       }\r
7318       CommentPopDown();\r
7319       return TRUE;\r
7320 \r
7321     case IDCANCEL:\r
7322     case OPT_CancelComment:\r
7323       CommentPopDown();\r
7324       return TRUE;\r
7325 \r
7326     case OPT_ClearComment:\r
7327       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7328       break;\r
7329 \r
7330     case OPT_EditComment:\r
7331       EditCommentEvent();\r
7332       return TRUE;\r
7333 \r
7334     default:\r
7335       break;\r
7336     }\r
7337     break;\r
7338 \r
7339   case WM_SIZE:\r
7340     newSizeX = LOWORD(lParam);\r
7341     newSizeY = HIWORD(lParam);\r
7342     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7343     sizeX = newSizeX;\r
7344     sizeY = newSizeY;\r
7345     break;\r
7346 \r
7347   case WM_GETMINMAXINFO:\r
7348     /* Prevent resizing window too small */\r
7349     mmi = (MINMAXINFO *) lParam;\r
7350     mmi->ptMinTrackSize.x = 100;\r
7351     mmi->ptMinTrackSize.y = 100;\r
7352     break;\r
7353   }\r
7354   return FALSE;\r
7355 }\r
7356 \r
7357 VOID\r
7358 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7359 {\r
7360   FARPROC lpProc;\r
7361   char *p, *q;\r
7362 \r
7363   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7364 \r
7365   if (str == NULL) str = "";\r
7366   p = (char *) malloc(2 * strlen(str) + 2);\r
7367   q = p;\r
7368   while (*str) {\r
7369     if (*str == '\n') *q++ = '\r';\r
7370     *q++ = *str++;\r
7371   }\r
7372   *q = NULLCHAR;\r
7373   if (commentText != NULL) free(commentText);\r
7374 \r
7375   commentIndex = index;\r
7376   commentTitle = title;\r
7377   commentText = p;\r
7378   editComment = edit;\r
7379 \r
7380   if (commentDialog) {\r
7381     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7382     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7383   } else {\r
7384     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7385     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7386                  hwndMain, (DLGPROC)lpProc);\r
7387     FreeProcInstance(lpProc);\r
7388   }\r
7389   commentDialogUp = TRUE;\r
7390 }\r
7391 \r
7392 \r
7393 /*---------------------------------------------------------------------------*\\r
7394  *\r
7395  * Type-in move dialog functions\r
7396  * \r
7397 \*---------------------------------------------------------------------------*/\r
7398 \r
7399 LRESULT CALLBACK\r
7400 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7401 {\r
7402   char move[MSG_SIZ];\r
7403   HWND hInput;\r
7404   ChessMove moveType;\r
7405   int fromX, fromY, toX, toY;\r
7406   char promoChar;\r
7407 \r
7408   switch (message) {\r
7409   case WM_INITDIALOG:\r
7410     move[0] = (char) lParam;\r
7411     move[1] = NULLCHAR;\r
7412     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7413     hInput = GetDlgItem(hDlg, OPT_Move);\r
7414     SetWindowText(hInput, move);\r
7415     SetFocus(hInput);\r
7416     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7417     return FALSE;\r
7418 \r
7419   case WM_COMMAND:\r
7420     switch (LOWORD(wParam)) {\r
7421     case IDOK:\r
7422       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7423       { int n; Board board;\r
7424         // [HGM] FENedit\r
7425         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7426                 EditPositionPasteFEN(move);\r
7427                 EndDialog(hDlg, TRUE);\r
7428                 return TRUE;\r
7429         }\r
7430         // [HGM] movenum: allow move number to be typed in any mode\r
7431         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7432           currentMove = 2*n-1;\r
7433           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7434           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7435           EndDialog(hDlg, TRUE);\r
7436           DrawPosition(TRUE, boards[currentMove]);\r
7437           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7438           else DisplayMessage("", "");\r
7439           return TRUE;\r
7440         }\r
7441       }\r
7442       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7443         gameMode != Training) {\r
7444         DisplayMoveError("Displayed move is not current");\r
7445       } else {\r
7446 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7447         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7448           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7449         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7450         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7451           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7452           if (gameMode != Training)\r
7453               forwardMostMove = currentMove;\r
7454           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7455         } else {\r
7456           DisplayMoveError("Could not parse move");\r
7457         }\r
7458       }\r
7459       EndDialog(hDlg, TRUE);\r
7460       return TRUE;\r
7461     case IDCANCEL:\r
7462       EndDialog(hDlg, FALSE);\r
7463       return TRUE;\r
7464     default:\r
7465       break;\r
7466     }\r
7467     break;\r
7468   }\r
7469   return FALSE;\r
7470 }\r
7471 \r
7472 VOID\r
7473 PopUpMoveDialog(char firstchar)\r
7474 {\r
7475     FARPROC lpProc;\r
7476     \r
7477     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7478         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7479         gameMode == AnalyzeMode || gameMode == EditGame || \r
7480         gameMode == EditPosition || gameMode == IcsExamining ||\r
7481         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7482         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7483                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7484                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7485         gameMode == Training) {\r
7486       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7487       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7488         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7489       FreeProcInstance(lpProc);\r
7490     }\r
7491 }\r
7492 \r
7493 /*---------------------------------------------------------------------------*\\r
7494  *\r
7495  * Type-in name dialog functions\r
7496  * \r
7497 \*---------------------------------------------------------------------------*/\r
7498 \r
7499 LRESULT CALLBACK\r
7500 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7501 {\r
7502   char move[MSG_SIZ];\r
7503   HWND hInput;\r
7504 \r
7505   switch (message) {\r
7506   case WM_INITDIALOG:\r
7507     move[0] = (char) lParam;\r
7508     move[1] = NULLCHAR;\r
7509     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7510     hInput = GetDlgItem(hDlg, OPT_Name);\r
7511     SetWindowText(hInput, move);\r
7512     SetFocus(hInput);\r
7513     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7514     return FALSE;\r
7515 \r
7516   case WM_COMMAND:\r
7517     switch (LOWORD(wParam)) {\r
7518     case IDOK:\r
7519       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7520       appData.userName = strdup(move);\r
7521       SetUserLogo();\r
7522 \r
7523       EndDialog(hDlg, TRUE);\r
7524       return TRUE;\r
7525     case IDCANCEL:\r
7526       EndDialog(hDlg, FALSE);\r
7527       return TRUE;\r
7528     default:\r
7529       break;\r
7530     }\r
7531     break;\r
7532   }\r
7533   return FALSE;\r
7534 }\r
7535 \r
7536 VOID\r
7537 PopUpNameDialog(char firstchar)\r
7538 {\r
7539     FARPROC lpProc;\r
7540     \r
7541       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7542       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7543         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7544       FreeProcInstance(lpProc);\r
7545 }\r
7546 \r
7547 /*---------------------------------------------------------------------------*\\r
7548  *\r
7549  *  Error dialogs\r
7550  * \r
7551 \*---------------------------------------------------------------------------*/\r
7552 \r
7553 /* Nonmodal error box */\r
7554 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7555                              WPARAM wParam, LPARAM lParam);\r
7556 \r
7557 VOID\r
7558 ErrorPopUp(char *title, char *content)\r
7559 {\r
7560   FARPROC lpProc;\r
7561   char *p, *q;\r
7562   BOOLEAN modal = hwndMain == NULL;\r
7563 \r
7564   p = content;\r
7565   q = errorMessage;\r
7566   while (*p) {\r
7567     if (*p == '\n') {\r
7568       if (modal) {\r
7569         *q++ = ' ';\r
7570         p++;\r
7571       } else {\r
7572         *q++ = '\r';\r
7573         *q++ = *p++;\r
7574       }\r
7575     } else {\r
7576       *q++ = *p++;\r
7577     }\r
7578   }\r
7579   *q = NULLCHAR;\r
7580   strncpy(errorTitle, title, sizeof(errorTitle));\r
7581   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7582   \r
7583   if (modal) {\r
7584     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7585   } else {\r
7586     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7587     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7588                  hwndMain, (DLGPROC)lpProc);\r
7589     FreeProcInstance(lpProc);\r
7590   }\r
7591 }\r
7592 \r
7593 VOID\r
7594 ErrorPopDown()\r
7595 {\r
7596   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7597   if (errorDialog == NULL) return;\r
7598   DestroyWindow(errorDialog);\r
7599   errorDialog = NULL;\r
7600 }\r
7601 \r
7602 LRESULT CALLBACK\r
7603 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7604 {\r
7605   HANDLE hwndText;\r
7606   RECT rChild;\r
7607 \r
7608   switch (message) {\r
7609   case WM_INITDIALOG:\r
7610     GetWindowRect(hDlg, &rChild);\r
7611 \r
7612     /*\r
7613     SetWindowPos(hDlg, NULL, rChild.left,\r
7614       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7615       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7616     */\r
7617 \r
7618     /* \r
7619         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7620         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7621         and it doesn't work when you resize the dialog.\r
7622         For now, just give it a default position.\r
7623     */\r
7624     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7625 \r
7626     errorDialog = hDlg;\r
7627     SetWindowText(hDlg, errorTitle);\r
7628     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7629     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7630     return FALSE;\r
7631 \r
7632   case WM_COMMAND:\r
7633     switch (LOWORD(wParam)) {\r
7634     case IDOK:\r
7635     case IDCANCEL:\r
7636       if (errorDialog == hDlg) errorDialog = NULL;\r
7637       DestroyWindow(hDlg);\r
7638       return TRUE;\r
7639 \r
7640     default:\r
7641       break;\r
7642     }\r
7643     break;\r
7644   }\r
7645   return FALSE;\r
7646 }\r
7647 \r
7648 #ifdef GOTHIC\r
7649 HWND gothicDialog = NULL;\r
7650 \r
7651 LRESULT CALLBACK\r
7652 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7653 {\r
7654   HANDLE hwndText;\r
7655   RECT rChild;\r
7656   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7657 \r
7658   switch (message) {\r
7659   case WM_INITDIALOG:\r
7660     GetWindowRect(hDlg, &rChild);\r
7661 \r
7662     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7663                                                              SWP_NOZORDER);\r
7664 \r
7665     /* \r
7666         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7667         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7668         and it doesn't work when you resize the dialog.\r
7669         For now, just give it a default position.\r
7670     */\r
7671     gothicDialog = hDlg;\r
7672     SetWindowText(hDlg, errorTitle);\r
7673     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7674     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7675     return FALSE;\r
7676 \r
7677   case WM_COMMAND:\r
7678     switch (LOWORD(wParam)) {\r
7679     case IDOK:\r
7680     case IDCANCEL:\r
7681       if (errorDialog == hDlg) errorDialog = NULL;\r
7682       DestroyWindow(hDlg);\r
7683       return TRUE;\r
7684 \r
7685     default:\r
7686       break;\r
7687     }\r
7688     break;\r
7689   }\r
7690   return FALSE;\r
7691 }\r
7692 \r
7693 VOID\r
7694 GothicPopUp(char *title, VariantClass variant)\r
7695 {\r
7696   FARPROC lpProc;\r
7697   static char *lastTitle;\r
7698 \r
7699   strncpy(errorTitle, title, sizeof(errorTitle));\r
7700   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7701 \r
7702   if(lastTitle != title && gothicDialog != NULL) {\r
7703     DestroyWindow(gothicDialog);\r
7704     gothicDialog = NULL;\r
7705   }\r
7706   if(variant != VariantNormal && gothicDialog == NULL) {\r
7707     title = lastTitle;\r
7708     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7709     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7710                  hwndMain, (DLGPROC)lpProc);\r
7711     FreeProcInstance(lpProc);\r
7712   }\r
7713 }\r
7714 #endif\r
7715 \r
7716 /*---------------------------------------------------------------------------*\\r
7717  *\r
7718  *  Ics Interaction console functions\r
7719  *\r
7720 \*---------------------------------------------------------------------------*/\r
7721 \r
7722 #define HISTORY_SIZE 64\r
7723 static char *history[HISTORY_SIZE];\r
7724 int histIn = 0, histP = 0;\r
7725 \r
7726 VOID\r
7727 SaveInHistory(char *cmd)\r
7728 {\r
7729   if (history[histIn] != NULL) {\r
7730     free(history[histIn]);\r
7731     history[histIn] = NULL;\r
7732   }\r
7733   if (*cmd == NULLCHAR) return;\r
7734   history[histIn] = StrSave(cmd);\r
7735   histIn = (histIn + 1) % HISTORY_SIZE;\r
7736   if (history[histIn] != NULL) {\r
7737     free(history[histIn]);\r
7738     history[histIn] = NULL;\r
7739   }\r
7740   histP = histIn;\r
7741 }\r
7742 \r
7743 char *\r
7744 PrevInHistory(char *cmd)\r
7745 {\r
7746   int newhp;\r
7747   if (histP == histIn) {\r
7748     if (history[histIn] != NULL) free(history[histIn]);\r
7749     history[histIn] = StrSave(cmd);\r
7750   }\r
7751   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7752   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7753   histP = newhp;\r
7754   return history[histP];\r
7755 }\r
7756 \r
7757 char *\r
7758 NextInHistory()\r
7759 {\r
7760   if (histP == histIn) return NULL;\r
7761   histP = (histP + 1) % HISTORY_SIZE;\r
7762   return history[histP];\r
7763 }\r
7764 \r
7765 typedef struct {\r
7766   char *item;\r
7767   char *command;\r
7768   BOOLEAN getname;\r
7769   BOOLEAN immediate;\r
7770 } IcsTextMenuEntry;\r
7771 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7772 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7773 \r
7774 void\r
7775 ParseIcsTextMenu(char *icsTextMenuString)\r
7776 {\r
7777 //  int flags = 0;\r
7778   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7779   char *p = icsTextMenuString;\r
7780   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7781     free(e->item);\r
7782     e->item = NULL;\r
7783     if (e->command != NULL) {\r
7784       free(e->command);\r
7785       e->command = NULL;\r
7786     }\r
7787     e++;\r
7788   }\r
7789   e = icsTextMenuEntry;\r
7790   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7791     if (*p == ';' || *p == '\n') {\r
7792       e->item = strdup("-");\r
7793       e->command = NULL;\r
7794       p++;\r
7795     } else if (*p == '-') {\r
7796       e->item = strdup("-");\r
7797       e->command = NULL;\r
7798       p++;\r
7799       if (*p) p++;\r
7800     } else {\r
7801       char *q, *r, *s, *t;\r
7802       char c;\r
7803       q = strchr(p, ',');\r
7804       if (q == NULL) break;\r
7805       *q = NULLCHAR;\r
7806       r = strchr(q + 1, ',');\r
7807       if (r == NULL) break;\r
7808       *r = NULLCHAR;\r
7809       s = strchr(r + 1, ',');\r
7810       if (s == NULL) break;\r
7811       *s = NULLCHAR;\r
7812       c = ';';\r
7813       t = strchr(s + 1, c);\r
7814       if (t == NULL) {\r
7815         c = '\n';\r
7816         t = strchr(s + 1, c);\r
7817       }\r
7818       if (t != NULL) *t = NULLCHAR;\r
7819       e->item = strdup(p);\r
7820       e->command = strdup(q + 1);\r
7821       e->getname = *(r + 1) != '0';\r
7822       e->immediate = *(s + 1) != '0';\r
7823       *q = ',';\r
7824       *r = ',';\r
7825       *s = ',';\r
7826       if (t == NULL) break;\r
7827       *t = c;\r
7828       p = t + 1;\r
7829     }\r
7830     e++;\r
7831   } \r
7832 }\r
7833 \r
7834 HMENU\r
7835 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7836 {\r
7837   HMENU hmenu, h;\r
7838   int i = 0;\r
7839   hmenu = LoadMenu(hInst, "TextMenu");\r
7840   h = GetSubMenu(hmenu, 0);\r
7841   while (e->item) {\r
7842     if (strcmp(e->item, "-") == 0) {\r
7843       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7844     } else {\r
7845       if (e->item[0] == '|') {\r
7846         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7847                    IDM_CommandX + i, &e->item[1]);\r
7848       } else {\r
7849         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7850       }\r
7851     }\r
7852     e++;\r
7853     i++;\r
7854   } \r
7855   return hmenu;\r
7856 }\r
7857 \r
7858 WNDPROC consoleTextWindowProc;\r
7859 \r
7860 void\r
7861 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7862 {\r
7863   char buf[MSG_SIZ], name[MSG_SIZ];\r
7864   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7865   CHARRANGE sel;\r
7866 \r
7867   if (!getname) {\r
7868     SetWindowText(hInput, command);\r
7869     if (immediate) {\r
7870       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7871     } else {\r
7872       sel.cpMin = 999999;\r
7873       sel.cpMax = 999999;\r
7874       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7875       SetFocus(hInput);\r
7876     }\r
7877     return;\r
7878   }    \r
7879   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7880   if (sel.cpMin == sel.cpMax) {\r
7881     /* Expand to surrounding word */\r
7882     TEXTRANGE tr;\r
7883     do {\r
7884       tr.chrg.cpMax = sel.cpMin;\r
7885       tr.chrg.cpMin = --sel.cpMin;\r
7886       if (sel.cpMin < 0) break;\r
7887       tr.lpstrText = name;\r
7888       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7889     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7890     sel.cpMin++;\r
7891 \r
7892     do {\r
7893       tr.chrg.cpMin = sel.cpMax;\r
7894       tr.chrg.cpMax = ++sel.cpMax;\r
7895       tr.lpstrText = name;\r
7896       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7897     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7898     sel.cpMax--;\r
7899 \r
7900     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7901       MessageBeep(MB_ICONEXCLAMATION);\r
7902       return;\r
7903     }\r
7904     tr.chrg = sel;\r
7905     tr.lpstrText = name;\r
7906     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7907   } else {\r
7908     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7909       MessageBeep(MB_ICONEXCLAMATION);\r
7910       return;\r
7911     }\r
7912     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7913   }\r
7914   if (immediate) {\r
7915     sprintf(buf, "%s %s", command, name);\r
7916     SetWindowText(hInput, buf);\r
7917     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7918   } else {\r
7919     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7920     SetWindowText(hInput, buf);\r
7921     sel.cpMin = 999999;\r
7922     sel.cpMax = 999999;\r
7923     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7924     SetFocus(hInput);\r
7925   }\r
7926 }\r
7927 \r
7928 LRESULT CALLBACK \r
7929 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7930 {\r
7931   HWND hInput;\r
7932   CHARRANGE sel;\r
7933 \r
7934   switch (message) {\r
7935   case WM_KEYDOWN:\r
7936     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7937     switch (wParam) {\r
7938     case VK_PRIOR:\r
7939       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7940       return 0;\r
7941     case VK_NEXT:\r
7942       sel.cpMin = 999999;\r
7943       sel.cpMax = 999999;\r
7944       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7945       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7946       return 0;\r
7947     }\r
7948     break;\r
7949   case WM_CHAR:\r
7950    if(wParam != '\022') {\r
7951     if (wParam == '\t') {\r
7952       if (GetKeyState(VK_SHIFT) < 0) {\r
7953         /* shifted */\r
7954         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7955         if (buttonDesc[0].hwnd) {\r
7956           SetFocus(buttonDesc[0].hwnd);\r
7957         } else {\r
7958           SetFocus(hwndMain);\r
7959         }\r
7960       } else {\r
7961         /* unshifted */\r
7962         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7963       }\r
7964     } else {\r
7965       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7966       JAWS_DELETE( SetFocus(hInput); )\r
7967       SendMessage(hInput, message, wParam, lParam);\r
7968     }\r
7969     return 0;\r
7970    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7971   case WM_RBUTTONUP:\r
7972     if (GetKeyState(VK_SHIFT) & ~1) {\r
7973       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7974         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7975     } else {\r
7976       POINT pt;\r
7977       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7978       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7979       if (sel.cpMin == sel.cpMax) {\r
7980         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7981         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7982       }\r
7983       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7984         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7985       }\r
7986       pt.x = LOWORD(lParam);\r
7987       pt.y = HIWORD(lParam);\r
7988       MenuPopup(hwnd, pt, hmenu, -1);\r
7989     }\r
7990     return 0;\r
7991   case WM_PASTE:\r
7992     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7993     SetFocus(hInput);\r
7994     return SendMessage(hInput, message, wParam, lParam);\r
7995   case WM_MBUTTONDOWN:\r
7996     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7997   case WM_RBUTTONDOWN:\r
7998     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7999       /* Move selection here if it was empty */\r
8000       POINT pt;\r
8001       pt.x = LOWORD(lParam);\r
8002       pt.y = HIWORD(lParam);\r
8003       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8004       if (sel.cpMin == sel.cpMax) {\r
8005         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
8006         sel.cpMax = sel.cpMin;\r
8007         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8008       }\r
8009       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
8010     }\r
8011     return 0;\r
8012   case WM_COMMAND:\r
8013     switch (LOWORD(wParam)) {\r
8014     case IDM_QuickPaste:\r
8015       {\r
8016         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8017         if (sel.cpMin == sel.cpMax) {\r
8018           MessageBeep(MB_ICONEXCLAMATION);\r
8019           return 0;\r
8020         }\r
8021         SendMessage(hwnd, WM_COPY, 0, 0);\r
8022         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8023         SendMessage(hInput, WM_PASTE, 0, 0);\r
8024         SetFocus(hInput);\r
8025         return 0;\r
8026       }\r
8027     case IDM_Cut:\r
8028       SendMessage(hwnd, WM_CUT, 0, 0);\r
8029       return 0;\r
8030     case IDM_Paste:\r
8031       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8032       return 0;\r
8033     case IDM_Copy:\r
8034       SendMessage(hwnd, WM_COPY, 0, 0);\r
8035       return 0;\r
8036     default:\r
8037       {\r
8038         int i = LOWORD(wParam) - IDM_CommandX;\r
8039         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8040             icsTextMenuEntry[i].command != NULL) {\r
8041           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8042                    icsTextMenuEntry[i].getname,\r
8043                    icsTextMenuEntry[i].immediate);\r
8044           return 0;\r
8045         }\r
8046       }\r
8047       break;\r
8048     }\r
8049     break;\r
8050   }\r
8051   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8052 }\r
8053 \r
8054 WNDPROC consoleInputWindowProc;\r
8055 \r
8056 LRESULT CALLBACK\r
8057 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8058 {\r
8059   char buf[MSG_SIZ];\r
8060   char *p;\r
8061   static BOOL sendNextChar = FALSE;\r
8062   static BOOL quoteNextChar = FALSE;\r
8063   InputSource *is = consoleInputSource;\r
8064   CHARFORMAT cf;\r
8065   CHARRANGE sel;\r
8066 \r
8067   switch (message) {\r
8068   case WM_CHAR:\r
8069     if (!appData.localLineEditing || sendNextChar) {\r
8070       is->buf[0] = (CHAR) wParam;\r
8071       is->count = 1;\r
8072       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8073       sendNextChar = FALSE;\r
8074       return 0;\r
8075     }\r
8076     if (quoteNextChar) {\r
8077       buf[0] = (char) wParam;\r
8078       buf[1] = NULLCHAR;\r
8079       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8080       quoteNextChar = FALSE;\r
8081       return 0;\r
8082     }\r
8083     switch (wParam) {\r
8084     case '\r':   /* Enter key */\r
8085       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8086       if (consoleEcho) SaveInHistory(is->buf);\r
8087       is->buf[is->count++] = '\n';\r
8088       is->buf[is->count] = NULLCHAR;\r
8089       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8090       if (consoleEcho) {\r
8091         ConsoleOutput(is->buf, is->count, TRUE);\r
8092       } else if (appData.localLineEditing) {\r
8093         ConsoleOutput("\n", 1, TRUE);\r
8094       }\r
8095       /* fall thru */\r
8096     case '\033': /* Escape key */\r
8097       SetWindowText(hwnd, "");\r
8098       cf.cbSize = sizeof(CHARFORMAT);\r
8099       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8100       if (consoleEcho) {\r
8101         cf.crTextColor = textAttribs[ColorNormal].color;\r
8102       } else {\r
8103         cf.crTextColor = COLOR_ECHOOFF;\r
8104       }\r
8105       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8106       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8107       return 0;\r
8108     case '\t':   /* Tab key */\r
8109       if (GetKeyState(VK_SHIFT) < 0) {\r
8110         /* shifted */\r
8111         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8112       } else {\r
8113         /* unshifted */\r
8114         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8115         if (buttonDesc[0].hwnd) {\r
8116           SetFocus(buttonDesc[0].hwnd);\r
8117         } else {\r
8118           SetFocus(hwndMain);\r
8119         }\r
8120       }\r
8121       return 0;\r
8122     case '\023': /* Ctrl+S */\r
8123       sendNextChar = TRUE;\r
8124       return 0;\r
8125     case '\021': /* Ctrl+Q */\r
8126       quoteNextChar = TRUE;\r
8127       return 0;\r
8128     JAWS_REPLAY\r
8129     default:\r
8130       break;\r
8131     }\r
8132     break;\r
8133   case WM_KEYDOWN:\r
8134     switch (wParam) {\r
8135     case VK_UP:\r
8136       GetWindowText(hwnd, buf, MSG_SIZ);\r
8137       p = PrevInHistory(buf);\r
8138       if (p != NULL) {\r
8139         SetWindowText(hwnd, p);\r
8140         sel.cpMin = 999999;\r
8141         sel.cpMax = 999999;\r
8142         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8143         return 0;\r
8144       }\r
8145       break;\r
8146     case VK_DOWN:\r
8147       p = NextInHistory();\r
8148       if (p != NULL) {\r
8149         SetWindowText(hwnd, p);\r
8150         sel.cpMin = 999999;\r
8151         sel.cpMax = 999999;\r
8152         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8153         return 0;\r
8154       }\r
8155       break;\r
8156     case VK_HOME:\r
8157     case VK_END:\r
8158       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8159       /* fall thru */\r
8160     case VK_PRIOR:\r
8161     case VK_NEXT:\r
8162       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8163       return 0;\r
8164     }\r
8165     break;\r
8166   case WM_MBUTTONDOWN:\r
8167     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8168       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8169     break;\r
8170   case WM_RBUTTONUP:\r
8171     if (GetKeyState(VK_SHIFT) & ~1) {\r
8172       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8173         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8174     } else {\r
8175       POINT pt;\r
8176       HMENU hmenu;\r
8177       hmenu = LoadMenu(hInst, "InputMenu");\r
8178       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8179       if (sel.cpMin == sel.cpMax) {\r
8180         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8181         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8182       }\r
8183       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8184         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8185       }\r
8186       pt.x = LOWORD(lParam);\r
8187       pt.y = HIWORD(lParam);\r
8188       MenuPopup(hwnd, pt, hmenu, -1);\r
8189     }\r
8190     return 0;\r
8191   case WM_COMMAND:\r
8192     switch (LOWORD(wParam)) { \r
8193     case IDM_Undo:\r
8194       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8195       return 0;\r
8196     case IDM_SelectAll:\r
8197       sel.cpMin = 0;\r
8198       sel.cpMax = -1; /*999999?*/\r
8199       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8200       return 0;\r
8201     case IDM_Cut:\r
8202       SendMessage(hwnd, WM_CUT, 0, 0);\r
8203       return 0;\r
8204     case IDM_Paste:\r
8205       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8206       return 0;\r
8207     case IDM_Copy:\r
8208       SendMessage(hwnd, WM_COPY, 0, 0);\r
8209       return 0;\r
8210     }\r
8211     break;\r
8212   }\r
8213   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8214 }\r
8215 \r
8216 #define CO_MAX  100000\r
8217 #define CO_TRIM   1000\r
8218 \r
8219 LRESULT CALLBACK\r
8220 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8221 {\r
8222   static SnapData sd;\r
8223   static HWND hText, hInput /*, hFocus*/;\r
8224 //  InputSource *is = consoleInputSource;\r
8225   RECT rect;\r
8226   static int sizeX, sizeY;\r
8227   int newSizeX, newSizeY;\r
8228   MINMAXINFO *mmi;\r
8229 \r
8230   switch (message) {\r
8231   case WM_INITDIALOG: /* message: initialize dialog box */\r
8232     hwndConsole = hDlg;\r
8233     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8234     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8235     SetFocus(hInput);\r
8236     consoleTextWindowProc = (WNDPROC)\r
8237       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8238     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8239     consoleInputWindowProc = (WNDPROC)\r
8240       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8241     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8242     Colorize(ColorNormal, TRUE);\r
8243     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8244     ChangedConsoleFont();\r
8245     GetClientRect(hDlg, &rect);\r
8246     sizeX = rect.right;\r
8247     sizeY = rect.bottom;\r
8248     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8249         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8250       WINDOWPLACEMENT wp;\r
8251       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8252       wp.length = sizeof(WINDOWPLACEMENT);\r
8253       wp.flags = 0;\r
8254       wp.showCmd = SW_SHOW;\r
8255       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8256       wp.rcNormalPosition.left = wpConsole.x;\r
8257       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8258       wp.rcNormalPosition.top = wpConsole.y;\r
8259       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8260       SetWindowPlacement(hDlg, &wp);\r
8261     }\r
8262 #if 1\r
8263    // [HGM] Chessknight's change 2004-07-13\r
8264    else { /* Determine Defaults */\r
8265        WINDOWPLACEMENT wp;\r
8266        wpConsole.x = winWidth + 1;\r
8267        wpConsole.y = boardY;\r
8268        wpConsole.width = screenWidth -  winWidth;\r
8269        wpConsole.height = winHeight;\r
8270        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8271        wp.length = sizeof(WINDOWPLACEMENT);\r
8272        wp.flags = 0;\r
8273        wp.showCmd = SW_SHOW;\r
8274        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8275        wp.rcNormalPosition.left = wpConsole.x;\r
8276        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8277        wp.rcNormalPosition.top = wpConsole.y;\r
8278        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8279        SetWindowPlacement(hDlg, &wp);\r
8280     }\r
8281 #endif\r
8282     return FALSE;\r
8283 \r
8284   case WM_SETFOCUS:\r
8285     SetFocus(hInput);\r
8286     return 0;\r
8287 \r
8288   case WM_CLOSE:\r
8289     ExitEvent(0);\r
8290     /* not reached */\r
8291     break;\r
8292 \r
8293   case WM_SIZE:\r
8294     if (IsIconic(hDlg)) break;\r
8295     newSizeX = LOWORD(lParam);\r
8296     newSizeY = HIWORD(lParam);\r
8297     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8298       RECT rectText, rectInput;\r
8299       POINT pt;\r
8300       int newTextHeight, newTextWidth;\r
8301       GetWindowRect(hText, &rectText);\r
8302       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8303       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8304       if (newTextHeight < 0) {\r
8305         newSizeY += -newTextHeight;\r
8306         newTextHeight = 0;\r
8307       }\r
8308       SetWindowPos(hText, NULL, 0, 0,\r
8309         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8310       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8311       pt.x = rectInput.left;\r
8312       pt.y = rectInput.top + newSizeY - sizeY;\r
8313       ScreenToClient(hDlg, &pt);\r
8314       SetWindowPos(hInput, NULL, \r
8315         pt.x, pt.y, /* needs client coords */   \r
8316         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8317         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8318     }\r
8319     sizeX = newSizeX;\r
8320     sizeY = newSizeY;\r
8321     break;\r
8322 \r
8323   case WM_GETMINMAXINFO:\r
8324     /* Prevent resizing window too small */\r
8325     mmi = (MINMAXINFO *) lParam;\r
8326     mmi->ptMinTrackSize.x = 100;\r
8327     mmi->ptMinTrackSize.y = 100;\r
8328     break;\r
8329 \r
8330   /* [AS] Snapping */\r
8331   case WM_ENTERSIZEMOVE:\r
8332     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8333 \r
8334   case WM_SIZING:\r
8335     return OnSizing( &sd, hDlg, wParam, lParam );\r
8336 \r
8337   case WM_MOVING:\r
8338     return OnMoving( &sd, hDlg, wParam, lParam );\r
8339 \r
8340   case WM_EXITSIZEMOVE:\r
8341     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8342   }\r
8343 \r
8344   return DefWindowProc(hDlg, message, wParam, lParam);\r
8345 }\r
8346 \r
8347 \r
8348 VOID\r
8349 ConsoleCreate()\r
8350 {\r
8351   HWND hCons;\r
8352   if (hwndConsole) return;\r
8353   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8354   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8355 }\r
8356 \r
8357 \r
8358 VOID\r
8359 ConsoleOutput(char* data, int length, int forceVisible)\r
8360 {\r
8361   HWND hText;\r
8362   int trim, exlen;\r
8363   char *p, *q;\r
8364   char buf[CO_MAX+1];\r
8365   POINT pEnd;\r
8366   RECT rect;\r
8367   static int delayLF = 0;\r
8368   CHARRANGE savesel, sel;\r
8369 \r
8370   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8371   p = data;\r
8372   q = buf;\r
8373   if (delayLF) {\r
8374     *q++ = '\r';\r
8375     *q++ = '\n';\r
8376     delayLF = 0;\r
8377   }\r
8378   while (length--) {\r
8379     if (*p == '\n') {\r
8380       if (*++p) {\r
8381         *q++ = '\r';\r
8382         *q++ = '\n';\r
8383       } else {\r
8384         delayLF = 1;\r
8385       }\r
8386     } else if (*p == '\007') {\r
8387        MyPlaySound(&sounds[(int)SoundBell]);\r
8388        p++;\r
8389     } else {\r
8390       *q++ = *p++;\r
8391     }\r
8392   }\r
8393   *q = NULLCHAR;\r
8394   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8395   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8396   /* Save current selection */\r
8397   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8398   exlen = GetWindowTextLength(hText);\r
8399   /* Find out whether current end of text is visible */\r
8400   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8401   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8402   /* Trim existing text if it's too long */\r
8403   if (exlen + (q - buf) > CO_MAX) {\r
8404     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8405     sel.cpMin = 0;\r
8406     sel.cpMax = trim;\r
8407     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8408     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8409     exlen -= trim;\r
8410     savesel.cpMin -= trim;\r
8411     savesel.cpMax -= trim;\r
8412     if (exlen < 0) exlen = 0;\r
8413     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8414     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8415   }\r
8416   /* Append the new text */\r
8417   sel.cpMin = exlen;\r
8418   sel.cpMax = exlen;\r
8419   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8420   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8421   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8422   if (forceVisible || exlen == 0 ||\r
8423       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8424        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8425     /* Scroll to make new end of text visible if old end of text\r
8426        was visible or new text is an echo of user typein */\r
8427     sel.cpMin = 9999999;\r
8428     sel.cpMax = 9999999;\r
8429     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8430     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8431     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8432     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8433   }\r
8434   if (savesel.cpMax == exlen || forceVisible) {\r
8435     /* Move insert point to new end of text if it was at the old\r
8436        end of text or if the new text is an echo of user typein */\r
8437     sel.cpMin = 9999999;\r
8438     sel.cpMax = 9999999;\r
8439     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8440   } else {\r
8441     /* Restore previous selection */\r
8442     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8443   }\r
8444   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8445 }\r
8446 \r
8447 /*---------*/\r
8448 \r
8449 \r
8450 void\r
8451 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8452 {\r
8453   char buf[100];\r
8454   char *str;\r
8455   COLORREF oldFg, oldBg;\r
8456   HFONT oldFont;\r
8457   RECT rect;\r
8458 \r
8459   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8460 \r
8461   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8462   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8463   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8464 \r
8465   rect.left = x;\r
8466   rect.right = x + squareSize;\r
8467   rect.top  = y;\r
8468   rect.bottom = y + squareSize;\r
8469   str = buf;\r
8470 \r
8471   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8472                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8473              y, ETO_CLIPPED|ETO_OPAQUE,\r
8474              &rect, str, strlen(str), NULL);\r
8475 \r
8476   (void) SetTextColor(hdc, oldFg);\r
8477   (void) SetBkColor(hdc, oldBg);\r
8478   (void) SelectObject(hdc, oldFont);\r
8479 }\r
8480 \r
8481 void\r
8482 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8483               RECT *rect, char *color, char *flagFell)\r
8484 {\r
8485   char buf[100];\r
8486   char *str;\r
8487   COLORREF oldFg, oldBg;\r
8488   HFONT oldFont;\r
8489 \r
8490   if (appData.clockMode) {\r
8491     if (tinyLayout)\r
8492       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8493     else\r
8494       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8495     str = buf;\r
8496   } else {\r
8497     str = color;\r
8498   }\r
8499 \r
8500   if (highlight) {\r
8501     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8502     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8503   } else {\r
8504     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8505     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8506   }\r
8507   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8508 \r
8509   JAWS_SILENCE\r
8510 \r
8511   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8512              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8513              rect, str, strlen(str), NULL);\r
8514   if(logoHeight > 0 && appData.clockMode) {\r
8515       RECT r;\r
8516       sprintf(buf, "%s %s", buf+7, flagFell);\r
8517       r.top = rect->top + logoHeight/2;\r
8518       r.left = rect->left;\r
8519       r.right = rect->right;\r
8520       r.bottom = rect->bottom;\r
8521       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8522                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8523                  &r, str, strlen(str), NULL);\r
8524   }\r
8525   (void) SetTextColor(hdc, oldFg);\r
8526   (void) SetBkColor(hdc, oldBg);\r
8527   (void) SelectObject(hdc, oldFont);\r
8528 }\r
8529 \r
8530 \r
8531 int\r
8532 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8533            OVERLAPPED *ovl)\r
8534 {\r
8535   int ok, err;\r
8536 \r
8537   /* [AS]  */\r
8538   if( count <= 0 ) {\r
8539     if (appData.debugMode) {\r
8540       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8541     }\r
8542 \r
8543     return ERROR_INVALID_USER_BUFFER;\r
8544   }\r
8545 \r
8546   ResetEvent(ovl->hEvent);\r
8547   ovl->Offset = ovl->OffsetHigh = 0;\r
8548   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8549   if (ok) {\r
8550     err = NO_ERROR;\r
8551   } else {\r
8552     err = GetLastError();\r
8553     if (err == ERROR_IO_PENDING) {\r
8554       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8555       if (ok)\r
8556         err = NO_ERROR;\r
8557       else\r
8558         err = GetLastError();\r
8559     }\r
8560   }\r
8561   return err;\r
8562 }\r
8563 \r
8564 int\r
8565 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8566             OVERLAPPED *ovl)\r
8567 {\r
8568   int ok, err;\r
8569 \r
8570   ResetEvent(ovl->hEvent);\r
8571   ovl->Offset = ovl->OffsetHigh = 0;\r
8572   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8573   if (ok) {\r
8574     err = NO_ERROR;\r
8575   } else {\r
8576     err = GetLastError();\r
8577     if (err == ERROR_IO_PENDING) {\r
8578       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8579       if (ok)\r
8580         err = NO_ERROR;\r
8581       else\r
8582         err = GetLastError();\r
8583     }\r
8584   }\r
8585   return err;\r
8586 }\r
8587 \r
8588 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8589 void CheckForInputBufferFull( InputSource * is )\r
8590 {\r
8591     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8592         /* Look for end of line */\r
8593         char * p = is->buf;\r
8594         \r
8595         while( p < is->next && *p != '\n' ) {\r
8596             p++;\r
8597         }\r
8598 \r
8599         if( p >= is->next ) {\r
8600             if (appData.debugMode) {\r
8601                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8602             }\r
8603 \r
8604             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8605             is->count = (DWORD) -1;\r
8606             is->next = is->buf;\r
8607         }\r
8608     }\r
8609 }\r
8610 \r
8611 DWORD\r
8612 InputThread(LPVOID arg)\r
8613 {\r
8614   InputSource *is;\r
8615   OVERLAPPED ovl;\r
8616 \r
8617   is = (InputSource *) arg;\r
8618   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8619   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8620   while (is->hThread != NULL) {\r
8621     is->error = DoReadFile(is->hFile, is->next,\r
8622                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8623                            &is->count, &ovl);\r
8624     if (is->error == NO_ERROR) {\r
8625       is->next += is->count;\r
8626     } else {\r
8627       if (is->error == ERROR_BROKEN_PIPE) {\r
8628         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8629         is->count = 0;\r
8630       } else {\r
8631         is->count = (DWORD) -1;\r
8632         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8633         break; \r
8634       }\r
8635     }\r
8636 \r
8637     CheckForInputBufferFull( is );\r
8638 \r
8639     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8640 \r
8641     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8642 \r
8643     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8644   }\r
8645 \r
8646   CloseHandle(ovl.hEvent);\r
8647   CloseHandle(is->hFile);\r
8648 \r
8649   if (appData.debugMode) {\r
8650     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8651   }\r
8652 \r
8653   return 0;\r
8654 }\r
8655 \r
8656 \r
8657 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8658 DWORD\r
8659 NonOvlInputThread(LPVOID arg)\r
8660 {\r
8661   InputSource *is;\r
8662   char *p, *q;\r
8663   int i;\r
8664   char prev;\r
8665 \r
8666   is = (InputSource *) arg;\r
8667   while (is->hThread != NULL) {\r
8668     is->error = ReadFile(is->hFile, is->next,\r
8669                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8670                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8671     if (is->error == NO_ERROR) {\r
8672       /* Change CRLF to LF */\r
8673       if (is->next > is->buf) {\r
8674         p = is->next - 1;\r
8675         i = is->count + 1;\r
8676       } else {\r
8677         p = is->next;\r
8678         i = is->count;\r
8679       }\r
8680       q = p;\r
8681       prev = NULLCHAR;\r
8682       while (i > 0) {\r
8683         if (prev == '\r' && *p == '\n') {\r
8684           *(q-1) = '\n';\r
8685           is->count--;\r
8686         } else { \r
8687           *q++ = *p;\r
8688         }\r
8689         prev = *p++;\r
8690         i--;\r
8691       }\r
8692       *q = NULLCHAR;\r
8693       is->next = q;\r
8694     } else {\r
8695       if (is->error == ERROR_BROKEN_PIPE) {\r
8696         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8697         is->count = 0; \r
8698       } else {\r
8699         is->count = (DWORD) -1;\r
8700       }\r
8701     }\r
8702 \r
8703     CheckForInputBufferFull( is );\r
8704 \r
8705     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8706 \r
8707     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8708 \r
8709     if (is->count < 0) break;  /* Quit on error */\r
8710   }\r
8711   CloseHandle(is->hFile);\r
8712   return 0;\r
8713 }\r
8714 \r
8715 DWORD\r
8716 SocketInputThread(LPVOID arg)\r
8717 {\r
8718   InputSource *is;\r
8719 \r
8720   is = (InputSource *) arg;\r
8721   while (is->hThread != NULL) {\r
8722     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8723     if ((int)is->count == SOCKET_ERROR) {\r
8724       is->count = (DWORD) -1;\r
8725       is->error = WSAGetLastError();\r
8726     } else {\r
8727       is->error = NO_ERROR;\r
8728       is->next += is->count;\r
8729       if (is->count == 0 && is->second == is) {\r
8730         /* End of file on stderr; quit with no message */\r
8731         break;\r
8732       }\r
8733     }\r
8734     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8735 \r
8736     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8737 \r
8738     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8739   }\r
8740   return 0;\r
8741 }\r
8742 \r
8743 VOID\r
8744 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8745 {\r
8746   InputSource *is;\r
8747 \r
8748   is = (InputSource *) lParam;\r
8749   if (is->lineByLine) {\r
8750     /* Feed in lines one by one */\r
8751     char *p = is->buf;\r
8752     char *q = p;\r
8753     while (q < is->next) {\r
8754       if (*q++ == '\n') {\r
8755         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8756         p = q;\r
8757       }\r
8758     }\r
8759     \r
8760     /* Move any partial line to the start of the buffer */\r
8761     q = is->buf;\r
8762     while (p < is->next) {\r
8763       *q++ = *p++;\r
8764     }\r
8765     is->next = q;\r
8766 \r
8767     if (is->error != NO_ERROR || is->count == 0) {\r
8768       /* Notify backend of the error.  Note: If there was a partial\r
8769          line at the end, it is not flushed through. */\r
8770       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8771     }\r
8772   } else {\r
8773     /* Feed in the whole chunk of input at once */\r
8774     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8775     is->next = is->buf;\r
8776   }\r
8777 }\r
8778 \r
8779 /*---------------------------------------------------------------------------*\\r
8780  *\r
8781  *  Menu enables. Used when setting various modes.\r
8782  *\r
8783 \*---------------------------------------------------------------------------*/\r
8784 \r
8785 typedef struct {\r
8786   int item;\r
8787   int flags;\r
8788 } Enables;\r
8789 \r
8790 VOID\r
8791 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8792 {\r
8793   while (enab->item > 0) {\r
8794     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8795     enab++;\r
8796   }\r
8797 }\r
8798 \r
8799 Enables gnuEnables[] = {\r
8800   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8802   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8803   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8805   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8806   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8807   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8808   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8809   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8810   { -1, -1 }\r
8811 };\r
8812 \r
8813 Enables icsEnables[] = {\r
8814   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8820   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8826   { -1, -1 }\r
8827 };\r
8828 \r
8829 #ifdef ZIPPY\r
8830 Enables zippyEnables[] = {\r
8831   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8832   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8833   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8834   { -1, -1 }\r
8835 };\r
8836 #endif\r
8837 \r
8838 Enables ncpEnables[] = {\r
8839   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8841   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8843   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8844   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8845   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8846   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8847   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8848   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8849   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8850   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8851   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8852   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8853   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8854   { -1, -1 }\r
8855 };\r
8856 \r
8857 Enables trainingOnEnables[] = {\r
8858   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8859   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8860   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8861   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8862   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8863   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8866   { -1, -1 }\r
8867 };\r
8868 \r
8869 Enables trainingOffEnables[] = {\r
8870   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8871   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8872   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8873   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8874   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8875   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8876   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8877   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8878   { -1, -1 }\r
8879 };\r
8880 \r
8881 /* These modify either ncpEnables or gnuEnables */\r
8882 Enables cmailEnables[] = {\r
8883   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8884   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8885   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8886   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8887   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8888   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8889   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8890   { -1, -1 }\r
8891 };\r
8892 \r
8893 Enables machineThinkingEnables[] = {\r
8894   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8895   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8896   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8897   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8898   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8899   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8900   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8901   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8902   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8903   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8904   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8905   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8906   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8907   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8908   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8909   { -1, -1 }\r
8910 };\r
8911 \r
8912 Enables userThinkingEnables[] = {\r
8913   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8914   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8915   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8916   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8917   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8918   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8919   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8920   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8921   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8922   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8923   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8924   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8925   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8926   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8927   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8928   { -1, -1 }\r
8929 };\r
8930 \r
8931 /*---------------------------------------------------------------------------*\\r
8932  *\r
8933  *  Front-end interface functions exported by XBoard.\r
8934  *  Functions appear in same order as prototypes in frontend.h.\r
8935  * \r
8936 \*---------------------------------------------------------------------------*/\r
8937 VOID\r
8938 ModeHighlight()\r
8939 {\r
8940   static UINT prevChecked = 0;\r
8941   static int prevPausing = 0;\r
8942   UINT nowChecked;\r
8943 \r
8944   if (pausing != prevPausing) {\r
8945     prevPausing = pausing;\r
8946     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8947                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8948     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8949   }\r
8950 \r
8951   switch (gameMode) {\r
8952   case BeginningOfGame:\r
8953     if (appData.icsActive)\r
8954       nowChecked = IDM_IcsClient;\r
8955     else if (appData.noChessProgram)\r
8956       nowChecked = IDM_EditGame;\r
8957     else\r
8958       nowChecked = IDM_MachineBlack;\r
8959     break;\r
8960   case MachinePlaysBlack:\r
8961     nowChecked = IDM_MachineBlack;\r
8962     break;\r
8963   case MachinePlaysWhite:\r
8964     nowChecked = IDM_MachineWhite;\r
8965     break;\r
8966   case TwoMachinesPlay:\r
8967     nowChecked = IDM_TwoMachines;\r
8968     break;\r
8969   case AnalyzeMode:\r
8970     nowChecked = IDM_AnalysisMode;\r
8971     break;\r
8972   case AnalyzeFile:\r
8973     nowChecked = IDM_AnalyzeFile;\r
8974     break;\r
8975   case EditGame:\r
8976     nowChecked = IDM_EditGame;\r
8977     break;\r
8978   case PlayFromGameFile:\r
8979     nowChecked = IDM_LoadGame;\r
8980     break;\r
8981   case EditPosition:\r
8982     nowChecked = IDM_EditPosition;\r
8983     break;\r
8984   case Training:\r
8985     nowChecked = IDM_Training;\r
8986     break;\r
8987   case IcsPlayingWhite:\r
8988   case IcsPlayingBlack:\r
8989   case IcsObserving:\r
8990   case IcsIdle:\r
8991     nowChecked = IDM_IcsClient;\r
8992     break;\r
8993   default:\r
8994   case EndOfGame:\r
8995     nowChecked = 0;\r
8996     break;\r
8997   }\r
8998   if (prevChecked != 0)\r
8999     (void) CheckMenuItem(GetMenu(hwndMain),\r
9000                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
9001   if (nowChecked != 0)\r
9002     (void) CheckMenuItem(GetMenu(hwndMain),\r
9003                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
9004 \r
9005   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
9006     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
9007                           MF_BYCOMMAND|MF_ENABLED);\r
9008   } else {\r
9009     (void) EnableMenuItem(GetMenu(hwndMain), \r
9010                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
9011   }\r
9012 \r
9013   prevChecked = nowChecked;\r
9014 \r
9015   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
9016   if (appData.icsActive) {\r
9017        if (appData.icsEngineAnalyze) {\r
9018                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9019                        MF_BYCOMMAND|MF_CHECKED);\r
9020        } else {\r
9021                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9022                        MF_BYCOMMAND|MF_UNCHECKED);\r
9023        }\r
9024   }\r
9025 }\r
9026 \r
9027 VOID\r
9028 SetICSMode()\r
9029 {\r
9030   HMENU hmenu = GetMenu(hwndMain);\r
9031   SetMenuEnables(hmenu, icsEnables);\r
9032   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9033     MF_BYPOSITION|MF_ENABLED);\r
9034 #ifdef ZIPPY\r
9035   if (appData.zippyPlay) {\r
9036     SetMenuEnables(hmenu, zippyEnables);\r
9037     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9038          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9039           MF_BYCOMMAND|MF_ENABLED);\r
9040   }\r
9041 #endif\r
9042 }\r
9043 \r
9044 VOID\r
9045 SetGNUMode()\r
9046 {\r
9047   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9048 }\r
9049 \r
9050 VOID\r
9051 SetNCPMode()\r
9052 {\r
9053   HMENU hmenu = GetMenu(hwndMain);\r
9054   SetMenuEnables(hmenu, ncpEnables);\r
9055   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9056     MF_BYPOSITION|MF_GRAYED);\r
9057     DrawMenuBar(hwndMain);\r
9058 }\r
9059 \r
9060 VOID\r
9061 SetCmailMode()\r
9062 {\r
9063   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9064 }\r
9065 \r
9066 VOID \r
9067 SetTrainingModeOn()\r
9068 {\r
9069   int i;\r
9070   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9071   for (i = 0; i < N_BUTTONS; i++) {\r
9072     if (buttonDesc[i].hwnd != NULL)\r
9073       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9074   }\r
9075   CommentPopDown();\r
9076 }\r
9077 \r
9078 VOID SetTrainingModeOff()\r
9079 {\r
9080   int i;\r
9081   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9082   for (i = 0; i < N_BUTTONS; i++) {\r
9083     if (buttonDesc[i].hwnd != NULL)\r
9084       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9085   }\r
9086 }\r
9087 \r
9088 \r
9089 VOID\r
9090 SetUserThinkingEnables()\r
9091 {\r
9092   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9093 }\r
9094 \r
9095 VOID\r
9096 SetMachineThinkingEnables()\r
9097 {\r
9098   HMENU hMenu = GetMenu(hwndMain);\r
9099   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9100 \r
9101   SetMenuEnables(hMenu, machineThinkingEnables);\r
9102 \r
9103   if (gameMode == MachinePlaysBlack) {\r
9104     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9105   } else if (gameMode == MachinePlaysWhite) {\r
9106     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9107   } else if (gameMode == TwoMachinesPlay) {\r
9108     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9109   }\r
9110 }\r
9111 \r
9112 \r
9113 VOID\r
9114 DisplayTitle(char *str)\r
9115 {\r
9116   char title[MSG_SIZ], *host;\r
9117   if (str[0] != NULLCHAR) {\r
9118     strcpy(title, str);\r
9119   } else if (appData.icsActive) {\r
9120     if (appData.icsCommPort[0] != NULLCHAR)\r
9121       host = "ICS";\r
9122     else \r
9123       host = appData.icsHost;\r
9124     sprintf(title, "%s: %s", szTitle, host);\r
9125   } else if (appData.noChessProgram) {\r
9126     strcpy(title, szTitle);\r
9127   } else {\r
9128     strcpy(title, szTitle);\r
9129     strcat(title, ": ");\r
9130     strcat(title, first.tidy);\r
9131   }\r
9132   SetWindowText(hwndMain, title);\r
9133 }\r
9134 \r
9135 \r
9136 VOID\r
9137 DisplayMessage(char *str1, char *str2)\r
9138 {\r
9139   HDC hdc;\r
9140   HFONT oldFont;\r
9141   int remain = MESSAGE_TEXT_MAX - 1;\r
9142   int len;\r
9143 \r
9144   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9145   messageText[0] = NULLCHAR;\r
9146   if (*str1) {\r
9147     len = strlen(str1);\r
9148     if (len > remain) len = remain;\r
9149     strncpy(messageText, str1, len);\r
9150     messageText[len] = NULLCHAR;\r
9151     remain -= len;\r
9152   }\r
9153   if (*str2 && remain >= 2) {\r
9154     if (*str1) {\r
9155       strcat(messageText, "  ");\r
9156       remain -= 2;\r
9157     }\r
9158     len = strlen(str2);\r
9159     if (len > remain) len = remain;\r
9160     strncat(messageText, str2, len);\r
9161   }\r
9162   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9163 \r
9164   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9165 \r
9166   SAYMACHINEMOVE();\r
9167 \r
9168   hdc = GetDC(hwndMain);\r
9169   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9170   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9171              &messageRect, messageText, strlen(messageText), NULL);\r
9172   (void) SelectObject(hdc, oldFont);\r
9173   (void) ReleaseDC(hwndMain, hdc);\r
9174 }\r
9175 \r
9176 VOID\r
9177 DisplayError(char *str, int error)\r
9178 {\r
9179   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9180   int len;\r
9181 \r
9182   if (error == 0) {\r
9183     strcpy(buf, str);\r
9184   } else {\r
9185     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9186                         NULL, error, LANG_NEUTRAL,\r
9187                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9188     if (len > 0) {\r
9189       sprintf(buf, "%s:\n%s", str, buf2);\r
9190     } else {\r
9191       ErrorMap *em = errmap;\r
9192       while (em->err != 0 && em->err != error) em++;\r
9193       if (em->err != 0) {\r
9194         sprintf(buf, "%s:\n%s", str, em->msg);\r
9195       } else {\r
9196         sprintf(buf, "%s:\nError code %d", str, error);\r
9197       }\r
9198     }\r
9199   }\r
9200   \r
9201   ErrorPopUp("Error", buf);\r
9202 }\r
9203 \r
9204 \r
9205 VOID\r
9206 DisplayMoveError(char *str)\r
9207 {\r
9208   fromX = fromY = -1;\r
9209   ClearHighlights();\r
9210   DrawPosition(FALSE, NULL);\r
9211   if (appData.popupMoveErrors) {\r
9212     ErrorPopUp("Error", str);\r
9213   } else {\r
9214     DisplayMessage(str, "");\r
9215     moveErrorMessageUp = TRUE;\r
9216   }\r
9217 }\r
9218 \r
9219 VOID\r
9220 DisplayFatalError(char *str, int error, int exitStatus)\r
9221 {\r
9222   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9223   int len;\r
9224   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9225 \r
9226   if (error != 0) {\r
9227     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9228                         NULL, error, LANG_NEUTRAL,\r
9229                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9230     if (len > 0) {\r
9231       sprintf(buf, "%s:\n%s", str, buf2);\r
9232     } else {\r
9233       ErrorMap *em = errmap;\r
9234       while (em->err != 0 && em->err != error) em++;\r
9235       if (em->err != 0) {\r
9236         sprintf(buf, "%s:\n%s", str, em->msg);\r
9237       } else {\r
9238         sprintf(buf, "%s:\nError code %d", str, error);\r
9239       }\r
9240     }\r
9241     str = buf;\r
9242   }\r
9243   if (appData.debugMode) {\r
9244     fprintf(debugFP, "%s: %s\n", label, str);\r
9245   }\r
9246   if (appData.popupExitMessage) {\r
9247     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9248                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9249   }\r
9250   ExitEvent(exitStatus);\r
9251 }\r
9252 \r
9253 \r
9254 VOID\r
9255 DisplayInformation(char *str)\r
9256 {\r
9257   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9258 }\r
9259 \r
9260 \r
9261 VOID\r
9262 DisplayNote(char *str)\r
9263 {\r
9264   ErrorPopUp("Note", str);\r
9265 }\r
9266 \r
9267 \r
9268 typedef struct {\r
9269   char *title, *question, *replyPrefix;\r
9270   ProcRef pr;\r
9271 } QuestionParams;\r
9272 \r
9273 LRESULT CALLBACK\r
9274 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9275 {\r
9276   static QuestionParams *qp;\r
9277   char reply[MSG_SIZ];\r
9278   int len, err;\r
9279 \r
9280   switch (message) {\r
9281   case WM_INITDIALOG:\r
9282     qp = (QuestionParams *) lParam;\r
9283     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9284     SetWindowText(hDlg, qp->title);\r
9285     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9286     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9287     return FALSE;\r
9288 \r
9289   case WM_COMMAND:\r
9290     switch (LOWORD(wParam)) {\r
9291     case IDOK:\r
9292       strcpy(reply, qp->replyPrefix);\r
9293       if (*reply) strcat(reply, " ");\r
9294       len = strlen(reply);\r
9295       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9296       strcat(reply, "\n");\r
9297       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9298       EndDialog(hDlg, TRUE);\r
9299       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9300       return TRUE;\r
9301     case IDCANCEL:\r
9302       EndDialog(hDlg, FALSE);\r
9303       return TRUE;\r
9304     default:\r
9305       break;\r
9306     }\r
9307     break;\r
9308   }\r
9309   return FALSE;\r
9310 }\r
9311 \r
9312 VOID\r
9313 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9314 {\r
9315     QuestionParams qp;\r
9316     FARPROC lpProc;\r
9317     \r
9318     qp.title = title;\r
9319     qp.question = question;\r
9320     qp.replyPrefix = replyPrefix;\r
9321     qp.pr = pr;\r
9322     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9323     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9324       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9325     FreeProcInstance(lpProc);\r
9326 }\r
9327 \r
9328 /* [AS] Pick FRC position */\r
9329 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9330 {\r
9331     static int * lpIndexFRC;\r
9332     BOOL index_is_ok;\r
9333     char buf[16];\r
9334 \r
9335     switch( message )\r
9336     {\r
9337     case WM_INITDIALOG:\r
9338         lpIndexFRC = (int *) lParam;\r
9339 \r
9340         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9341 \r
9342         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9343         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9344         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9345         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9346 \r
9347         break;\r
9348 \r
9349     case WM_COMMAND:\r
9350         switch( LOWORD(wParam) ) {\r
9351         case IDOK:\r
9352             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9353             EndDialog( hDlg, 0 );\r
9354             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9355             return TRUE;\r
9356         case IDCANCEL:\r
9357             EndDialog( hDlg, 1 );   \r
9358             return TRUE;\r
9359         case IDC_NFG_Edit:\r
9360             if( HIWORD(wParam) == EN_CHANGE ) {\r
9361                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9362 \r
9363                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9364             }\r
9365             return TRUE;\r
9366         case IDC_NFG_Random:\r
9367             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9368             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9369             return TRUE;\r
9370         }\r
9371 \r
9372         break;\r
9373     }\r
9374 \r
9375     return FALSE;\r
9376 }\r
9377 \r
9378 int NewGameFRC()\r
9379 {\r
9380     int result;\r
9381     int index = appData.defaultFrcPosition;\r
9382     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9383 \r
9384     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9385 \r
9386     if( result == 0 ) {\r
9387         appData.defaultFrcPosition = index;\r
9388     }\r
9389 \r
9390     return result;\r
9391 }\r
9392 \r
9393 /* [AS] Game list options */\r
9394 typedef struct {\r
9395     char id;\r
9396     char * name;\r
9397 } GLT_Item;\r
9398 \r
9399 static GLT_Item GLT_ItemInfo[] = {\r
9400     { GLT_EVENT,      "Event" },\r
9401     { GLT_SITE,       "Site" },\r
9402     { GLT_DATE,       "Date" },\r
9403     { GLT_ROUND,      "Round" },\r
9404     { GLT_PLAYERS,    "Players" },\r
9405     { GLT_RESULT,     "Result" },\r
9406     { GLT_WHITE_ELO,  "White Rating" },\r
9407     { GLT_BLACK_ELO,  "Black Rating" },\r
9408     { GLT_TIME_CONTROL,"Time Control" },\r
9409     { GLT_VARIANT,    "Variant" },\r
9410     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9411     { 0, 0 }\r
9412 };\r
9413 \r
9414 const char * GLT_FindItem( char id )\r
9415 {\r
9416     const char * result = 0;\r
9417 \r
9418     GLT_Item * list = GLT_ItemInfo;\r
9419 \r
9420     while( list->id != 0 ) {\r
9421         if( list->id == id ) {\r
9422             result = list->name;\r
9423             break;\r
9424         }\r
9425 \r
9426         list++;\r
9427     }\r
9428 \r
9429     return result;\r
9430 }\r
9431 \r
9432 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9433 {\r
9434     const char * name = GLT_FindItem( id );\r
9435 \r
9436     if( name != 0 ) {\r
9437         if( index >= 0 ) {\r
9438             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9439         }\r
9440         else {\r
9441             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9442         }\r
9443     }\r
9444 }\r
9445 \r
9446 void GLT_TagsToList( HWND hDlg, char * tags )\r
9447 {\r
9448     char * pc = tags;\r
9449 \r
9450     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9451 \r
9452     while( *pc ) {\r
9453         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9454         pc++;\r
9455     }\r
9456 \r
9457     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9458 \r
9459     pc = GLT_ALL_TAGS;\r
9460 \r
9461     while( *pc ) {\r
9462         if( strchr( tags, *pc ) == 0 ) {\r
9463             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9464         }\r
9465         pc++;\r
9466     }\r
9467 \r
9468     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9469 }\r
9470 \r
9471 char GLT_ListItemToTag( HWND hDlg, int index )\r
9472 {\r
9473     char result = '\0';\r
9474     char name[128];\r
9475 \r
9476     GLT_Item * list = GLT_ItemInfo;\r
9477 \r
9478     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9479         while( list->id != 0 ) {\r
9480             if( strcmp( list->name, name ) == 0 ) {\r
9481                 result = list->id;\r
9482                 break;\r
9483             }\r
9484 \r
9485             list++;\r
9486         }\r
9487     }\r
9488 \r
9489     return result;\r
9490 }\r
9491 \r
9492 void GLT_MoveSelection( HWND hDlg, int delta )\r
9493 {\r
9494     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9495     int idx2 = idx1 + delta;\r
9496     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9497 \r
9498     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9499         char buf[128];\r
9500 \r
9501         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9502         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9503         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9504         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9505     }\r
9506 }\r
9507 \r
9508 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9509 {\r
9510     static char glt[64];\r
9511     static char * lpUserGLT;\r
9512 \r
9513     switch( message )\r
9514     {\r
9515     case WM_INITDIALOG:\r
9516         lpUserGLT = (char *) lParam;\r
9517         \r
9518         strcpy( glt, lpUserGLT );\r
9519 \r
9520         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9521 \r
9522         /* Initialize list */\r
9523         GLT_TagsToList( hDlg, glt );\r
9524 \r
9525         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9526 \r
9527         break;\r
9528 \r
9529     case WM_COMMAND:\r
9530         switch( LOWORD(wParam) ) {\r
9531         case IDOK:\r
9532             {\r
9533                 char * pc = lpUserGLT;\r
9534                 int idx = 0;\r
9535 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9536                 char id;\r
9537 \r
9538                 do {\r
9539                     id = GLT_ListItemToTag( hDlg, idx );\r
9540 \r
9541                     *pc++ = id;\r
9542                     idx++;\r
9543                 } while( id != '\0' );\r
9544             }\r
9545             EndDialog( hDlg, 0 );\r
9546             return TRUE;\r
9547         case IDCANCEL:\r
9548             EndDialog( hDlg, 1 );\r
9549             return TRUE;\r
9550 \r
9551         case IDC_GLT_Default:\r
9552             strcpy( glt, GLT_DEFAULT_TAGS );\r
9553             GLT_TagsToList( hDlg, glt );\r
9554             return TRUE;\r
9555 \r
9556         case IDC_GLT_Restore:\r
9557             strcpy( glt, lpUserGLT );\r
9558             GLT_TagsToList( hDlg, glt );\r
9559             return TRUE;\r
9560 \r
9561         case IDC_GLT_Up:\r
9562             GLT_MoveSelection( hDlg, -1 );\r
9563             return TRUE;\r
9564 \r
9565         case IDC_GLT_Down:\r
9566             GLT_MoveSelection( hDlg, +1 );\r
9567             return TRUE;\r
9568         }\r
9569 \r
9570         break;\r
9571     }\r
9572 \r
9573     return FALSE;\r
9574 }\r
9575 \r
9576 int GameListOptions()\r
9577 {\r
9578     char glt[64];\r
9579     int result;\r
9580     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9581 \r
9582     strcpy( glt, appData.gameListTags );\r
9583 \r
9584     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9585 \r
9586     if( result == 0 ) {\r
9587         /* [AS] Memory leak here! */\r
9588         appData.gameListTags = strdup( glt ); \r
9589     }\r
9590 \r
9591     return result;\r
9592 }\r
9593 \r
9594 \r
9595 VOID\r
9596 DisplayIcsInteractionTitle(char *str)\r
9597 {\r
9598   char consoleTitle[MSG_SIZ];\r
9599 \r
9600   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9601   SetWindowText(hwndConsole, consoleTitle);\r
9602 }\r
9603 \r
9604 void\r
9605 DrawPosition(int fullRedraw, Board board)\r
9606 {\r
9607   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9608 }\r
9609 \r
9610 \r
9611 VOID\r
9612 ResetFrontEnd()\r
9613 {\r
9614   fromX = fromY = -1;\r
9615   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9616     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9617     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9618     dragInfo.lastpos = dragInfo.pos;\r
9619     dragInfo.start.x = dragInfo.start.y = -1;\r
9620     dragInfo.from = dragInfo.start;\r
9621     ReleaseCapture();\r
9622     DrawPosition(TRUE, NULL);\r
9623   }\r
9624 }\r
9625 \r
9626 \r
9627 VOID\r
9628 CommentPopUp(char *title, char *str)\r
9629 {\r
9630   HWND hwnd = GetActiveWindow();\r
9631   EitherCommentPopUp(0, title, str, FALSE);\r
9632   SetActiveWindow(hwnd);\r
9633 }\r
9634 \r
9635 VOID\r
9636 CommentPopDown(void)\r
9637 {\r
9638   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9639   if (commentDialog) {\r
9640     ShowWindow(commentDialog, SW_HIDE);\r
9641   }\r
9642   commentDialogUp = FALSE;\r
9643 }\r
9644 \r
9645 VOID\r
9646 EditCommentPopUp(int index, char *title, char *str)\r
9647 {\r
9648   EitherCommentPopUp(index, title, str, TRUE);\r
9649 }\r
9650 \r
9651 \r
9652 VOID\r
9653 RingBell()\r
9654 {\r
9655   MyPlaySound(&sounds[(int)SoundMove]);\r
9656 }\r
9657 \r
9658 VOID PlayIcsWinSound()\r
9659 {\r
9660   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9661 }\r
9662 \r
9663 VOID PlayIcsLossSound()\r
9664 {\r
9665   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9666 }\r
9667 \r
9668 VOID PlayIcsDrawSound()\r
9669 {\r
9670   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9671 }\r
9672 \r
9673 VOID PlayIcsUnfinishedSound()\r
9674 {\r
9675   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9676 }\r
9677 \r
9678 VOID\r
9679 PlayAlarmSound()\r
9680 {\r
9681   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9682 }\r
9683 \r
9684 \r
9685 VOID\r
9686 EchoOn()\r
9687 {\r
9688   HWND hInput;\r
9689   consoleEcho = TRUE;\r
9690   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9691   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9692   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9693 }\r
9694 \r
9695 \r
9696 VOID\r
9697 EchoOff()\r
9698 {\r
9699   CHARFORMAT cf;\r
9700   HWND hInput;\r
9701   consoleEcho = FALSE;\r
9702   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9703   /* This works OK: set text and background both to the same color */\r
9704   cf = consoleCF;\r
9705   cf.crTextColor = COLOR_ECHOOFF;\r
9706   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9707   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9708 }\r
9709 \r
9710 /* No Raw()...? */\r
9711 \r
9712 void Colorize(ColorClass cc, int continuation)\r
9713 {\r
9714   currentColorClass = cc;\r
9715   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9716   consoleCF.crTextColor = textAttribs[cc].color;\r
9717   consoleCF.dwEffects = textAttribs[cc].effects;\r
9718   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9719 }\r
9720 \r
9721 char *\r
9722 UserName()\r
9723 {\r
9724   static char buf[MSG_SIZ];\r
9725   DWORD bufsiz = MSG_SIZ;\r
9726 \r
9727   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9728         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9729   }\r
9730   if (!GetUserName(buf, &bufsiz)) {\r
9731     /*DisplayError("Error getting user name", GetLastError());*/\r
9732     strcpy(buf, "User");\r
9733   }\r
9734   return buf;\r
9735 }\r
9736 \r
9737 char *\r
9738 HostName()\r
9739 {\r
9740   static char buf[MSG_SIZ];\r
9741   DWORD bufsiz = MSG_SIZ;\r
9742 \r
9743   if (!GetComputerName(buf, &bufsiz)) {\r
9744     /*DisplayError("Error getting host name", GetLastError());*/\r
9745     strcpy(buf, "Unknown");\r
9746   }\r
9747   return buf;\r
9748 }\r
9749 \r
9750 \r
9751 int\r
9752 ClockTimerRunning()\r
9753 {\r
9754   return clockTimerEvent != 0;\r
9755 }\r
9756 \r
9757 int\r
9758 StopClockTimer()\r
9759 {\r
9760   if (clockTimerEvent == 0) return FALSE;\r
9761   KillTimer(hwndMain, clockTimerEvent);\r
9762   clockTimerEvent = 0;\r
9763   return TRUE;\r
9764 }\r
9765 \r
9766 void\r
9767 StartClockTimer(long millisec)\r
9768 {\r
9769   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9770                              (UINT) millisec, NULL);\r
9771 }\r
9772 \r
9773 void\r
9774 DisplayWhiteClock(long timeRemaining, int highlight)\r
9775 {\r
9776   HDC hdc;\r
9777   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9778 \r
9779   if(appData.noGUI) return;\r
9780   hdc = GetDC(hwndMain);\r
9781   if (!IsIconic(hwndMain)) {\r
9782     DisplayAClock(hdc, timeRemaining, highlight, \r
9783                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9784   }\r
9785   if (highlight && iconCurrent == iconBlack) {\r
9786     iconCurrent = iconWhite;\r
9787     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9788     if (IsIconic(hwndMain)) {\r
9789       DrawIcon(hdc, 2, 2, iconCurrent);\r
9790     }\r
9791   }\r
9792   (void) ReleaseDC(hwndMain, hdc);\r
9793   if (hwndConsole)\r
9794     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9795 }\r
9796 \r
9797 void\r
9798 DisplayBlackClock(long timeRemaining, int highlight)\r
9799 {\r
9800   HDC hdc;\r
9801   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9802 \r
9803   if(appData.noGUI) return;\r
9804   hdc = GetDC(hwndMain);\r
9805   if (!IsIconic(hwndMain)) {\r
9806     DisplayAClock(hdc, timeRemaining, highlight, \r
9807                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9808   }\r
9809   if (highlight && iconCurrent == iconWhite) {\r
9810     iconCurrent = iconBlack;\r
9811     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9812     if (IsIconic(hwndMain)) {\r
9813       DrawIcon(hdc, 2, 2, iconCurrent);\r
9814     }\r
9815   }\r
9816   (void) ReleaseDC(hwndMain, hdc);\r
9817   if (hwndConsole)\r
9818     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9819 }\r
9820 \r
9821 \r
9822 int\r
9823 LoadGameTimerRunning()\r
9824 {\r
9825   return loadGameTimerEvent != 0;\r
9826 }\r
9827 \r
9828 int\r
9829 StopLoadGameTimer()\r
9830 {\r
9831   if (loadGameTimerEvent == 0) return FALSE;\r
9832   KillTimer(hwndMain, loadGameTimerEvent);\r
9833   loadGameTimerEvent = 0;\r
9834   return TRUE;\r
9835 }\r
9836 \r
9837 void\r
9838 StartLoadGameTimer(long millisec)\r
9839 {\r
9840   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9841                                 (UINT) millisec, NULL);\r
9842 }\r
9843 \r
9844 void\r
9845 AutoSaveGame()\r
9846 {\r
9847   char *defName;\r
9848   FILE *f;\r
9849   char fileTitle[MSG_SIZ];\r
9850 \r
9851   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9852   f = OpenFileDialog(hwndMain, "a", defName,\r
9853                      appData.oldSaveStyle ? "gam" : "pgn",\r
9854                      GAME_FILT, \r
9855                      "Save Game to File", NULL, fileTitle, NULL);\r
9856   if (f != NULL) {\r
9857     SaveGame(f, 0, "");\r
9858     fclose(f);\r
9859   }\r
9860 }\r
9861 \r
9862 \r
9863 void\r
9864 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9865 {\r
9866   if (delayedTimerEvent != 0) {\r
9867     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9868       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9869     }\r
9870     KillTimer(hwndMain, delayedTimerEvent);\r
9871     delayedTimerEvent = 0;\r
9872     delayedTimerCallback();\r
9873   }\r
9874   delayedTimerCallback = cb;\r
9875   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9876                                 (UINT) millisec, NULL);\r
9877 }\r
9878 \r
9879 DelayedEventCallback\r
9880 GetDelayedEvent()\r
9881 {\r
9882   if (delayedTimerEvent) {\r
9883     return delayedTimerCallback;\r
9884   } else {\r
9885     return NULL;\r
9886   }\r
9887 }\r
9888 \r
9889 void\r
9890 CancelDelayedEvent()\r
9891 {\r
9892   if (delayedTimerEvent) {\r
9893     KillTimer(hwndMain, delayedTimerEvent);\r
9894     delayedTimerEvent = 0;\r
9895   }\r
9896 }\r
9897 \r
9898 DWORD GetWin32Priority(int nice)\r
9899 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9900 /*\r
9901 REALTIME_PRIORITY_CLASS     0x00000100\r
9902 HIGH_PRIORITY_CLASS         0x00000080\r
9903 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9904 NORMAL_PRIORITY_CLASS       0x00000020\r
9905 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9906 IDLE_PRIORITY_CLASS         0x00000040\r
9907 */\r
9908         if (nice < -15) return 0x00000080;\r
9909         if (nice < 0)   return 0x00008000;\r
9910         if (nice == 0)  return 0x00000020;\r
9911         if (nice < 15)  return 0x00004000;\r
9912         return 0x00000040;\r
9913 }\r
9914 \r
9915 /* Start a child process running the given program.\r
9916    The process's standard output can be read from "from", and its\r
9917    standard input can be written to "to".\r
9918    Exit with fatal error if anything goes wrong.\r
9919    Returns an opaque pointer that can be used to destroy the process\r
9920    later.\r
9921 */\r
9922 int\r
9923 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9924 {\r
9925 #define BUFSIZE 4096\r
9926 \r
9927   HANDLE hChildStdinRd, hChildStdinWr,\r
9928     hChildStdoutRd, hChildStdoutWr;\r
9929   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9930   SECURITY_ATTRIBUTES saAttr;\r
9931   BOOL fSuccess;\r
9932   PROCESS_INFORMATION piProcInfo;\r
9933   STARTUPINFO siStartInfo;\r
9934   ChildProc *cp;\r
9935   char buf[MSG_SIZ];\r
9936   DWORD err;\r
9937 \r
9938   if (appData.debugMode) {\r
9939     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9940   }\r
9941 \r
9942   *pr = NoProc;\r
9943 \r
9944   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9945   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9946   saAttr.bInheritHandle = TRUE;\r
9947   saAttr.lpSecurityDescriptor = NULL;\r
9948 \r
9949   /*\r
9950    * The steps for redirecting child's STDOUT:\r
9951    *     1. Create anonymous pipe to be STDOUT for child.\r
9952    *     2. Create a noninheritable duplicate of read handle,\r
9953    *         and close the inheritable read handle.\r
9954    */\r
9955 \r
9956   /* Create a pipe for the child's STDOUT. */\r
9957   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9958     return GetLastError();\r
9959   }\r
9960 \r
9961   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9962   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9963                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9964                              FALSE,     /* not inherited */\r
9965                              DUPLICATE_SAME_ACCESS);\r
9966   if (! fSuccess) {\r
9967     return GetLastError();\r
9968   }\r
9969   CloseHandle(hChildStdoutRd);\r
9970 \r
9971   /*\r
9972    * The steps for redirecting child's STDIN:\r
9973    *     1. Create anonymous pipe to be STDIN for child.\r
9974    *     2. Create a noninheritable duplicate of write handle,\r
9975    *         and close the inheritable write handle.\r
9976    */\r
9977 \r
9978   /* Create a pipe for the child's STDIN. */\r
9979   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9980     return GetLastError();\r
9981   }\r
9982 \r
9983   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9984   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9985                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9986                              FALSE,     /* not inherited */\r
9987                              DUPLICATE_SAME_ACCESS);\r
9988   if (! fSuccess) {\r
9989     return GetLastError();\r
9990   }\r
9991   CloseHandle(hChildStdinWr);\r
9992 \r
9993   /* Arrange to (1) look in dir for the child .exe file, and\r
9994    * (2) have dir be the child's working directory.  Interpret\r
9995    * dir relative to the directory WinBoard loaded from. */\r
9996   GetCurrentDirectory(MSG_SIZ, buf);\r
9997   SetCurrentDirectory(installDir);\r
9998   SetCurrentDirectory(dir);\r
9999 \r
10000   /* Now create the child process. */\r
10001 \r
10002   siStartInfo.cb = sizeof(STARTUPINFO);\r
10003   siStartInfo.lpReserved = NULL;\r
10004   siStartInfo.lpDesktop = NULL;\r
10005   siStartInfo.lpTitle = NULL;\r
10006   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
10007   siStartInfo.cbReserved2 = 0;\r
10008   siStartInfo.lpReserved2 = NULL;\r
10009   siStartInfo.hStdInput = hChildStdinRd;\r
10010   siStartInfo.hStdOutput = hChildStdoutWr;\r
10011   siStartInfo.hStdError = hChildStdoutWr;\r
10012 \r
10013   fSuccess = CreateProcess(NULL,\r
10014                            cmdLine,        /* command line */\r
10015                            NULL,           /* process security attributes */\r
10016                            NULL,           /* primary thread security attrs */\r
10017                            TRUE,           /* handles are inherited */\r
10018                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10019                            NULL,           /* use parent's environment */\r
10020                            NULL,\r
10021                            &siStartInfo, /* STARTUPINFO pointer */\r
10022                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10023 \r
10024   err = GetLastError();\r
10025   SetCurrentDirectory(buf); /* return to prev directory */\r
10026   if (! fSuccess) {\r
10027     return err;\r
10028   }\r
10029 \r
10030   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10031     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10032     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10033   }\r
10034 \r
10035   /* Close the handles we don't need in the parent */\r
10036   CloseHandle(piProcInfo.hThread);\r
10037   CloseHandle(hChildStdinRd);\r
10038   CloseHandle(hChildStdoutWr);\r
10039 \r
10040   /* Prepare return value */\r
10041   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10042   cp->kind = CPReal;\r
10043   cp->hProcess = piProcInfo.hProcess;\r
10044   cp->pid = piProcInfo.dwProcessId;\r
10045   cp->hFrom = hChildStdoutRdDup;\r
10046   cp->hTo = hChildStdinWrDup;\r
10047 \r
10048   *pr = (void *) cp;\r
10049 \r
10050   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10051      2000 where engines sometimes don't see the initial command(s)\r
10052      from WinBoard and hang.  I don't understand how that can happen,\r
10053      but the Sleep is harmless, so I've put it in.  Others have also\r
10054      reported what may be the same problem, so hopefully this will fix\r
10055      it for them too.  */\r
10056   Sleep(500);\r
10057 \r
10058   return NO_ERROR;\r
10059 }\r
10060 \r
10061 \r
10062 void\r
10063 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10064 {\r
10065   ChildProc *cp; int result;\r
10066 \r
10067   cp = (ChildProc *) pr;\r
10068   if (cp == NULL) return;\r
10069 \r
10070   switch (cp->kind) {\r
10071   case CPReal:\r
10072     /* TerminateProcess is considered harmful, so... */\r
10073     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10074     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10075     /* The following doesn't work because the chess program\r
10076        doesn't "have the same console" as WinBoard.  Maybe\r
10077        we could arrange for this even though neither WinBoard\r
10078        nor the chess program uses a console for stdio? */\r
10079     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10080 \r
10081     /* [AS] Special termination modes for misbehaving programs... */\r
10082     if( signal == 9 ) { \r
10083         result = TerminateProcess( cp->hProcess, 0 );\r
10084 \r
10085         if ( appData.debugMode) {\r
10086             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10087         }\r
10088     }\r
10089     else if( signal == 10 ) {\r
10090         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10091 \r
10092         if( dw != WAIT_OBJECT_0 ) {\r
10093             result = TerminateProcess( cp->hProcess, 0 );\r
10094 \r
10095             if ( appData.debugMode) {\r
10096                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10097             }\r
10098 \r
10099         }\r
10100     }\r
10101 \r
10102     CloseHandle(cp->hProcess);\r
10103     break;\r
10104 \r
10105   case CPComm:\r
10106     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10107     break;\r
10108 \r
10109   case CPSock:\r
10110     closesocket(cp->sock);\r
10111     WSACleanup();\r
10112     break;\r
10113 \r
10114   case CPRcmd:\r
10115     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10116     closesocket(cp->sock);\r
10117     closesocket(cp->sock2);\r
10118     WSACleanup();\r
10119     break;\r
10120   }\r
10121   free(cp);\r
10122 }\r
10123 \r
10124 void\r
10125 InterruptChildProcess(ProcRef pr)\r
10126 {\r
10127   ChildProc *cp;\r
10128 \r
10129   cp = (ChildProc *) pr;\r
10130   if (cp == NULL) return;\r
10131   switch (cp->kind) {\r
10132   case CPReal:\r
10133     /* The following doesn't work because the chess program\r
10134        doesn't "have the same console" as WinBoard.  Maybe\r
10135        we could arrange for this even though neither WinBoard\r
10136        nor the chess program uses a console for stdio */\r
10137     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10138     break;\r
10139 \r
10140   case CPComm:\r
10141   case CPSock:\r
10142     /* Can't interrupt */\r
10143     break;\r
10144 \r
10145   case CPRcmd:\r
10146     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10147     break;\r
10148   }\r
10149 }\r
10150 \r
10151 \r
10152 int\r
10153 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10154 {\r
10155   char cmdLine[MSG_SIZ];\r
10156 \r
10157   if (port[0] == NULLCHAR) {\r
10158     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10159   } else {\r
10160     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10161   }\r
10162   return StartChildProcess(cmdLine, "", pr);\r
10163 }\r
10164 \r
10165 \r
10166 /* Code to open TCP sockets */\r
10167 \r
10168 int\r
10169 OpenTCP(char *host, char *port, ProcRef *pr)\r
10170 {\r
10171   ChildProc *cp;\r
10172   int err;\r
10173   SOCKET s;\r
10174   struct sockaddr_in sa, mysa;\r
10175   struct hostent FAR *hp;\r
10176   unsigned short uport;\r
10177   WORD wVersionRequested;\r
10178   WSADATA wsaData;\r
10179 \r
10180   /* Initialize socket DLL */\r
10181   wVersionRequested = MAKEWORD(1, 1);\r
10182   err = WSAStartup(wVersionRequested, &wsaData);\r
10183   if (err != 0) return err;\r
10184 \r
10185   /* Make socket */\r
10186   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10187     err = WSAGetLastError();\r
10188     WSACleanup();\r
10189     return err;\r
10190   }\r
10191 \r
10192   /* Bind local address using (mostly) don't-care values.\r
10193    */\r
10194   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10195   mysa.sin_family = AF_INET;\r
10196   mysa.sin_addr.s_addr = INADDR_ANY;\r
10197   uport = (unsigned short) 0;\r
10198   mysa.sin_port = htons(uport);\r
10199   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10200       == SOCKET_ERROR) {\r
10201     err = WSAGetLastError();\r
10202     WSACleanup();\r
10203     return err;\r
10204   }\r
10205 \r
10206   /* Resolve remote host name */\r
10207   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10208   if (!(hp = gethostbyname(host))) {\r
10209     unsigned int b0, b1, b2, b3;\r
10210 \r
10211     err = WSAGetLastError();\r
10212 \r
10213     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10214       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10215       hp->h_addrtype = AF_INET;\r
10216       hp->h_length = 4;\r
10217       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10218       hp->h_addr_list[0] = (char *) malloc(4);\r
10219       hp->h_addr_list[0][0] = (char) b0;\r
10220       hp->h_addr_list[0][1] = (char) b1;\r
10221       hp->h_addr_list[0][2] = (char) b2;\r
10222       hp->h_addr_list[0][3] = (char) b3;\r
10223     } else {\r
10224       WSACleanup();\r
10225       return err;\r
10226     }\r
10227   }\r
10228   sa.sin_family = hp->h_addrtype;\r
10229   uport = (unsigned short) atoi(port);\r
10230   sa.sin_port = htons(uport);\r
10231   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10232 \r
10233   /* Make connection */\r
10234   if (connect(s, (struct sockaddr *) &sa,\r
10235               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10236     err = WSAGetLastError();\r
10237     WSACleanup();\r
10238     return err;\r
10239   }\r
10240 \r
10241   /* Prepare return value */\r
10242   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10243   cp->kind = CPSock;\r
10244   cp->sock = s;\r
10245   *pr = (ProcRef *) cp;\r
10246 \r
10247   return NO_ERROR;\r
10248 }\r
10249 \r
10250 int\r
10251 OpenCommPort(char *name, ProcRef *pr)\r
10252 {\r
10253   HANDLE h;\r
10254   COMMTIMEOUTS ct;\r
10255   ChildProc *cp;\r
10256   char fullname[MSG_SIZ];\r
10257 \r
10258   if (*name != '\\')\r
10259     sprintf(fullname, "\\\\.\\%s", name);\r
10260   else\r
10261     strcpy(fullname, name);\r
10262 \r
10263   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10264                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10265   if (h == (HANDLE) -1) {\r
10266     return GetLastError();\r
10267   }\r
10268   hCommPort = h;\r
10269 \r
10270   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10271 \r
10272   /* Accumulate characters until a 100ms pause, then parse */\r
10273   ct.ReadIntervalTimeout = 100;\r
10274   ct.ReadTotalTimeoutMultiplier = 0;\r
10275   ct.ReadTotalTimeoutConstant = 0;\r
10276   ct.WriteTotalTimeoutMultiplier = 0;\r
10277   ct.WriteTotalTimeoutConstant = 0;\r
10278   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10279 \r
10280   /* Prepare return value */\r
10281   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10282   cp->kind = CPComm;\r
10283   cp->hFrom = h;\r
10284   cp->hTo = h;\r
10285   *pr = (ProcRef *) cp;\r
10286 \r
10287   return NO_ERROR;\r
10288 }\r
10289 \r
10290 int\r
10291 OpenLoopback(ProcRef *pr)\r
10292 {\r
10293   DisplayFatalError("Not implemented", 0, 1);\r
10294   return NO_ERROR;\r
10295 }\r
10296 \r
10297 \r
10298 int\r
10299 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10300 {\r
10301   ChildProc *cp;\r
10302   int err;\r
10303   SOCKET s, s2, s3;\r
10304   struct sockaddr_in sa, mysa;\r
10305   struct hostent FAR *hp;\r
10306   unsigned short uport;\r
10307   WORD wVersionRequested;\r
10308   WSADATA wsaData;\r
10309   int fromPort;\r
10310   char stderrPortStr[MSG_SIZ];\r
10311 \r
10312   /* Initialize socket DLL */\r
10313   wVersionRequested = MAKEWORD(1, 1);\r
10314   err = WSAStartup(wVersionRequested, &wsaData);\r
10315   if (err != 0) return err;\r
10316 \r
10317   /* Resolve remote host name */\r
10318   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10319   if (!(hp = gethostbyname(host))) {\r
10320     unsigned int b0, b1, b2, b3;\r
10321 \r
10322     err = WSAGetLastError();\r
10323 \r
10324     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10325       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10326       hp->h_addrtype = AF_INET;\r
10327       hp->h_length = 4;\r
10328       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10329       hp->h_addr_list[0] = (char *) malloc(4);\r
10330       hp->h_addr_list[0][0] = (char) b0;\r
10331       hp->h_addr_list[0][1] = (char) b1;\r
10332       hp->h_addr_list[0][2] = (char) b2;\r
10333       hp->h_addr_list[0][3] = (char) b3;\r
10334     } else {\r
10335       WSACleanup();\r
10336       return err;\r
10337     }\r
10338   }\r
10339   sa.sin_family = hp->h_addrtype;\r
10340   uport = (unsigned short) 514;\r
10341   sa.sin_port = htons(uport);\r
10342   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10343 \r
10344   /* Bind local socket to unused "privileged" port address\r
10345    */\r
10346   s = INVALID_SOCKET;\r
10347   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10348   mysa.sin_family = AF_INET;\r
10349   mysa.sin_addr.s_addr = INADDR_ANY;\r
10350   for (fromPort = 1023;; fromPort--) {\r
10351     if (fromPort < 0) {\r
10352       WSACleanup();\r
10353       return WSAEADDRINUSE;\r
10354     }\r
10355     if (s == INVALID_SOCKET) {\r
10356       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10357         err = WSAGetLastError();\r
10358         WSACleanup();\r
10359         return err;\r
10360       }\r
10361     }\r
10362     uport = (unsigned short) fromPort;\r
10363     mysa.sin_port = htons(uport);\r
10364     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10365         == SOCKET_ERROR) {\r
10366       err = WSAGetLastError();\r
10367       if (err == WSAEADDRINUSE) continue;\r
10368       WSACleanup();\r
10369       return err;\r
10370     }\r
10371     if (connect(s, (struct sockaddr *) &sa,\r
10372       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10373       err = WSAGetLastError();\r
10374       if (err == WSAEADDRINUSE) {\r
10375         closesocket(s);\r
10376         s = -1;\r
10377         continue;\r
10378       }\r
10379       WSACleanup();\r
10380       return err;\r
10381     }\r
10382     break;\r
10383   }\r
10384 \r
10385   /* Bind stderr local socket to unused "privileged" port address\r
10386    */\r
10387   s2 = INVALID_SOCKET;\r
10388   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10389   mysa.sin_family = AF_INET;\r
10390   mysa.sin_addr.s_addr = INADDR_ANY;\r
10391   for (fromPort = 1023;; fromPort--) {\r
10392     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10393     if (fromPort < 0) {\r
10394       (void) closesocket(s);\r
10395       WSACleanup();\r
10396       return WSAEADDRINUSE;\r
10397     }\r
10398     if (s2 == INVALID_SOCKET) {\r
10399       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10400         err = WSAGetLastError();\r
10401         closesocket(s);\r
10402         WSACleanup();\r
10403         return err;\r
10404       }\r
10405     }\r
10406     uport = (unsigned short) fromPort;\r
10407     mysa.sin_port = htons(uport);\r
10408     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10409         == SOCKET_ERROR) {\r
10410       err = WSAGetLastError();\r
10411       if (err == WSAEADDRINUSE) continue;\r
10412       (void) closesocket(s);\r
10413       WSACleanup();\r
10414       return err;\r
10415     }\r
10416     if (listen(s2, 1) == SOCKET_ERROR) {\r
10417       err = WSAGetLastError();\r
10418       if (err == WSAEADDRINUSE) {\r
10419         closesocket(s2);\r
10420         s2 = INVALID_SOCKET;\r
10421         continue;\r
10422       }\r
10423       (void) closesocket(s);\r
10424       (void) closesocket(s2);\r
10425       WSACleanup();\r
10426       return err;\r
10427     }\r
10428     break;\r
10429   }\r
10430   prevStderrPort = fromPort; // remember port used\r
10431   sprintf(stderrPortStr, "%d", fromPort);\r
10432 \r
10433   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10434     err = WSAGetLastError();\r
10435     (void) closesocket(s);\r
10436     (void) closesocket(s2);\r
10437     WSACleanup();\r
10438     return err;\r
10439   }\r
10440 \r
10441   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10442     err = WSAGetLastError();\r
10443     (void) closesocket(s);\r
10444     (void) closesocket(s2);\r
10445     WSACleanup();\r
10446     return err;\r
10447   }\r
10448   if (*user == NULLCHAR) user = UserName();\r
10449   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10450     err = WSAGetLastError();\r
10451     (void) closesocket(s);\r
10452     (void) closesocket(s2);\r
10453     WSACleanup();\r
10454     return err;\r
10455   }\r
10456   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10457     err = WSAGetLastError();\r
10458     (void) closesocket(s);\r
10459     (void) closesocket(s2);\r
10460     WSACleanup();\r
10461     return err;\r
10462   }\r
10463 \r
10464   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10465     err = WSAGetLastError();\r
10466     (void) closesocket(s);\r
10467     (void) closesocket(s2);\r
10468     WSACleanup();\r
10469     return err;\r
10470   }\r
10471   (void) closesocket(s2);  /* Stop listening */\r
10472 \r
10473   /* Prepare return value */\r
10474   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10475   cp->kind = CPRcmd;\r
10476   cp->sock = s;\r
10477   cp->sock2 = s3;\r
10478   *pr = (ProcRef *) cp;\r
10479 \r
10480   return NO_ERROR;\r
10481 }\r
10482 \r
10483 \r
10484 InputSourceRef\r
10485 AddInputSource(ProcRef pr, int lineByLine,\r
10486                InputCallback func, VOIDSTAR closure)\r
10487 {\r
10488   InputSource *is, *is2 = NULL;\r
10489   ChildProc *cp = (ChildProc *) pr;\r
10490 \r
10491   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10492   is->lineByLine = lineByLine;\r
10493   is->func = func;\r
10494   is->closure = closure;\r
10495   is->second = NULL;\r
10496   is->next = is->buf;\r
10497   if (pr == NoProc) {\r
10498     is->kind = CPReal;\r
10499     consoleInputSource = is;\r
10500   } else {\r
10501     is->kind = cp->kind;\r
10502     /* \r
10503         [AS] Try to avoid a race condition if the thread is given control too early:\r
10504         we create all threads suspended so that the is->hThread variable can be\r
10505         safely assigned, then let the threads start with ResumeThread.\r
10506     */\r
10507     switch (cp->kind) {\r
10508     case CPReal:\r
10509       is->hFile = cp->hFrom;\r
10510       cp->hFrom = NULL; /* now owned by InputThread */\r
10511       is->hThread =\r
10512         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10513                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10514       break;\r
10515 \r
10516     case CPComm:\r
10517       is->hFile = cp->hFrom;\r
10518       cp->hFrom = NULL; /* now owned by InputThread */\r
10519       is->hThread =\r
10520         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10521                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10522       break;\r
10523 \r
10524     case CPSock:\r
10525       is->sock = cp->sock;\r
10526       is->hThread =\r
10527         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10528                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10529       break;\r
10530 \r
10531     case CPRcmd:\r
10532       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10533       *is2 = *is;\r
10534       is->sock = cp->sock;\r
10535       is->second = is2;\r
10536       is2->sock = cp->sock2;\r
10537       is2->second = is2;\r
10538       is->hThread =\r
10539         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10540                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10541       is2->hThread =\r
10542         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10543                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10544       break;\r
10545     }\r
10546 \r
10547     if( is->hThread != NULL ) {\r
10548         ResumeThread( is->hThread );\r
10549     }\r
10550 \r
10551     if( is2 != NULL && is2->hThread != NULL ) {\r
10552         ResumeThread( is2->hThread );\r
10553     }\r
10554   }\r
10555 \r
10556   return (InputSourceRef) is;\r
10557 }\r
10558 \r
10559 void\r
10560 RemoveInputSource(InputSourceRef isr)\r
10561 {\r
10562   InputSource *is;\r
10563 \r
10564   is = (InputSource *) isr;\r
10565   is->hThread = NULL;  /* tell thread to stop */\r
10566   CloseHandle(is->hThread);\r
10567   if (is->second != NULL) {\r
10568     is->second->hThread = NULL;\r
10569     CloseHandle(is->second->hThread);\r
10570   }\r
10571 }\r
10572 \r
10573 \r
10574 int\r
10575 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10576 {\r
10577   DWORD dOutCount;\r
10578   int outCount = SOCKET_ERROR;\r
10579   ChildProc *cp = (ChildProc *) pr;\r
10580   static OVERLAPPED ovl;\r
10581 \r
10582   if (pr == NoProc) {\r
10583     ConsoleOutput(message, count, FALSE);\r
10584     return count;\r
10585   } \r
10586 \r
10587   if (ovl.hEvent == NULL) {\r
10588     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10589   }\r
10590   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10591 \r
10592   switch (cp->kind) {\r
10593   case CPSock:\r
10594   case CPRcmd:\r
10595     outCount = send(cp->sock, message, count, 0);\r
10596     if (outCount == SOCKET_ERROR) {\r
10597       *outError = WSAGetLastError();\r
10598     } else {\r
10599       *outError = NO_ERROR;\r
10600     }\r
10601     break;\r
10602 \r
10603   case CPReal:\r
10604     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10605                   &dOutCount, NULL)) {\r
10606       *outError = NO_ERROR;\r
10607       outCount = (int) dOutCount;\r
10608     } else {\r
10609       *outError = GetLastError();\r
10610     }\r
10611     break;\r
10612 \r
10613   case CPComm:\r
10614     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10615                             &dOutCount, &ovl);\r
10616     if (*outError == NO_ERROR) {\r
10617       outCount = (int) dOutCount;\r
10618     }\r
10619     break;\r
10620   }\r
10621   return outCount;\r
10622 }\r
10623 \r
10624 int\r
10625 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10626                        long msdelay)\r
10627 {\r
10628   /* Ignore delay, not implemented for WinBoard */\r
10629   return OutputToProcess(pr, message, count, outError);\r
10630 }\r
10631 \r
10632 \r
10633 void\r
10634 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10635                         char *buf, int count, int error)\r
10636 {\r
10637   DisplayFatalError("Not implemented", 0, 1);\r
10638 }\r
10639 \r
10640 /* see wgamelist.c for Game List functions */\r
10641 /* see wedittags.c for Edit Tags functions */\r
10642 \r
10643 \r
10644 VOID\r
10645 ICSInitScript()\r
10646 {\r
10647   FILE *f;\r
10648   char buf[MSG_SIZ];\r
10649   char *dummy;\r
10650 \r
10651   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10652     f = fopen(buf, "r");\r
10653     if (f != NULL) {\r
10654       ProcessICSInitScript(f);\r
10655       fclose(f);\r
10656     }\r
10657   }\r
10658 }\r
10659 \r
10660 \r
10661 VOID\r
10662 StartAnalysisClock()\r
10663 {\r
10664   if (analysisTimerEvent) return;\r
10665   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10666                                         (UINT) 2000, NULL);\r
10667 }\r
10668 \r
10669 LRESULT CALLBACK\r
10670 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10671 {\r
10672   static HANDLE hwndText;\r
10673   RECT rect;\r
10674   static int sizeX, sizeY;\r
10675   int newSizeX, newSizeY, flags;\r
10676   MINMAXINFO *mmi;\r
10677 \r
10678   switch (message) {\r
10679   case WM_INITDIALOG: /* message: initialize dialog box */\r
10680     /* Initialize the dialog items */\r
10681     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10682     SetWindowText(hDlg, analysisTitle);\r
10683     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10684     /* Size and position the dialog */\r
10685     if (!analysisDialog) {\r
10686       analysisDialog = hDlg;\r
10687       flags = SWP_NOZORDER;\r
10688       GetClientRect(hDlg, &rect);\r
10689       sizeX = rect.right;\r
10690       sizeY = rect.bottom;\r
10691       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10692           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10693         WINDOWPLACEMENT wp;\r
10694         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10695         wp.length = sizeof(WINDOWPLACEMENT);\r
10696         wp.flags = 0;\r
10697         wp.showCmd = SW_SHOW;\r
10698         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10699         wp.rcNormalPosition.left = analysisX;\r
10700         wp.rcNormalPosition.right = analysisX + analysisW;\r
10701         wp.rcNormalPosition.top = analysisY;\r
10702         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10703         SetWindowPlacement(hDlg, &wp);\r
10704 \r
10705         GetClientRect(hDlg, &rect);\r
10706         newSizeX = rect.right;\r
10707         newSizeY = rect.bottom;\r
10708         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10709                               newSizeX, newSizeY);\r
10710         sizeX = newSizeX;\r
10711         sizeY = newSizeY;\r
10712       }\r
10713     }\r
10714     return FALSE;\r
10715 \r
10716   case WM_COMMAND: /* message: received a command */\r
10717     switch (LOWORD(wParam)) {\r
10718     case IDCANCEL:\r
10719       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10720           ExitAnalyzeMode();\r
10721           ModeHighlight();\r
10722           return TRUE;\r
10723       }\r
10724       EditGameEvent();\r
10725       return TRUE;\r
10726     default:\r
10727       break;\r
10728     }\r
10729     break;\r
10730 \r
10731   case WM_SIZE:\r
10732     newSizeX = LOWORD(lParam);\r
10733     newSizeY = HIWORD(lParam);\r
10734     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10735     sizeX = newSizeX;\r
10736     sizeY = newSizeY;\r
10737     break;\r
10738 \r
10739   case WM_GETMINMAXINFO:\r
10740     /* Prevent resizing window too small */\r
10741     mmi = (MINMAXINFO *) lParam;\r
10742     mmi->ptMinTrackSize.x = 100;\r
10743     mmi->ptMinTrackSize.y = 100;\r
10744     break;\r
10745   }\r
10746   return FALSE;\r
10747 }\r
10748 \r
10749 VOID\r
10750 AnalysisPopUp(char* title, char* str)\r
10751 {\r
10752   FARPROC lpProc;\r
10753   char *p, *q;\r
10754 \r
10755   /* [AS] */\r
10756   EngineOutputPopUp();\r
10757   return;\r
10758 \r
10759   if (str == NULL) str = "";\r
10760   p = (char *) malloc(2 * strlen(str) + 2);\r
10761   q = p;\r
10762   while (*str) {\r
10763     if (*str == '\n') *q++ = '\r';\r
10764     *q++ = *str++;\r
10765   }\r
10766   *q = NULLCHAR;\r
10767   if (analysisText != NULL) free(analysisText);\r
10768   analysisText = p;\r
10769 \r
10770   if (analysisDialog) {\r
10771     SetWindowText(analysisDialog, title);\r
10772     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10773     ShowWindow(analysisDialog, SW_SHOW);\r
10774   } else {\r
10775     analysisTitle = title;\r
10776     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10777     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10778                  hwndMain, (DLGPROC)lpProc);\r
10779     FreeProcInstance(lpProc);\r
10780   }\r
10781   analysisDialogUp = TRUE;  \r
10782 }\r
10783 \r
10784 VOID\r
10785 AnalysisPopDown()\r
10786 {\r
10787   if (analysisDialog) {\r
10788     ShowWindow(analysisDialog, SW_HIDE);\r
10789   }\r
10790   analysisDialogUp = FALSE;  \r
10791 }\r
10792 \r
10793 \r
10794 VOID\r
10795 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10796 {\r
10797   highlightInfo.sq[0].x = fromX;\r
10798   highlightInfo.sq[0].y = fromY;\r
10799   highlightInfo.sq[1].x = toX;\r
10800   highlightInfo.sq[1].y = toY;\r
10801 }\r
10802 \r
10803 VOID\r
10804 ClearHighlights()\r
10805 {\r
10806   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10807     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10808 }\r
10809 \r
10810 VOID\r
10811 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10812 {\r
10813   premoveHighlightInfo.sq[0].x = fromX;\r
10814   premoveHighlightInfo.sq[0].y = fromY;\r
10815   premoveHighlightInfo.sq[1].x = toX;\r
10816   premoveHighlightInfo.sq[1].y = toY;\r
10817 }\r
10818 \r
10819 VOID\r
10820 ClearPremoveHighlights()\r
10821 {\r
10822   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10823     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10824 }\r
10825 \r
10826 VOID\r
10827 ShutDownFrontEnd()\r
10828 {\r
10829   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10830   DeleteClipboardTempFiles();\r
10831 }\r
10832 \r
10833 void\r
10834 BoardToTop()\r
10835 {\r
10836     if (IsIconic(hwndMain))\r
10837       ShowWindow(hwndMain, SW_RESTORE);\r
10838 \r
10839     SetActiveWindow(hwndMain);\r
10840 }\r
10841 \r
10842 /*\r
10843  * Prototypes for animation support routines\r
10844  */\r
10845 static void ScreenSquare(int column, int row, POINT * pt);\r
10846 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10847      POINT frames[], int * nFrames);\r
10848 \r
10849 \r
10850 void\r
10851 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10852 {       // [HGM] atomic: animate blast wave\r
10853         int i;\r
10854 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10855         explodeInfo.fromX = fromX;\r
10856         explodeInfo.fromY = fromY;\r
10857         explodeInfo.toX = toX;\r
10858         explodeInfo.toY = toY;\r
10859         for(i=1; i<nFrames; i++) {\r
10860             explodeInfo.radius = (i*180)/(nFrames-1);\r
10861             DrawPosition(FALSE, NULL);\r
10862             Sleep(appData.animSpeed);\r
10863         }\r
10864         explodeInfo.radius = 0;\r
10865         DrawPosition(TRUE, NULL);\r
10866 }\r
10867 \r
10868 #define kFactor 4\r
10869 \r
10870 void\r
10871 AnimateMove(board, fromX, fromY, toX, toY)\r
10872      Board board;\r
10873      int fromX;\r
10874      int fromY;\r
10875      int toX;\r
10876      int toY;\r
10877 {\r
10878   ChessSquare piece;\r
10879   POINT start, finish, mid;\r
10880   POINT frames[kFactor * 2 + 1];\r
10881   int nFrames, n;\r
10882 \r
10883   if (!appData.animate) return;\r
10884   if (doingSizing) return;\r
10885   if (fromY < 0 || fromX < 0) return;\r
10886   piece = board[fromY][fromX];\r
10887   if (piece >= EmptySquare) return;\r
10888 \r
10889   ScreenSquare(fromX, fromY, &start);\r
10890   ScreenSquare(toX, toY, &finish);\r
10891 \r
10892   /* All pieces except knights move in straight line */\r
10893   if (piece != WhiteKnight && piece != BlackKnight) {\r
10894     mid.x = start.x + (finish.x - start.x) / 2;\r
10895     mid.y = start.y + (finish.y - start.y) / 2;\r
10896   } else {\r
10897     /* Knight: make diagonal movement then straight */\r
10898     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10899        mid.x = start.x + (finish.x - start.x) / 2;\r
10900        mid.y = finish.y;\r
10901      } else {\r
10902        mid.x = finish.x;\r
10903        mid.y = start.y + (finish.y - start.y) / 2;\r
10904      }\r
10905   }\r
10906   \r
10907   /* Don't use as many frames for very short moves */\r
10908   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10909     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10910   else\r
10911     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10912 \r
10913   animInfo.from.x = fromX;\r
10914   animInfo.from.y = fromY;\r
10915   animInfo.to.x = toX;\r
10916   animInfo.to.y = toY;\r
10917   animInfo.lastpos = start;\r
10918   animInfo.piece = piece;\r
10919   for (n = 0; n < nFrames; n++) {\r
10920     animInfo.pos = frames[n];\r
10921     DrawPosition(FALSE, NULL);\r
10922     animInfo.lastpos = animInfo.pos;\r
10923     Sleep(appData.animSpeed);\r
10924   }\r
10925   animInfo.pos = finish;\r
10926   DrawPosition(FALSE, NULL);\r
10927   animInfo.piece = EmptySquare;\r
10928   if(gameInfo.variant == VariantAtomic && \r
10929      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10930         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10931 }\r
10932 \r
10933 /*      Convert board position to corner of screen rect and color       */\r
10934 \r
10935 static void\r
10936 ScreenSquare(column, row, pt)\r
10937      int column; int row; POINT * pt;\r
10938 {\r
10939   if (flipView) {\r
10940     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10941     pt->y = lineGap + row * (squareSize + lineGap);\r
10942   } else {\r
10943     pt->x = lineGap + column * (squareSize + lineGap);\r
10944     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10945   }\r
10946 }\r
10947 \r
10948 /*      Generate a series of frame coords from start->mid->finish.\r
10949         The movement rate doubles until the half way point is\r
10950         reached, then halves back down to the final destination,\r
10951         which gives a nice slow in/out effect. The algorithmn\r
10952         may seem to generate too many intermediates for short\r
10953         moves, but remember that the purpose is to attract the\r
10954         viewers attention to the piece about to be moved and\r
10955         then to where it ends up. Too few frames would be less\r
10956         noticeable.                                             */\r
10957 \r
10958 static void\r
10959 Tween(start, mid, finish, factor, frames, nFrames)\r
10960      POINT * start; POINT * mid;\r
10961      POINT * finish; int factor;\r
10962      POINT frames[]; int * nFrames;\r
10963 {\r
10964   int n, fraction = 1, count = 0;\r
10965 \r
10966   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10967   for (n = 0; n < factor; n++)\r
10968     fraction *= 2;\r
10969   for (n = 0; n < factor; n++) {\r
10970     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10971     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10972     count ++;\r
10973     fraction = fraction / 2;\r
10974   }\r
10975   \r
10976   /* Midpoint */\r
10977   frames[count] = *mid;\r
10978   count ++;\r
10979   \r
10980   /* Slow out, stepping 1/2, then 1/4, ... */\r
10981   fraction = 2;\r
10982   for (n = 0; n < factor; n++) {\r
10983     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10984     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10985     count ++;\r
10986     fraction = fraction * 2;\r
10987   }\r
10988   *nFrames = count;\r
10989 }\r
10990 \r
10991 void\r
10992 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10993 {\r
10994 #if 0\r
10995     char buf[256];\r
10996 \r
10997     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10998         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10999 \r
11000     OutputDebugString( buf );\r
11001 #endif\r
11002 \r
11003     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
11004 \r
11005     EvalGraphSet( first, last, current, pvInfoList );\r
11006 }\r
11007 \r
11008 void SetProgramStats( FrontEndProgramStats * stats )\r
11009 {\r
11010 #if 0\r
11011     char buf[1024];\r
11012 \r
11013     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
11014         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
11015 \r
11016     OutputDebugString( buf );\r
11017 #endif\r
11018 \r
11019     EngineOutputUpdate( stats );\r
11020 }\r