new forceIllegalMoves option
[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 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 char *defaultTextAttribs[] = \r
236 {\r
237   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
238   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
239   COLOR_NONE\r
240 };\r
241 \r
242 typedef struct {\r
243   char *name;\r
244   int squareSize;\r
245   int lineGap;\r
246   int smallLayout;\r
247   int tinyLayout;\r
248   int cliWidth, cliHeight;\r
249 } SizeInfo;\r
250 \r
251 SizeInfo sizeInfo[] = \r
252 {\r
253   { "tiny",     21, 0, 1, 1, 0, 0 },\r
254   { "teeny",    25, 1, 1, 1, 0, 0 },\r
255   { "dinky",    29, 1, 1, 1, 0, 0 },\r
256   { "petite",   33, 1, 1, 1, 0, 0 },\r
257   { "slim",     37, 2, 1, 0, 0, 0 },\r
258   { "small",    40, 2, 1, 0, 0, 0 },\r
259   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
260   { "middling", 49, 2, 0, 0, 0, 0 },\r
261   { "average",  54, 2, 0, 0, 0, 0 },\r
262   { "moderate", 58, 3, 0, 0, 0, 0 },\r
263   { "medium",   64, 3, 0, 0, 0, 0 },\r
264   { "bulky",    72, 3, 0, 0, 0, 0 },\r
265   { "large",    80, 3, 0, 0, 0, 0 },\r
266   { "big",      87, 3, 0, 0, 0, 0 },\r
267   { "huge",     95, 3, 0, 0, 0, 0 },\r
268   { "giant",    108, 3, 0, 0, 0, 0 },\r
269   { "colossal", 116, 4, 0, 0, 0, 0 },\r
270   { "titanic",  129, 4, 0, 0, 0, 0 },\r
271   { NULL, 0, 0, 0, 0, 0, 0 }\r
272 };\r
273 \r
274 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
275 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
276 {\r
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291   { 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
292   { 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
293   { 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
294   { 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
295 };\r
296 \r
297 MyFont *font[NUM_SIZES][NUM_FONTS];\r
298 \r
299 typedef struct {\r
300   char *label;\r
301   int id;\r
302   HWND hwnd;\r
303   WNDPROC wndproc;\r
304 } MyButtonDesc;\r
305 \r
306 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
307 #define N_BUTTONS 5\r
308 \r
309 MyButtonDesc buttonDesc[N_BUTTONS] =\r
310 {\r
311   {"<<", IDM_ToStart, NULL, NULL},\r
312   {"<", IDM_Backward, NULL, NULL},\r
313   {"P", IDM_Pause, NULL, NULL},\r
314   {">", IDM_Forward, NULL, NULL},\r
315   {">>", IDM_ToEnd, NULL, NULL},\r
316 };\r
317 \r
318 int tinyLayout = 0, smallLayout = 0;\r
319 #define MENU_BAR_ITEMS 7\r
320 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
321   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
322   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
323 };\r
324 \r
325 \r
326 MySound sounds[(int)NSoundClasses];\r
327 MyTextAttribs textAttribs[(int)NColorClasses];\r
328 \r
329 MyColorizeAttribs colorizeAttribs[] = {\r
330   { (COLORREF)0, 0, "Shout Text" },\r
331   { (COLORREF)0, 0, "SShout/CShout" },\r
332   { (COLORREF)0, 0, "Channel 1 Text" },\r
333   { (COLORREF)0, 0, "Channel Text" },\r
334   { (COLORREF)0, 0, "Kibitz Text" },\r
335   { (COLORREF)0, 0, "Tell Text" },\r
336   { (COLORREF)0, 0, "Challenge Text" },\r
337   { (COLORREF)0, 0, "Request Text" },\r
338   { (COLORREF)0, 0, "Seek Text" },\r
339   { (COLORREF)0, 0, "Normal Text" },\r
340   { (COLORREF)0, 0, "None" }\r
341 };\r
342 \r
343 \r
344 \r
345 static char *commentTitle;\r
346 static char *commentText;\r
347 static int commentIndex;\r
348 static Boolean editComment = FALSE;\r
349 HWND commentDialog = NULL;\r
350 BOOLEAN commentDialogUp = FALSE;\r
351 static int commentX, commentY, commentH, commentW;\r
352 \r
353 static char *analysisTitle;\r
354 static char *analysisText;\r
355 HWND analysisDialog = NULL;\r
356 BOOLEAN analysisDialogUp = FALSE;\r
357 static int analysisX, analysisY, analysisH, analysisW;\r
358 \r
359 char errorTitle[MSG_SIZ];\r
360 char errorMessage[2*MSG_SIZ];\r
361 HWND errorDialog = NULL;\r
362 BOOLEAN moveErrorMessageUp = FALSE;\r
363 BOOLEAN consoleEcho = TRUE;\r
364 CHARFORMAT consoleCF;\r
365 COLORREF consoleBackgroundColor;\r
366 \r
367 char *programVersion;\r
368 \r
369 #define CPReal 1\r
370 #define CPComm 2\r
371 #define CPSock 3\r
372 #define CPRcmd 4\r
373 typedef int CPKind;\r
374 \r
375 typedef struct {\r
376   CPKind kind;\r
377   HANDLE hProcess;\r
378   DWORD pid;\r
379   HANDLE hTo;\r
380   HANDLE hFrom;\r
381   SOCKET sock;\r
382   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
383 } ChildProc;\r
384 \r
385 #define INPUT_SOURCE_BUF_SIZE 4096\r
386 \r
387 typedef struct _InputSource {\r
388   CPKind kind;\r
389   HANDLE hFile;\r
390   SOCKET sock;\r
391   int lineByLine;\r
392   HANDLE hThread;\r
393   DWORD id;\r
394   char buf[INPUT_SOURCE_BUF_SIZE];\r
395   char *next;\r
396   DWORD count;\r
397   int error;\r
398   InputCallback func;\r
399   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
400   VOIDSTAR closure;\r
401 } InputSource;\r
402 \r
403 InputSource *consoleInputSource;\r
404 \r
405 DCB dcb;\r
406 \r
407 /* forward */\r
408 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
409 VOID ConsoleCreate();\r
410 LRESULT CALLBACK\r
411   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
412 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
413 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
414 VOID ParseCommSettings(char *arg, DCB *dcb);\r
415 LRESULT CALLBACK\r
416   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
417 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
418 void ParseIcsTextMenu(char *icsTextMenuString);\r
419 VOID PopUpMoveDialog(char firstchar);\r
420 VOID PopUpNameDialog(char firstchar);\r
421 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
422 \r
423 /* [AS] */\r
424 int NewGameFRC();\r
425 int GameListOptions();\r
426 \r
427 HWND moveHistoryDialog = NULL;\r
428 BOOLEAN moveHistoryDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpMoveHistory;\r
431 \r
432 HWND evalGraphDialog = NULL;\r
433 BOOLEAN evalGraphDialogUp = FALSE;\r
434 \r
435 WindowPlacement wpEvalGraph;\r
436 \r
437 HWND engineOutputDialog = NULL;\r
438 BOOLEAN engineOutputDialogUp = FALSE;\r
439 \r
440 WindowPlacement wpEngineOutput;\r
441 WindowPlacement wpGameList;\r
442 WindowPlacement wpConsole;\r
443 \r
444 VOID MoveHistoryPopUp();\r
445 VOID MoveHistoryPopDown();\r
446 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
447 BOOL MoveHistoryIsUp();\r
448 \r
449 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
450 VOID EvalGraphPopUp();\r
451 VOID EvalGraphPopDown();\r
452 BOOL EvalGraphIsUp();\r
453 \r
454 VOID EngineOutputPopUp();\r
455 VOID EngineOutputPopDown();\r
456 BOOL EngineOutputIsUp();\r
457 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
458 \r
459 VOID EngineOptionsPopup(); // [HGM] settings\r
460 \r
461 VOID GothicPopUp(char *title, VariantClass variant);\r
462 /*\r
463  * Setting "frozen" should disable all user input other than deleting\r
464  * the window.  We do this while engines are initializing themselves.\r
465  */\r
466 static int frozen = 0;\r
467 static int oldMenuItemState[MENU_BAR_ITEMS];\r
468 void FreezeUI()\r
469 {\r
470   HMENU hmenu;\r
471   int i;\r
472 \r
473   if (frozen) return;\r
474   frozen = 1;\r
475   hmenu = GetMenu(hwndMain);\r
476   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
477     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
478   }\r
479   DrawMenuBar(hwndMain);\r
480 }\r
481 \r
482 /* Undo a FreezeUI */\r
483 void ThawUI()\r
484 {\r
485   HMENU hmenu;\r
486   int i;\r
487 \r
488   if (!frozen) return;\r
489   frozen = 0;\r
490   hmenu = GetMenu(hwndMain);\r
491   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
492     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
493   }\r
494   DrawMenuBar(hwndMain);\r
495 }\r
496 \r
497 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
498 \r
499 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
500 #ifdef JAWS\r
501 #include "jaws.c"\r
502 #else\r
503 #define JAWS_INIT\r
504 #define JAWS_ARGS\r
505 #define JAWS_ALT_INTERCEPT\r
506 #define JAWS_KB_NAVIGATION\r
507 #define JAWS_MENU_ITEMS\r
508 #define JAWS_SILENCE\r
509 #define JAWS_REPLAY\r
510 #define JAWS_ACCEL\r
511 #define JAWS_COPYRIGHT\r
512 #define JAWS_DELETE(X) X\r
513 #define SAYMACHINEMOVE()\r
514 #define SAY(X)\r
515 #endif\r
516 \r
517 /*---------------------------------------------------------------------------*\\r
518  *\r
519  * WinMain\r
520  *\r
521 \*---------------------------------------------------------------------------*/\r
522 \r
523 int APIENTRY\r
524 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
525         LPSTR lpCmdLine, int nCmdShow)\r
526 {\r
527   MSG msg;\r
528   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
529 //  INITCOMMONCONTROLSEX ex;\r
530 \r
531   debugFP = stderr;\r
532 \r
533   LoadLibrary("RICHED32.DLL");\r
534   consoleCF.cbSize = sizeof(CHARFORMAT);\r
535 \r
536   if (!InitApplication(hInstance)) {\r
537     return (FALSE);\r
538   }\r
539   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
540     return (FALSE);\r
541   }\r
542 \r
543   JAWS_INIT\r
544 \r
545 //  InitCommonControlsEx(&ex);\r
546   InitCommonControls();\r
547 \r
548   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
549   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
550   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
551 \r
552   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
553 \r
554   while (GetMessage(&msg, /* message structure */\r
555                     NULL, /* handle of window receiving the message */\r
556                     0,    /* lowest message to examine */\r
557                     0))   /* highest message to examine */\r
558     {\r
559 \r
560       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
561         // [HGM] navigate: switch between all windows with tab\r
562         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
563         int i, currentElement = 0;\r
564 \r
565         // first determine what element of the chain we come from (if any)\r
566         if(appData.icsActive) {\r
567             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
568             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
569         }\r
570         if(engineOutputDialog && EngineOutputIsUp()) {\r
571             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
572             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
573         }\r
574         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
575             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
576         }\r
577         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
578         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
579         if(msg.hwnd == e1)                 currentElement = 2; else\r
580         if(msg.hwnd == e2)                 currentElement = 3; else\r
581         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
582         if(msg.hwnd == mh)                currentElement = 4; else\r
583         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
584         if(msg.hwnd == hText)  currentElement = 5; else\r
585         if(msg.hwnd == hInput) currentElement = 6; else\r
586         for (i = 0; i < N_BUTTONS; i++) {\r
587             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
588         }\r
589 \r
590         // determine where to go to\r
591         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
592           do {\r
593             currentElement = (currentElement + direction) % 7;\r
594             switch(currentElement) {\r
595                 case 0:\r
596                   h = hwndMain; break; // passing this case always makes the loop exit\r
597                 case 1:\r
598                   h = buttonDesc[0].hwnd; break; // could be NULL\r
599                 case 2:\r
600                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
601                   h = e1; break;\r
602                 case 3:\r
603                   if(!EngineOutputIsUp()) continue;\r
604                   h = e2; break;\r
605                 case 4:\r
606                   if(!MoveHistoryIsUp()) continue;\r
607                   h = mh; break;\r
608 //              case 6: // input to eval graph does not seem to get here!\r
609 //                if(!EvalGraphIsUp()) continue;\r
610 //                h = evalGraphDialog; break;\r
611                 case 5:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("display");\r
614                   h = hText; break;\r
615                 case 6:\r
616                   if(!appData.icsActive) continue;\r
617                   SAY("input");\r
618                   h = hInput; break;\r
619             }\r
620           } while(h == 0);\r
621 \r
622           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
623           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
624           SetFocus(h);\r
625 \r
626           continue; // this message now has been processed\r
627         }\r
628       }\r
629 \r
630       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
631           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
632           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
633           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
634           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
635           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
636           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
637           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
638           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
639           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
640         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
641         for(i=0; i<MAX_CHAT; i++) \r
642             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
643                 done = 1; break;\r
644         }\r
645         if(done) continue; // [HGM] chat: end patch\r
646         TranslateMessage(&msg); /* Translates virtual key codes */\r
647         DispatchMessage(&msg);  /* Dispatches message to window */\r
648       }\r
649     }\r
650 \r
651 \r
652   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
653 }\r
654 \r
655 /*---------------------------------------------------------------------------*\\r
656  *\r
657  * Initialization functions\r
658  *\r
659 \*---------------------------------------------------------------------------*/\r
660 \r
661 void\r
662 SetUserLogo()\r
663 {   // update user logo if necessary\r
664     static char oldUserName[MSG_SIZ], *curName;\r
665 \r
666     if(appData.autoLogo) {\r
667           curName = UserName();\r
668           if(strcmp(curName, oldUserName)) {\r
669                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
670                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
671                 strcpy(oldUserName, curName);\r
672           }\r
673     }\r
674 }\r
675 \r
676 BOOL\r
677 InitApplication(HINSTANCE hInstance)\r
678 {\r
679   WNDCLASS wc;\r
680 \r
681   /* Fill in window class structure with parameters that describe the */\r
682   /* main window. */\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
685   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
686   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
687   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
688   wc.hInstance     = hInstance;         /* Owner of this class */\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
691   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
692   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
693   wc.lpszClassName = szAppName;                 /* Name to register as */\r
694 \r
695   /* Register the window class and return success/failure code. */\r
696   if (!RegisterClass(&wc)) return FALSE;\r
697 \r
698   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
699   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
700   wc.cbClsExtra    = 0;\r
701   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
702   wc.hInstance     = hInstance;\r
703   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
704   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
705   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
706   wc.lpszMenuName  = NULL;\r
707   wc.lpszClassName = szConsoleName;\r
708 \r
709   if (!RegisterClass(&wc)) return FALSE;\r
710   return TRUE;\r
711 }\r
712 \r
713 \r
714 /* Set by InitInstance, used by EnsureOnScreen */\r
715 int screenHeight, screenWidth;\r
716 \r
717 void\r
718 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
719 {\r
720 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
721   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
722   if (*x > screenWidth - 32) *x = 0;\r
723   if (*y > screenHeight - 32) *y = 0;\r
724   if (*x < minX) *x = minX;\r
725   if (*y < minY) *y = minY;\r
726 }\r
727 \r
728 BOOL\r
729 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
730 {\r
731   HWND hwnd; /* Main window handle. */\r
732   int ibs;\r
733   WINDOWPLACEMENT wp;\r
734   char *filepart;\r
735 \r
736   hInst = hInstance;    /* Store instance handle in our global variable */\r
737 \r
738   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
739     *filepart = NULLCHAR;\r
740   } else {\r
741     GetCurrentDirectory(MSG_SIZ, installDir);\r
742   }\r
743   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
744   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
745   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
746   if (appData.debugMode) {\r
747     debugFP = fopen(appData.nameOfDebugFile, "w");\r
748     setbuf(debugFP, NULL);\r
749   }\r
750 \r
751   InitBackEnd1();\r
752 \r
753 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
754 //  InitEngineUCI( installDir, &second );\r
755 \r
756   /* Create a main window for this application instance. */\r
757   hwnd = CreateWindow(szAppName, szTitle,\r
758                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
759                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
760                       NULL, NULL, hInstance, NULL);\r
761   hwndMain = hwnd;\r
762 \r
763   /* If window could not be created, return "failure" */\r
764   if (!hwnd) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
769   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
770       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (first.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
777         char buf[MSG_SIZ];\r
778         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
779         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
780       }\r
781   }\r
782 \r
783   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
784       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
785 \r
786       if (second.programLogo == NULL && appData.debugMode) {\r
787           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
788       }\r
789   } else if(appData.autoLogo) {\r
790       char buf[MSG_SIZ];\r
791       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
792         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
794       } else\r
795       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
796         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
797         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
798       }\r
799   }\r
800 \r
801   SetUserLogo();\r
802 \r
803   iconWhite = LoadIcon(hInstance, "icon_white");\r
804   iconBlack = LoadIcon(hInstance, "icon_black");\r
805   iconCurrent = iconWhite;\r
806   InitDrawingColors();\r
807   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
808   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
809   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
810     /* Compute window size for each board size, and use the largest\r
811        size that fits on this screen as the default. */\r
812     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
813     if (boardSize == (BoardSize)-1 &&\r
814         winH <= screenHeight\r
815            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
816         && winW <= screenWidth) {\r
817       boardSize = (BoardSize)ibs;\r
818     }\r
819   }\r
820 \r
821   InitDrawingSizes(boardSize, 0);\r
822   InitMenuChecks();\r
823   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
824 \r
825   /* [AS] Load textures if specified */\r
826   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
827   \r
828   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
829       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
830       liteBackTextureMode = appData.liteBackTextureMode;\r
831 \r
832       if (liteBackTexture == NULL && appData.debugMode) {\r
833           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
834       }\r
835   }\r
836   \r
837   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
838       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
839       darkBackTextureMode = appData.darkBackTextureMode;\r
840 \r
841       if (darkBackTexture == NULL && appData.debugMode) {\r
842           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
843       }\r
844   }\r
845 \r
846   mysrandom( (unsigned) time(NULL) );\r
847 \r
848   /* [AS] Restore layout */\r
849   if( wpMoveHistory.visible ) {\r
850       MoveHistoryPopUp();\r
851   }\r
852 \r
853   if( wpEvalGraph.visible ) {\r
854       EvalGraphPopUp();\r
855   }\r
856 \r
857   if( wpEngineOutput.visible ) {\r
858       EngineOutputPopUp();\r
859   }\r
860 \r
861   InitBackEnd2();\r
862 \r
863   /* Make the window visible; update its client area; and return "success" */\r
864   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
865   wp.length = sizeof(WINDOWPLACEMENT);\r
866   wp.flags = 0;\r
867   wp.showCmd = nCmdShow;\r
868   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
869   wp.rcNormalPosition.left = boardX;\r
870   wp.rcNormalPosition.right = boardX + winWidth;\r
871   wp.rcNormalPosition.top = boardY;\r
872   wp.rcNormalPosition.bottom = boardY + winHeight;\r
873   SetWindowPlacement(hwndMain, &wp);\r
874 \r
875   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
876                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
877 \r
878   if (hwndConsole) {\r
879 #if AOT_CONSOLE\r
880     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
881                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
882 #endif\r
883     ShowWindow(hwndConsole, nCmdShow);\r
884   }\r
885   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
886   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
887 \r
888   return TRUE;\r
889 \r
890 }\r
891 \r
892 \r
893 typedef enum {\r
894   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
895   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
896   ArgSettingsFilename,\r
897   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
898 } ArgType;\r
899 \r
900 typedef struct {\r
901   char *argName;\r
902   ArgType argType;\r
903   /***\r
904   union {\r
905     String *pString;       // ArgString\r
906     int *pInt;             // ArgInt\r
907     float *pFloat;         // ArgFloat\r
908     Boolean *pBoolean;     // ArgBoolean\r
909     COLORREF *pColor;      // ArgColor\r
910     ColorClass cc;         // ArgAttribs\r
911     String *pFilename;     // ArgFilename\r
912     BoardSize *pBoardSize; // ArgBoardSize\r
913     int whichFont;         // ArgFont\r
914     DCB *pDCB;             // ArgCommSettings\r
915     String *pFilename;     // ArgSettingsFilename\r
916   } argLoc;\r
917   ***/\r
918   LPVOID argLoc;\r
919   BOOL save;\r
920 } ArgDescriptor;\r
921 \r
922 int junk;\r
923 ArgDescriptor argDescriptors[] = {\r
924   /* positional arguments */\r
925   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
926   { "", ArgNone, NULL },\r
927   /* keyword arguments */\r
928   JAWS_ARGS\r
929   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
930   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
931   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
932   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
933   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
934   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
935   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
936   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
937   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
938   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
939   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
940   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
941   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
942   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
943   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
944   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
945   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
946   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
947     FALSE },\r
948   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
949     FALSE },\r
950   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
951     FALSE },\r
952   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
953   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
954     FALSE },\r
955   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
956   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
957   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
958   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
959   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
960   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
961   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
962   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
963   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
964   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
965   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
966   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
967   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
968   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
969   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
970   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
971   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
972   /*!!bitmapDirectory?*/\r
973   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
974   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
975   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
976   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
977   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
978   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
979   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
980   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
981   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
982   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
983   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
984   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
985   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
986   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
987   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
988   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
989   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
990   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
991   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
992   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
993   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
994   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
995   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
996   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
997   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
998   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
999   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1000   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1001   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
1002   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
1003   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
1004   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1005   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1006   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1007   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1008   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1009   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1010   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1011   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1012   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1013   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1014   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1015   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1016   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1017   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1018   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1019   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1020   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1021   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1022   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1023   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1024   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1025   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1026   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1027   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1028   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1029   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1030   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1031   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1032   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1033   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1034   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1035   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1036   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1037   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1038   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1039   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1040   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1041   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1042   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1043   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1044   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1045   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1046   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1047   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1048   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1049   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1050   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1051   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1052   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1053   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1054   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1055   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1056   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1057   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1058   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1059   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1060   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1061   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1062   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1063   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1064   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1065   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1066   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1067     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1068   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1069   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1070   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1071   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1072   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1073   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1074   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1075   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1076     TRUE }, /* must come after all fonts */\r
1077   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1078   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1079     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1080   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1081   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1082   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1083   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1084   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1085   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1086   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1087   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1088   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1089   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1090   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1091   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1092   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1093   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1094   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1095   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1096   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1097   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1098   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1099   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1100   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1101   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1102   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1103   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1104   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1105   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1106   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1107   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1108 #if 0\r
1109   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1110   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1111 #endif\r
1112   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1113   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1114   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1115   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1116   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1117   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1118   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1119   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1120   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1121   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1122   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1123   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1124   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1125   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1126   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1127   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1128   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1129   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1130   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1131   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1132   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1133   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1134   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1135   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1136   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1137   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1138   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1139   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1140   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1141   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1142   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1143   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1144   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1145   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1146   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1147   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1148   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1149   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1150   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1151   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1152   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1153   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1154   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1155   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1156   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1157   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1158   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1159   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1160   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1161   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1162   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1163   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1164   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1165   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1166   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1167   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1168   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1169   { "highlightLastMove", ArgBoolean,\r
1170     (LPVOID) &appData.highlightLastMove, TRUE },\r
1171   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1172   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1173   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1174   { "highlightDragging", ArgBoolean,\r
1175     (LPVOID) &appData.highlightDragging, TRUE },\r
1176   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1177   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1178   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1179   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1180   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1181   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1182   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1183   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1184   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1185   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1186   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1187   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1188   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1189   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1190   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1191   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1192   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1193   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1194   { "soundShout", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1196   { "soundSShout", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1198   { "soundChannel1", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1200   { "soundChannel", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1202   { "soundKibitz", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1204   { "soundTell", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1206   { "soundChallenge", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1208   { "soundRequest", ArgFilename,\r
1209     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1210   { "soundSeek", ArgFilename,\r
1211     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1212   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1213   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1214   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1215   { "soundIcsLoss", ArgFilename, \r
1216     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1217   { "soundIcsDraw", ArgFilename, \r
1218     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1219   { "soundIcsUnfinished", ArgFilename, \r
1220     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1221   { "soundIcsAlarm", ArgFilename, \r
1222     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1223   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1224   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1225   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1226   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1227   { "reuseChessPrograms", ArgBoolean,\r
1228     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1229   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1230   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1231   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1232   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1233   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1234   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1235   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1236   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1237   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1238   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1239   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1240   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1241   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1242   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1243   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1244     TRUE },\r
1245   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1246     TRUE },\r
1247   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1248   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1249   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1250   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1251   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1252   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1253   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1254   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1255   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1256   /* [AS] New features */\r
1257   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1258   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1259   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1260   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1261   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1262   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1263   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1264   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1265   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1266   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1267   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1268   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1269   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1270   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1271   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1272   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1273   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1274   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1275   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1276   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1277   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1278   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1279   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1280   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1281   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1282   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1283   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1284   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1285   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1286   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1287   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1288   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1289   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1290   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1291   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1292   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1293   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1294   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1295   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1296   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1297   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1298   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1299   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1300   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1301   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1302   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1303   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1304   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1305   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1306   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1307 \r
1308   /* [HGM] board-size, adjudication and misc. options */\r
1309   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1310   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1311   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1312   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1313   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1314   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1315   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1316   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1317   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1318   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1319   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1320   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1321   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1322   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1323   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1324   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1325   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1326   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1327   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1328   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1329   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1330   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1331   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1332   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1333   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1334   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1335   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1336   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1337   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1338   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1339   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1340   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1341   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1342   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1343 \r
1344 #ifdef ZIPPY\r
1345   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1346   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1347   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1348   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1349   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1350   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1351   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1352   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1353   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1354   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1355   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1356   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1357   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1358     FALSE },\r
1359   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1360   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1361   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1362   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1363   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1364   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1365   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1366     FALSE },\r
1367   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1368   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1369   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1370   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1371   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1372   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1373   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1374   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1375   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1376   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1377   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1378   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1379   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1380   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1381   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1382   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1383   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1384   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1385   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1386 #endif\r
1387   /* [HGM] options for broadcasting and time odds */\r
1388   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1389   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1390   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1391   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1392   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1393   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1394   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1395   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1396   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1397   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1398   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1399 \r
1400   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1401   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1402   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1403   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1404   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1405   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1406   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1407   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1408   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1409   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1410   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1411   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1412   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1413   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1414   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1415   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1416   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1417   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1418   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1419   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1420   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1421   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1422   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1423   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1424   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1425   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1426   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1427   /* [AS] Layout stuff */\r
1428   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1429   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1430   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1431   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1432   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1433 \r
1434   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1435   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1436   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1437   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1438   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1439 \r
1440   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1441   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1442   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1443   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1444   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1445 \r
1446   { NULL, ArgNone, NULL, FALSE }\r
1447 };\r
1448 \r
1449 \r
1450 /* Kludge for indirection files on command line */\r
1451 char* lastIndirectionFilename;\r
1452 ArgDescriptor argDescriptorIndirection =\r
1453 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1454 \r
1455 \r
1456 VOID\r
1457 ExitArgError(char *msg, char *badArg)\r
1458 {\r
1459   char buf[MSG_SIZ];\r
1460 \r
1461   sprintf(buf, "%s %s", msg, badArg);\r
1462   DisplayFatalError(buf, 0, 2);\r
1463   exit(2);\r
1464 }\r
1465 \r
1466 /* Command line font name parser.  NULL name means do nothing.\r
1467    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1468    For backward compatibility, syntax without the colon is also\r
1469    accepted, but font names with digits in them won't work in that case.\r
1470 */\r
1471 VOID\r
1472 ParseFontName(char *name, MyFontParams *mfp)\r
1473 {\r
1474   char *p, *q;\r
1475   if (name == NULL) return;\r
1476   p = name;\r
1477   q = strchr(p, ':');\r
1478   if (q) {\r
1479     if (q - p >= sizeof(mfp->faceName))\r
1480       ExitArgError("Font name too long:", name);\r
1481     memcpy(mfp->faceName, p, q - p);\r
1482     mfp->faceName[q - p] = NULLCHAR;\r
1483     p = q + 1;\r
1484   } else {\r
1485     q = mfp->faceName;\r
1486     while (*p && !isdigit(*p)) {\r
1487       *q++ = *p++;\r
1488       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1489         ExitArgError("Font name too long:", name);\r
1490     }\r
1491     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1492     *q = NULLCHAR;\r
1493   }\r
1494   if (!*p) ExitArgError("Font point size missing:", name);\r
1495   mfp->pointSize = (float) atof(p);\r
1496   mfp->bold = (strchr(p, 'b') != NULL);\r
1497   mfp->italic = (strchr(p, 'i') != NULL);\r
1498   mfp->underline = (strchr(p, 'u') != NULL);\r
1499   mfp->strikeout = (strchr(p, 's') != NULL);\r
1500 }\r
1501 \r
1502 /* Color name parser.\r
1503    X version accepts X color names, but this one\r
1504    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1505 COLORREF\r
1506 ParseColorName(char *name)\r
1507 {\r
1508   int red, green, blue, count;\r
1509   char buf[MSG_SIZ];\r
1510 \r
1511   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1512   if (count != 3) {\r
1513     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1514       &red, &green, &blue);\r
1515   }\r
1516   if (count != 3) {\r
1517     sprintf(buf, "Can't parse color name %s", name);\r
1518     DisplayError(buf, 0);\r
1519     return RGB(0, 0, 0);\r
1520   }\r
1521   return PALETTERGB(red, green, blue);\r
1522 }\r
1523 \r
1524 \r
1525 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1526 {\r
1527   char *e = argValue;\r
1528   int eff = 0;\r
1529 \r
1530   while (*e) {\r
1531     if (*e == 'b')      eff |= CFE_BOLD;\r
1532     else if (*e == 'i') eff |= CFE_ITALIC;\r
1533     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1534     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1535     else if (*e == '#' || isdigit(*e)) break;\r
1536     e++;\r
1537   }\r
1538   *effects = eff;\r
1539   *color   = ParseColorName(e);\r
1540 }\r
1541 \r
1542 \r
1543 BoardSize\r
1544 ParseBoardSize(char *name)\r
1545 {\r
1546   BoardSize bs = SizeTiny;\r
1547   while (sizeInfo[bs].name != NULL) {\r
1548     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1549     bs++;\r
1550   }\r
1551   ExitArgError("Unrecognized board size value", name);\r
1552   return bs; /* not reached */\r
1553 }\r
1554 \r
1555 \r
1556 char\r
1557 StringGet(void *getClosure)\r
1558 {\r
1559   char **p = (char **) getClosure;\r
1560   return *((*p)++);\r
1561 }\r
1562 \r
1563 char\r
1564 FileGet(void *getClosure)\r
1565 {\r
1566   int c;\r
1567   FILE* f = (FILE*) getClosure;\r
1568 \r
1569   c = getc(f);\r
1570   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1571   if (c == EOF)\r
1572     return NULLCHAR;\r
1573   else\r
1574     return (char) c;\r
1575 }\r
1576 \r
1577 /* Parse settings file named "name". If file found, return the\r
1578    full name in fullname and return TRUE; else return FALSE */\r
1579 BOOLEAN\r
1580 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1581 {\r
1582   char *dummy;\r
1583   FILE *f;\r
1584   int ok; char buf[MSG_SIZ];\r
1585 \r
1586   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1587   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1588     sprintf(buf, "%s.ini", name);\r
1589     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1590   }\r
1591   if (ok) {\r
1592     f = fopen(fullname, "r");\r
1593     if (f != NULL) {\r
1594       ParseArgs(FileGet, f);\r
1595       fclose(f);\r
1596       return TRUE;\r
1597     }\r
1598   }\r
1599   return FALSE;\r
1600 }\r
1601 \r
1602 VOID\r
1603 ParseArgs(GetFunc get, void *cl)\r
1604 {\r
1605   char argName[ARG_MAX];\r
1606   char argValue[ARG_MAX];\r
1607   ArgDescriptor *ad;\r
1608   char start;\r
1609   char *q;\r
1610   int i, octval;\r
1611   char ch;\r
1612   int posarg = 0;\r
1613 \r
1614   ch = get(cl);\r
1615   for (;;) {\r
1616     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1617     if (ch == NULLCHAR) break;\r
1618     if (ch == ';') {\r
1619       /* Comment to end of line */\r
1620       ch = get(cl);\r
1621       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1622       continue;\r
1623     } else if (ch == '/' || ch == '-') {\r
1624       /* Switch */\r
1625       q = argName;\r
1626       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1627              ch != '\n' && ch != '\t') {\r
1628         *q++ = ch;\r
1629         ch = get(cl);\r
1630       }\r
1631       *q = NULLCHAR;\r
1632 \r
1633       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1634         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1635 \r
1636       if (ad->argName == NULL)\r
1637         ExitArgError("Unrecognized argument", argName);\r
1638 \r
1639     } else if (ch == '@') {\r
1640       /* Indirection file */\r
1641       ad = &argDescriptorIndirection;\r
1642       ch = get(cl);\r
1643     } else {\r
1644       /* Positional argument */\r
1645       ad = &argDescriptors[posarg++];\r
1646       strcpy(argName, ad->argName);\r
1647     }\r
1648 \r
1649     if (ad->argType == ArgTrue) {\r
1650       *(Boolean *) ad->argLoc = TRUE;\r
1651       continue;\r
1652     }\r
1653     if (ad->argType == ArgFalse) {\r
1654       *(Boolean *) ad->argLoc = FALSE;\r
1655       continue;\r
1656     }\r
1657 \r
1658     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1659     if (ch == NULLCHAR || ch == '\n') {\r
1660       ExitArgError("No value provided for argument", argName);\r
1661     }\r
1662     q = argValue;\r
1663     if (ch == '{') {\r
1664       // Quoting with { }.  No characters have to (or can) be escaped.\r
1665       // Thus the string cannot contain a '}' character.\r
1666       start = ch;\r
1667       ch = get(cl);\r
1668       while (start) {\r
1669         switch (ch) {\r
1670         case NULLCHAR:\r
1671           start = NULLCHAR;\r
1672           break;\r
1673           \r
1674         case '}':\r
1675           ch = get(cl);\r
1676           start = NULLCHAR;\r
1677           break;\r
1678 \r
1679         default:\r
1680           *q++ = ch;\r
1681           ch = get(cl);\r
1682           break;\r
1683         }\r
1684       }   \r
1685     } else if (ch == '\'' || ch == '"') {\r
1686       // Quoting with ' ' or " ", with \ as escape character.\r
1687       // Inconvenient for long strings that may contain Windows filenames.\r
1688       start = ch;\r
1689       ch = get(cl);\r
1690       while (start) {\r
1691         switch (ch) {\r
1692         case NULLCHAR:\r
1693           start = NULLCHAR;\r
1694           break;\r
1695 \r
1696         default:\r
1697         not_special:\r
1698           *q++ = ch;\r
1699           ch = get(cl);\r
1700           break;\r
1701 \r
1702         case '\'':\r
1703         case '\"':\r
1704           if (ch == start) {\r
1705             ch = get(cl);\r
1706             start = NULLCHAR;\r
1707             break;\r
1708           } else {\r
1709             goto not_special;\r
1710           }\r
1711 \r
1712         case '\\':\r
1713           if (ad->argType == ArgFilename\r
1714               || ad->argType == ArgSettingsFilename) {\r
1715               goto not_special;\r
1716           }\r
1717           ch = get(cl);\r
1718           switch (ch) {\r
1719           case NULLCHAR:\r
1720             ExitArgError("Incomplete \\ escape in value for", argName);\r
1721             break;\r
1722           case 'n':\r
1723             *q++ = '\n';\r
1724             ch = get(cl);\r
1725             break;\r
1726           case 'r':\r
1727             *q++ = '\r';\r
1728             ch = get(cl);\r
1729             break;\r
1730           case 't':\r
1731             *q++ = '\t';\r
1732             ch = get(cl);\r
1733             break;\r
1734           case 'b':\r
1735             *q++ = '\b';\r
1736             ch = get(cl);\r
1737             break;\r
1738           case 'f':\r
1739             *q++ = '\f';\r
1740             ch = get(cl);\r
1741             break;\r
1742           default:\r
1743             octval = 0;\r
1744             for (i = 0; i < 3; i++) {\r
1745               if (ch >= '0' && ch <= '7') {\r
1746                 octval = octval*8 + (ch - '0');\r
1747                 ch = get(cl);\r
1748               } else {\r
1749                 break;\r
1750               }\r
1751             }\r
1752             if (i > 0) {\r
1753               *q++ = (char) octval;\r
1754             } else {\r
1755               *q++ = ch;\r
1756               ch = get(cl);\r
1757             }\r
1758             break;\r
1759           }\r
1760           break;\r
1761         }\r
1762       }\r
1763     } else {\r
1764       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1765         *q++ = ch;\r
1766         ch = get(cl);\r
1767       }\r
1768     }\r
1769     *q = NULLCHAR;\r
1770 \r
1771     switch (ad->argType) {\r
1772     case ArgInt:\r
1773       *(int *) ad->argLoc = atoi(argValue);\r
1774       break;\r
1775 \r
1776     case ArgX:\r
1777       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1778       break;\r
1779 \r
1780     case ArgY:\r
1781       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1782       break;\r
1783 \r
1784     case ArgZ:\r
1785       *(int *) ad->argLoc = atoi(argValue);\r
1786       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1787       break;\r
1788 \r
1789     case ArgFloat:\r
1790       *(float *) ad->argLoc = (float) atof(argValue);\r
1791       break;\r
1792 \r
1793     case ArgString:\r
1794     case ArgFilename:\r
1795       *(char **) ad->argLoc = strdup(argValue);\r
1796       break;\r
1797 \r
1798     case ArgSettingsFilename:\r
1799       {\r
1800         char fullname[MSG_SIZ];\r
1801         if (ParseSettingsFile(argValue, fullname)) {\r
1802           if (ad->argLoc != NULL) {\r
1803             *(char **) ad->argLoc = strdup(fullname);\r
1804           }\r
1805         } else {\r
1806           if (ad->argLoc != NULL) {\r
1807           } else {\r
1808             ExitArgError("Failed to open indirection file", argValue);\r
1809           }\r
1810         }\r
1811       }\r
1812       break;\r
1813 \r
1814     case ArgBoolean:\r
1815       switch (argValue[0]) {\r
1816       case 't':\r
1817       case 'T':\r
1818         *(Boolean *) ad->argLoc = TRUE;\r
1819         break;\r
1820       case 'f':\r
1821       case 'F':\r
1822         *(Boolean *) ad->argLoc = FALSE;\r
1823         break;\r
1824       default:\r
1825         ExitArgError("Unrecognized boolean argument value", argValue);\r
1826         break;\r
1827       }\r
1828       break;\r
1829 \r
1830     case ArgColor:\r
1831       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1832       break;\r
1833 \r
1834     case ArgAttribs: {\r
1835       ColorClass cc = (ColorClass)ad->argLoc;\r
1836       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1837       }\r
1838       break;\r
1839       \r
1840     case ArgBoardSize:\r
1841       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1842       break;\r
1843 \r
1844     case ArgFont:\r
1845       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1846       break;\r
1847 \r
1848     case ArgCommSettings:\r
1849       ParseCommSettings(argValue, &dcb);\r
1850       break;\r
1851 \r
1852     case ArgNone:\r
1853       ExitArgError("Unrecognized argument", argValue);\r
1854       break;\r
1855     case ArgTrue:\r
1856     case ArgFalse: ;\r
1857     }\r
1858   }\r
1859 }\r
1860 \r
1861 VOID\r
1862 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1863 {\r
1864   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1865   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1866   DeleteDC(hdc);\r
1867   lf->lfWidth = 0;\r
1868   lf->lfEscapement = 0;\r
1869   lf->lfOrientation = 0;\r
1870   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1871   lf->lfItalic = mfp->italic;\r
1872   lf->lfUnderline = mfp->underline;\r
1873   lf->lfStrikeOut = mfp->strikeout;\r
1874   lf->lfCharSet = DEFAULT_CHARSET;\r
1875   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1876   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1877   lf->lfQuality = DEFAULT_QUALITY;\r
1878   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1879   strcpy(lf->lfFaceName, mfp->faceName);\r
1880 }\r
1881 \r
1882 VOID\r
1883 CreateFontInMF(MyFont *mf)\r
1884 {\r
1885   LFfromMFP(&mf->lf, &mf->mfp);\r
1886   if (mf->hf) DeleteObject(mf->hf);\r
1887   mf->hf = CreateFontIndirect(&mf->lf);\r
1888 }\r
1889 \r
1890 VOID\r
1891 SetDefaultTextAttribs()\r
1892 {\r
1893   ColorClass cc;\r
1894   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1895     ParseAttribs(&textAttribs[cc].color, \r
1896                  &textAttribs[cc].effects, \r
1897                  defaultTextAttribs[cc]);\r
1898   }\r
1899 }\r
1900 \r
1901 VOID\r
1902 SetDefaultSounds()\r
1903 {\r
1904   ColorClass cc;\r
1905   SoundClass sc;\r
1906   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1907     textAttribs[cc].sound.name = strdup("");\r
1908     textAttribs[cc].sound.data = NULL;\r
1909   }\r
1910   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1911     sounds[sc].name = strdup("");\r
1912     sounds[sc].data = NULL;\r
1913   }\r
1914   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1915 }\r
1916 \r
1917 VOID\r
1918 LoadAllSounds()\r
1919 {\r
1920   ColorClass cc;\r
1921   SoundClass sc;\r
1922   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1923     MyLoadSound(&textAttribs[cc].sound);\r
1924   }\r
1925   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1926     MyLoadSound(&sounds[sc]);\r
1927   }\r
1928 }\r
1929 \r
1930 VOID\r
1931 InitAppData(LPSTR lpCmdLine)\r
1932 {\r
1933   int i, j;\r
1934   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1935   char *dummy, *p;\r
1936 \r
1937   programName = szAppName;\r
1938 \r
1939   /* Initialize to defaults */\r
1940   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1941   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1942   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1943   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1944   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1945   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1946   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1947   SetDefaultTextAttribs();\r
1948   SetDefaultSounds();\r
1949   appData.movesPerSession = MOVES_PER_SESSION;\r
1950   appData.initString = INIT_STRING;\r
1951   appData.secondInitString = INIT_STRING;\r
1952   appData.firstComputerString = COMPUTER_STRING;\r
1953   appData.secondComputerString = COMPUTER_STRING;\r
1954   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1955   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1956   appData.firstPlaysBlack = FALSE;\r
1957   appData.noChessProgram = FALSE;\r
1958   chessProgram = FALSE;\r
1959   appData.firstHost = FIRST_HOST;\r
1960   appData.secondHost = SECOND_HOST;\r
1961   appData.firstDirectory = FIRST_DIRECTORY;\r
1962   appData.secondDirectory = SECOND_DIRECTORY;\r
1963   appData.bitmapDirectory = "";\r
1964   appData.remoteShell = REMOTE_SHELL;\r
1965   appData.remoteUser = "";\r
1966   appData.timeDelay = TIME_DELAY;\r
1967   appData.timeControl = TIME_CONTROL;\r
1968   appData.timeIncrement = TIME_INCREMENT;\r
1969   appData.icsActive = FALSE;\r
1970   appData.icsHost = "";\r
1971   appData.icsPort = ICS_PORT;\r
1972   appData.icsCommPort = ICS_COMM_PORT;\r
1973   appData.icsLogon = ICS_LOGON;\r
1974   appData.icsHelper = "";\r
1975   appData.useTelnet = FALSE;\r
1976   appData.telnetProgram = TELNET_PROGRAM;\r
1977   appData.gateway = "";\r
1978   appData.loadGameFile = "";\r
1979   appData.loadGameIndex = 0;\r
1980   appData.saveGameFile = "";\r
1981   appData.autoSaveGames = FALSE;\r
1982   appData.loadPositionFile = "";\r
1983   appData.loadPositionIndex = 1;\r
1984   appData.savePositionFile = "";\r
1985   appData.matchMode = FALSE;\r
1986   appData.matchGames = 0;\r
1987   appData.monoMode = FALSE;\r
1988   appData.debugMode = FALSE;\r
1989   appData.clockMode = TRUE;\r
1990   boardSize = (BoardSize) -1; /* determine by screen size */\r
1991   appData.Iconic = FALSE; /*unused*/\r
1992   appData.searchTime = "";\r
1993   appData.searchDepth = 0;\r
1994   appData.showCoords = FALSE;\r
1995   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1996   appData.autoCallFlag = FALSE;\r
1997   appData.flipView = FALSE;\r
1998   appData.autoFlipView = TRUE;\r
1999   appData.cmailGameName = "";\r
2000   appData.alwaysPromoteToQueen = FALSE;\r
2001   appData.oldSaveStyle = FALSE;\r
2002   appData.quietPlay = FALSE;\r
2003   appData.showThinking = FALSE;\r
2004   appData.ponderNextMove = TRUE;\r
2005   appData.periodicUpdates = TRUE;\r
2006   appData.popupExitMessage = TRUE;\r
2007   appData.popupMoveErrors = FALSE;\r
2008   appData.autoObserve = FALSE;\r
2009   appData.autoComment = FALSE;\r
2010   appData.animate = TRUE;\r
2011   appData.animSpeed = 10;\r
2012   appData.animateDragging = TRUE;\r
2013   appData.highlightLastMove = TRUE;\r
2014   appData.getMoveList = TRUE;\r
2015   appData.testLegality = TRUE;\r
2016   appData.premove = TRUE;\r
2017   appData.premoveWhite = FALSE;\r
2018   appData.premoveWhiteText = "";\r
2019   appData.premoveBlack = FALSE;\r
2020   appData.premoveBlackText = "";\r
2021   appData.icsAlarm = TRUE;\r
2022   appData.icsAlarmTime = 5000;\r
2023   appData.autoRaiseBoard = TRUE;\r
2024   appData.localLineEditing = TRUE;\r
2025   appData.colorize = TRUE;\r
2026   appData.reuseFirst = TRUE;\r
2027   appData.reuseSecond = TRUE;\r
2028   appData.blindfold = FALSE;\r
2029   appData.icsEngineAnalyze = FALSE;\r
2030   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2031   dcb.DCBlength = sizeof(DCB);\r
2032   dcb.BaudRate = 9600;\r
2033   dcb.fBinary = TRUE;\r
2034   dcb.fParity = FALSE;\r
2035   dcb.fOutxCtsFlow = FALSE;\r
2036   dcb.fOutxDsrFlow = FALSE;\r
2037   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2038   dcb.fDsrSensitivity = FALSE;\r
2039   dcb.fTXContinueOnXoff = TRUE;\r
2040   dcb.fOutX = FALSE;\r
2041   dcb.fInX = FALSE;\r
2042   dcb.fNull = FALSE;\r
2043   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2044   dcb.fAbortOnError = FALSE;\r
2045   dcb.ByteSize = 7;\r
2046   dcb.Parity = SPACEPARITY;\r
2047   dcb.StopBits = ONESTOPBIT;\r
2048   settingsFileName = SETTINGS_FILE;\r
2049   saveSettingsOnExit = TRUE;\r
2050   boardX = CW_USEDEFAULT;\r
2051   boardY = CW_USEDEFAULT;\r
2052   analysisX = CW_USEDEFAULT; \r
2053   analysisY = CW_USEDEFAULT; \r
2054   analysisW = CW_USEDEFAULT;\r
2055   analysisH = CW_USEDEFAULT;\r
2056   commentX = CW_USEDEFAULT; \r
2057   commentY = CW_USEDEFAULT; \r
2058   commentW = CW_USEDEFAULT;\r
2059   commentH = CW_USEDEFAULT;\r
2060   editTagsX = CW_USEDEFAULT; \r
2061   editTagsY = CW_USEDEFAULT; \r
2062   editTagsW = CW_USEDEFAULT;\r
2063   editTagsH = CW_USEDEFAULT;\r
2064   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2065   icsNames = ICS_NAMES;\r
2066   firstChessProgramNames = FCP_NAMES;\r
2067   secondChessProgramNames = SCP_NAMES;\r
2068   appData.initialMode = "";\r
2069   appData.variant = "normal";\r
2070   appData.firstProtocolVersion = PROTOVER;\r
2071   appData.secondProtocolVersion = PROTOVER;\r
2072   appData.showButtonBar = TRUE;\r
2073 \r
2074    /* [AS] New properties (see comments in header file) */\r
2075   appData.firstScoreIsAbsolute = FALSE;\r
2076   appData.secondScoreIsAbsolute = FALSE;\r
2077   appData.saveExtendedInfoInPGN = FALSE;\r
2078   appData.hideThinkingFromHuman = FALSE;\r
2079   appData.liteBackTextureFile = "";\r
2080   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2081   appData.darkBackTextureFile = "";\r
2082   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2083   appData.renderPiecesWithFont = "";\r
2084   appData.fontToPieceTable = "";\r
2085   appData.fontBackColorWhite = 0;\r
2086   appData.fontForeColorWhite = 0;\r
2087   appData.fontBackColorBlack = 0;\r
2088   appData.fontForeColorBlack = 0;\r
2089   appData.fontPieceSize = 80;\r
2090   appData.overrideLineGap = 1;\r
2091   appData.adjudicateLossThreshold = 0;\r
2092   appData.delayBeforeQuit = 0;\r
2093   appData.delayAfterQuit = 0;\r
2094   appData.nameOfDebugFile = "winboard.debug";\r
2095   appData.pgnEventHeader = "Computer Chess Game";\r
2096   appData.defaultFrcPosition = -1;\r
2097   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2098   appData.saveOutOfBookInfo = TRUE;\r
2099   appData.showEvalInMoveHistory = TRUE;\r
2100   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2101   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2102   appData.highlightMoveWithArrow = FALSE;\r
2103   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2104   appData.useStickyWindows = TRUE;\r
2105   appData.adjudicateDrawMoves = 0;\r
2106   appData.autoDisplayComment = TRUE;\r
2107   appData.autoDisplayTags = TRUE;\r
2108   appData.firstIsUCI = FALSE;\r
2109   appData.secondIsUCI = FALSE;\r
2110   appData.firstHasOwnBookUCI = TRUE;\r
2111   appData.secondHasOwnBookUCI = TRUE;\r
2112   appData.polyglotDir = "";\r
2113   appData.usePolyglotBook = FALSE;\r
2114   appData.polyglotBook = "";\r
2115   appData.defaultHashSize = 64;\r
2116   appData.defaultCacheSizeEGTB = 4;\r
2117   appData.defaultPathEGTB = "c:\\egtb";\r
2118   appData.firstOptions = "";\r
2119   appData.secondOptions = "";\r
2120 \r
2121   InitWindowPlacement( &wpGameList );\r
2122   InitWindowPlacement( &wpMoveHistory );\r
2123   InitWindowPlacement( &wpEvalGraph );\r
2124   InitWindowPlacement( &wpEngineOutput );\r
2125   InitWindowPlacement( &wpConsole );\r
2126 \r
2127   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2128   appData.NrFiles      = -1;\r
2129   appData.NrRanks      = -1;\r
2130   appData.holdingsSize = -1;\r
2131   appData.testClaims   = FALSE;\r
2132   appData.checkMates   = FALSE;\r
2133   appData.materialDraws= FALSE;\r
2134   appData.trivialDraws = FALSE;\r
2135   appData.ruleMoves    = 51;\r
2136   appData.drawRepeats  = 6;\r
2137   appData.matchPause   = 10000;\r
2138   appData.alphaRank    = FALSE;\r
2139   appData.allWhite     = FALSE;\r
2140   appData.upsideDown   = FALSE;\r
2141   appData.serverPause  = 15;\r
2142   appData.serverMovesName   = NULL;\r
2143   appData.suppressLoadMoves = FALSE;\r
2144   appData.firstTimeOdds  = 1;\r
2145   appData.secondTimeOdds = 1;\r
2146   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2147   appData.secondAccumulateTC = 1;\r
2148   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2149   appData.secondNPS = -1;\r
2150   appData.engineComments = 1;\r
2151   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2152   appData.egtFormats = "";\r
2153 \r
2154 #ifdef ZIPPY\r
2155   appData.zippyTalk = ZIPPY_TALK;\r
2156   appData.zippyPlay = ZIPPY_PLAY;\r
2157   appData.zippyLines = ZIPPY_LINES;\r
2158   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2159   appData.zippyPassword = ZIPPY_PASSWORD;\r
2160   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2161   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2162   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2163   appData.zippyUseI = ZIPPY_USE_I;\r
2164   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2165   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2166   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2167   appData.zippyGameStart = ZIPPY_GAME_START;\r
2168   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2169   appData.zippyAbort = ZIPPY_ABORT;\r
2170   appData.zippyVariants = ZIPPY_VARIANTS;\r
2171   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2172   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2173 #endif\r
2174 \r
2175   /* Point font array elements to structures and\r
2176      parse default font names */\r
2177   for (i=0; i<NUM_FONTS; i++) {\r
2178     for (j=0; j<NUM_SIZES; j++) {\r
2179       font[j][i] = &fontRec[j][i];\r
2180       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2181     }\r
2182   }\r
2183   \r
2184   /* Parse default settings file if any */\r
2185   if (ParseSettingsFile(settingsFileName, buf)) {\r
2186     settingsFileName = strdup(buf);\r
2187   }\r
2188 \r
2189   /* Parse command line */\r
2190   ParseArgs(StringGet, &lpCmdLine);\r
2191 \r
2192   /* [HGM] make sure board size is acceptable */\r
2193   if(appData.NrFiles > BOARD_SIZE ||\r
2194      appData.NrRanks > BOARD_SIZE   )\r
2195       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2196 \r
2197   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2198    * with options from the command line, we now make an even higher priority\r
2199    * overrule by WB options attached to the engine command line. This so that\r
2200    * tournament managers can use WB options (such as /timeOdds) that follow\r
2201    * the engines.\r
2202    */\r
2203   if(appData.firstChessProgram != NULL) {\r
2204       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2205       static char *f = "first";\r
2206       char buf[MSG_SIZ], *q = buf;\r
2207       if(p != NULL) { // engine command line contains WinBoard options\r
2208           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2209           ParseArgs(StringGet, &q);\r
2210           p[-1] = 0; // cut them offengine command line\r
2211       }\r
2212   }\r
2213   // now do same for second chess program\r
2214   if(appData.secondChessProgram != NULL) {\r
2215       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2216       static char *s = "second";\r
2217       char buf[MSG_SIZ], *q = buf;\r
2218       if(p != NULL) { // engine command line contains WinBoard options\r
2219           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2220           ParseArgs(StringGet, &q);\r
2221           p[-1] = 0; // cut them offengine command line\r
2222       }\r
2223   }\r
2224 \r
2225 \r
2226   /* Propagate options that affect others */\r
2227   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2228   if (appData.icsActive || appData.noChessProgram) {\r
2229      chessProgram = FALSE;  /* not local chess program mode */\r
2230   }\r
2231 \r
2232   /* Open startup dialog if needed */\r
2233   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2234       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2235       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2236                         *appData.secondChessProgram == NULLCHAR))) {\r
2237     FARPROC lpProc;\r
2238     \r
2239     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2240     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2241     FreeProcInstance(lpProc);\r
2242   }\r
2243 \r
2244   /* Make sure save files land in the right (?) directory */\r
2245   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2246     appData.saveGameFile = strdup(buf);\r
2247   }\r
2248   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2249     appData.savePositionFile = strdup(buf);\r
2250   }\r
2251 \r
2252   /* Finish initialization for fonts and sounds */\r
2253   for (i=0; i<NUM_FONTS; i++) {\r
2254     for (j=0; j<NUM_SIZES; j++) {\r
2255       CreateFontInMF(font[j][i]);\r
2256     }\r
2257   }\r
2258   /* xboard, and older WinBoards, controlled the move sound with the\r
2259      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2260      always turn the option on (so that the backend will call us),\r
2261      then let the user turn the sound off by setting it to silence if\r
2262      desired.  To accommodate old winboard.ini files saved by old\r
2263      versions of WinBoard, we also turn off the sound if the option\r
2264      was initially set to false. */\r
2265   if (!appData.ringBellAfterMoves) {\r
2266     sounds[(int)SoundMove].name = strdup("");\r
2267     appData.ringBellAfterMoves = TRUE;\r
2268   }\r
2269   GetCurrentDirectory(MSG_SIZ, currDir);\r
2270   SetCurrentDirectory(installDir);\r
2271   LoadAllSounds();\r
2272   SetCurrentDirectory(currDir);\r
2273 \r
2274   p = icsTextMenuString;\r
2275   if (p[0] == '@') {\r
2276     FILE* f = fopen(p + 1, "r");\r
2277     if (f == NULL) {\r
2278       DisplayFatalError(p + 1, errno, 2);\r
2279       return;\r
2280     }\r
2281     i = fread(buf, 1, sizeof(buf)-1, f);\r
2282     fclose(f);\r
2283     buf[i] = NULLCHAR;\r
2284     p = buf;\r
2285   }\r
2286   ParseIcsTextMenu(strdup(p));\r
2287 }\r
2288 \r
2289 \r
2290 VOID\r
2291 InitMenuChecks()\r
2292 {\r
2293   HMENU hmenu = GetMenu(hwndMain);\r
2294 \r
2295   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2296                         MF_BYCOMMAND|((appData.icsActive &&\r
2297                                        *appData.icsCommPort != NULLCHAR) ?\r
2298                                       MF_ENABLED : MF_GRAYED));\r
2299   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2300                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2301                                      MF_CHECKED : MF_UNCHECKED));\r
2302 }\r
2303 \r
2304 \r
2305 VOID\r
2306 SaveSettings(char* name)\r
2307 {\r
2308   FILE *f;\r
2309   ArgDescriptor *ad;\r
2310   WINDOWPLACEMENT wp;\r
2311   char dir[MSG_SIZ];\r
2312 \r
2313   if (!hwndMain) return;\r
2314 \r
2315   GetCurrentDirectory(MSG_SIZ, dir);\r
2316   SetCurrentDirectory(installDir);\r
2317   f = fopen(name, "w");\r
2318   SetCurrentDirectory(dir);\r
2319   if (f == NULL) {\r
2320     DisplayError(name, errno);\r
2321     return;\r
2322   }\r
2323   fprintf(f, ";\n");\r
2324   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2325   fprintf(f, ";\n");\r
2326   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2327   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2328   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2329   fprintf(f, ";\n");\r
2330 \r
2331   wp.length = sizeof(WINDOWPLACEMENT);\r
2332   GetWindowPlacement(hwndMain, &wp);\r
2333   boardX = wp.rcNormalPosition.left;\r
2334   boardY = wp.rcNormalPosition.top;\r
2335 \r
2336   if (hwndConsole) {\r
2337     GetWindowPlacement(hwndConsole, &wp);\r
2338     wpConsole.x = wp.rcNormalPosition.left;\r
2339     wpConsole.y = wp.rcNormalPosition.top;\r
2340     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2341     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2342   }\r
2343 \r
2344   if (analysisDialog) {\r
2345     GetWindowPlacement(analysisDialog, &wp);\r
2346     analysisX = wp.rcNormalPosition.left;\r
2347     analysisY = wp.rcNormalPosition.top;\r
2348     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2349     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2350   }\r
2351 \r
2352   if (commentDialog) {\r
2353     GetWindowPlacement(commentDialog, &wp);\r
2354     commentX = wp.rcNormalPosition.left;\r
2355     commentY = wp.rcNormalPosition.top;\r
2356     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2357     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2358   }\r
2359 \r
2360   if (editTagsDialog) {\r
2361     GetWindowPlacement(editTagsDialog, &wp);\r
2362     editTagsX = wp.rcNormalPosition.left;\r
2363     editTagsY = wp.rcNormalPosition.top;\r
2364     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2365     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2366   }\r
2367 \r
2368   if (gameListDialog) {\r
2369     GetWindowPlacement(gameListDialog, &wp);\r
2370     wpGameList.x = wp.rcNormalPosition.left;\r
2371     wpGameList.y = wp.rcNormalPosition.top;\r
2372     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2373     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2374   }\r
2375 \r
2376   /* [AS] Move history */\r
2377   wpMoveHistory.visible = MoveHistoryIsUp();\r
2378   \r
2379   if( moveHistoryDialog ) {\r
2380     GetWindowPlacement(moveHistoryDialog, &wp);\r
2381     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2382     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2383     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2384     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2385   }\r
2386 \r
2387   /* [AS] Eval graph */\r
2388   wpEvalGraph.visible = EvalGraphIsUp();\r
2389 \r
2390   if( evalGraphDialog ) {\r
2391     GetWindowPlacement(evalGraphDialog, &wp);\r
2392     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2393     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2394     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2395     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2396   }\r
2397 \r
2398   /* [AS] Engine output */\r
2399   wpEngineOutput.visible = EngineOutputIsUp();\r
2400 \r
2401   if( engineOutputDialog ) {\r
2402     GetWindowPlacement(engineOutputDialog, &wp);\r
2403     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2404     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2405     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2406     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2407   }\r
2408 \r
2409   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2410     if (!ad->save) continue;\r
2411     switch (ad->argType) {\r
2412     case ArgString:\r
2413       {\r
2414         char *p = *(char **)ad->argLoc;\r
2415         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2416           /* Quote multiline values or \-containing values\r
2417              with { } if possible */\r
2418           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2419         } else {\r
2420           /* Else quote with " " */\r
2421           fprintf(f, "/%s=\"", ad->argName);\r
2422           while (*p) {\r
2423             if (*p == '\n') fprintf(f, "\n");\r
2424             else if (*p == '\r') fprintf(f, "\\r");\r
2425             else if (*p == '\t') fprintf(f, "\\t");\r
2426             else if (*p == '\b') fprintf(f, "\\b");\r
2427             else if (*p == '\f') fprintf(f, "\\f");\r
2428             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2429             else if (*p == '\"') fprintf(f, "\\\"");\r
2430             else if (*p == '\\') fprintf(f, "\\\\");\r
2431             else putc(*p, f);\r
2432             p++;\r
2433           }\r
2434           fprintf(f, "\"\n");\r
2435         }\r
2436       }\r
2437       break;\r
2438     case ArgInt:\r
2439     case ArgZ:\r
2440       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2441       break;\r
2442     case ArgX:\r
2443       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2444       break;\r
2445     case ArgY:\r
2446       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2447       break;\r
2448     case ArgFloat:\r
2449       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2450       break;\r
2451     case ArgBoolean:\r
2452       fprintf(f, "/%s=%s\n", ad->argName, \r
2453         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2454       break;\r
2455     case ArgTrue:\r
2456       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2457       break;\r
2458     case ArgFalse:\r
2459       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2460       break;\r
2461     case ArgColor:\r
2462       {\r
2463         COLORREF color = *(COLORREF *)ad->argLoc;\r
2464         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2465           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2466       }\r
2467       break;\r
2468     case ArgAttribs:\r
2469       {\r
2470         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2471         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2472           (ta->effects & CFE_BOLD) ? "b" : "",\r
2473           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2474           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2475           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2476           (ta->effects) ? " " : "",\r
2477           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2478       }\r
2479       break;\r
2480     case ArgFilename:\r
2481       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2482         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2483       } else {\r
2484         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2485       }\r
2486       break;\r
2487     case ArgBoardSize:\r
2488       fprintf(f, "/%s=%s\n", ad->argName,\r
2489               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2490       break;\r
2491     case ArgFont:\r
2492       {\r
2493         int bs;\r
2494         for (bs=0; bs<NUM_SIZES; bs++) {\r
2495           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2496           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2497           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2498             ad->argName, mfp->faceName, mfp->pointSize,\r
2499             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2500             mfp->bold ? "b" : "",\r
2501             mfp->italic ? "i" : "",\r
2502             mfp->underline ? "u" : "",\r
2503             mfp->strikeout ? "s" : "");\r
2504         }\r
2505       }\r
2506       break;\r
2507     case ArgCommSettings:\r
2508       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2509     case ArgNone:\r
2510     case ArgSettingsFilename: ;\r
2511     }\r
2512   }\r
2513   fclose(f);\r
2514 }\r
2515 \r
2516 \r
2517 \r
2518 /*---------------------------------------------------------------------------*\\r
2519  *\r
2520  * GDI board drawing routines\r
2521  *\r
2522 \*---------------------------------------------------------------------------*/\r
2523 \r
2524 /* [AS] Draw square using background texture */\r
2525 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2526 {\r
2527     XFORM   x;\r
2528 \r
2529     if( mode == 0 ) {\r
2530         return; /* Should never happen! */\r
2531     }\r
2532 \r
2533     SetGraphicsMode( dst, GM_ADVANCED );\r
2534 \r
2535     switch( mode ) {\r
2536     case 1:\r
2537         /* Identity */\r
2538         break;\r
2539     case 2:\r
2540         /* X reflection */\r
2541         x.eM11 = -1.0;\r
2542         x.eM12 = 0;\r
2543         x.eM21 = 0;\r
2544         x.eM22 = 1.0;\r
2545         x.eDx = (FLOAT) dw + dx - 1;\r
2546         x.eDy = 0;\r
2547         dx = 0;\r
2548         SetWorldTransform( dst, &x );\r
2549         break;\r
2550     case 3:\r
2551         /* Y reflection */\r
2552         x.eM11 = 1.0;\r
2553         x.eM12 = 0;\r
2554         x.eM21 = 0;\r
2555         x.eM22 = -1.0;\r
2556         x.eDx = 0;\r
2557         x.eDy = (FLOAT) dh + dy - 1;\r
2558         dy = 0;\r
2559         SetWorldTransform( dst, &x );\r
2560         break;\r
2561     case 4:\r
2562         /* X/Y flip */\r
2563         x.eM11 = 0;\r
2564         x.eM12 = 1.0;\r
2565         x.eM21 = 1.0;\r
2566         x.eM22 = 0;\r
2567         x.eDx = (FLOAT) dx;\r
2568         x.eDy = (FLOAT) dy;\r
2569         dx = 0;\r
2570         dy = 0;\r
2571         SetWorldTransform( dst, &x );\r
2572         break;\r
2573     }\r
2574 \r
2575     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2576 \r
2577     x.eM11 = 1.0;\r
2578     x.eM12 = 0;\r
2579     x.eM21 = 0;\r
2580     x.eM22 = 1.0;\r
2581     x.eDx = 0;\r
2582     x.eDy = 0;\r
2583     SetWorldTransform( dst, &x );\r
2584 \r
2585     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2586 }\r
2587 \r
2588 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2589 enum {\r
2590     PM_WP = (int) WhitePawn, \r
2591     PM_WN = (int) WhiteKnight, \r
2592     PM_WB = (int) WhiteBishop, \r
2593     PM_WR = (int) WhiteRook, \r
2594     PM_WQ = (int) WhiteQueen, \r
2595     PM_WF = (int) WhiteFerz, \r
2596     PM_WW = (int) WhiteWazir, \r
2597     PM_WE = (int) WhiteAlfil, \r
2598     PM_WM = (int) WhiteMan, \r
2599     PM_WO = (int) WhiteCannon, \r
2600     PM_WU = (int) WhiteUnicorn, \r
2601     PM_WH = (int) WhiteNightrider, \r
2602     PM_WA = (int) WhiteAngel, \r
2603     PM_WC = (int) WhiteMarshall, \r
2604     PM_WAB = (int) WhiteCardinal, \r
2605     PM_WD = (int) WhiteDragon, \r
2606     PM_WL = (int) WhiteLance, \r
2607     PM_WS = (int) WhiteCobra, \r
2608     PM_WV = (int) WhiteFalcon, \r
2609     PM_WSG = (int) WhiteSilver, \r
2610     PM_WG = (int) WhiteGrasshopper, \r
2611     PM_WK = (int) WhiteKing,\r
2612     PM_BP = (int) BlackPawn, \r
2613     PM_BN = (int) BlackKnight, \r
2614     PM_BB = (int) BlackBishop, \r
2615     PM_BR = (int) BlackRook, \r
2616     PM_BQ = (int) BlackQueen, \r
2617     PM_BF = (int) BlackFerz, \r
2618     PM_BW = (int) BlackWazir, \r
2619     PM_BE = (int) BlackAlfil, \r
2620     PM_BM = (int) BlackMan,\r
2621     PM_BO = (int) BlackCannon, \r
2622     PM_BU = (int) BlackUnicorn, \r
2623     PM_BH = (int) BlackNightrider, \r
2624     PM_BA = (int) BlackAngel, \r
2625     PM_BC = (int) BlackMarshall, \r
2626     PM_BG = (int) BlackGrasshopper, \r
2627     PM_BAB = (int) BlackCardinal,\r
2628     PM_BD = (int) BlackDragon,\r
2629     PM_BL = (int) BlackLance,\r
2630     PM_BS = (int) BlackCobra,\r
2631     PM_BV = (int) BlackFalcon,\r
2632     PM_BSG = (int) BlackSilver,\r
2633     PM_BK = (int) BlackKing\r
2634 };\r
2635 \r
2636 static HFONT hPieceFont = NULL;\r
2637 static HBITMAP hPieceMask[(int) EmptySquare];\r
2638 static HBITMAP hPieceFace[(int) EmptySquare];\r
2639 static int fontBitmapSquareSize = 0;\r
2640 static char pieceToFontChar[(int) EmptySquare] =\r
2641                               { 'p', 'n', 'b', 'r', 'q', \r
2642                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2643                       'k', 'o', 'm', 'v', 't', 'w', \r
2644                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2645                                                               'l' };\r
2646 \r
2647 extern BOOL SetCharTable( char *table, const char * map );\r
2648 /* [HGM] moved to backend.c */\r
2649 \r
2650 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2651 {\r
2652     HBRUSH hbrush;\r
2653     BYTE r1 = GetRValue( color );\r
2654     BYTE g1 = GetGValue( color );\r
2655     BYTE b1 = GetBValue( color );\r
2656     BYTE r2 = r1 / 2;\r
2657     BYTE g2 = g1 / 2;\r
2658     BYTE b2 = b1 / 2;\r
2659     RECT rc;\r
2660 \r
2661     /* Create a uniform background first */\r
2662     hbrush = CreateSolidBrush( color );\r
2663     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2664     FillRect( hdc, &rc, hbrush );\r
2665     DeleteObject( hbrush );\r
2666     \r
2667     if( mode == 1 ) {\r
2668         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2669         int steps = squareSize / 2;\r
2670         int i;\r
2671 \r
2672         for( i=0; i<steps; i++ ) {\r
2673             BYTE r = r1 - (r1-r2) * i / steps;\r
2674             BYTE g = g1 - (g1-g2) * i / steps;\r
2675             BYTE b = b1 - (b1-b2) * i / steps;\r
2676 \r
2677             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2678             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2679             FillRect( hdc, &rc, hbrush );\r
2680             DeleteObject(hbrush);\r
2681         }\r
2682     }\r
2683     else if( mode == 2 ) {\r
2684         /* Diagonal gradient, good more or less for every piece */\r
2685         POINT triangle[3];\r
2686         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2687         HBRUSH hbrush_old;\r
2688         int steps = squareSize;\r
2689         int i;\r
2690 \r
2691         triangle[0].x = squareSize - steps;\r
2692         triangle[0].y = squareSize;\r
2693         triangle[1].x = squareSize;\r
2694         triangle[1].y = squareSize;\r
2695         triangle[2].x = squareSize;\r
2696         triangle[2].y = squareSize - steps;\r
2697 \r
2698         for( i=0; i<steps; i++ ) {\r
2699             BYTE r = r1 - (r1-r2) * i / steps;\r
2700             BYTE g = g1 - (g1-g2) * i / steps;\r
2701             BYTE b = b1 - (b1-b2) * i / steps;\r
2702 \r
2703             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2704             hbrush_old = SelectObject( hdc, hbrush );\r
2705             Polygon( hdc, triangle, 3 );\r
2706             SelectObject( hdc, hbrush_old );\r
2707             DeleteObject(hbrush);\r
2708             triangle[0].x++;\r
2709             triangle[2].y++;\r
2710         }\r
2711 \r
2712         SelectObject( hdc, hpen );\r
2713     }\r
2714 }\r
2715 \r
2716 /*\r
2717     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2718     seems to work ok. The main problem here is to find the "inside" of a chess\r
2719     piece: follow the steps as explained below.\r
2720 */\r
2721 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2722 {\r
2723     HBITMAP hbm;\r
2724     HBITMAP hbm_old;\r
2725     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2726     RECT rc;\r
2727     SIZE sz;\r
2728     POINT pt;\r
2729     int backColor = whitePieceColor; \r
2730     int foreColor = blackPieceColor;\r
2731     \r
2732     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2733         backColor = appData.fontBackColorWhite;\r
2734         foreColor = appData.fontForeColorWhite;\r
2735     }\r
2736     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2737         backColor = appData.fontBackColorBlack;\r
2738         foreColor = appData.fontForeColorBlack;\r
2739     }\r
2740 \r
2741     /* Mask */\r
2742     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2743 \r
2744     hbm_old = SelectObject( hdc, hbm );\r
2745 \r
2746     rc.left = 0;\r
2747     rc.top = 0;\r
2748     rc.right = squareSize;\r
2749     rc.bottom = squareSize;\r
2750 \r
2751     /* Step 1: background is now black */\r
2752     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2753 \r
2754     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2755 \r
2756     pt.x = (squareSize - sz.cx) / 2;\r
2757     pt.y = (squareSize - sz.cy) / 2;\r
2758 \r
2759     SetBkMode( hdc, TRANSPARENT );\r
2760     SetTextColor( hdc, chroma );\r
2761     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2762     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2763 \r
2764     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2765     /* Step 3: the area outside the piece is filled with white */\r
2766 //    FloodFill( hdc, 0, 0, chroma );\r
2767     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2768     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2769     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2770     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2771     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2772     /* \r
2773         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2774         but if the start point is not inside the piece we're lost!\r
2775         There should be a better way to do this... if we could create a region or path\r
2776         from the fill operation we would be fine for example.\r
2777     */\r
2778 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2779     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2780 \r
2781     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2782         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2783         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2784 \r
2785         SelectObject( dc2, bm2 );\r
2786         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2787         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2788         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2789         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2790         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2791 \r
2792         DeleteDC( dc2 );\r
2793         DeleteObject( bm2 );\r
2794     }\r
2795 \r
2796     SetTextColor( hdc, 0 );\r
2797     /* \r
2798         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2799         draw the piece again in black for safety.\r
2800     */\r
2801     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2802 \r
2803     SelectObject( hdc, hbm_old );\r
2804 \r
2805     if( hPieceMask[index] != NULL ) {\r
2806         DeleteObject( hPieceMask[index] );\r
2807     }\r
2808 \r
2809     hPieceMask[index] = hbm;\r
2810 \r
2811     /* Face */\r
2812     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2813 \r
2814     SelectObject( hdc, hbm );\r
2815 \r
2816     {\r
2817         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2818         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2819         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2820 \r
2821         SelectObject( dc1, hPieceMask[index] );\r
2822         SelectObject( dc2, bm2 );\r
2823         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2824         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2825         \r
2826         /* \r
2827             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2828             the piece background and deletes (makes transparent) the rest.\r
2829             Thanks to that mask, we are free to paint the background with the greates\r
2830             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2831             We use this, to make gradients and give the pieces a "roundish" look.\r
2832         */\r
2833         SetPieceBackground( hdc, backColor, 2 );\r
2834         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2835 \r
2836         DeleteDC( dc2 );\r
2837         DeleteDC( dc1 );\r
2838         DeleteObject( bm2 );\r
2839     }\r
2840 \r
2841     SetTextColor( hdc, foreColor );\r
2842     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2843 \r
2844     SelectObject( hdc, hbm_old );\r
2845 \r
2846     if( hPieceFace[index] != NULL ) {\r
2847         DeleteObject( hPieceFace[index] );\r
2848     }\r
2849 \r
2850     hPieceFace[index] = hbm;\r
2851 }\r
2852 \r
2853 static int TranslatePieceToFontPiece( int piece )\r
2854 {\r
2855     switch( piece ) {\r
2856     case BlackPawn:\r
2857         return PM_BP;\r
2858     case BlackKnight:\r
2859         return PM_BN;\r
2860     case BlackBishop:\r
2861         return PM_BB;\r
2862     case BlackRook:\r
2863         return PM_BR;\r
2864     case BlackQueen:\r
2865         return PM_BQ;\r
2866     case BlackKing:\r
2867         return PM_BK;\r
2868     case WhitePawn:\r
2869         return PM_WP;\r
2870     case WhiteKnight:\r
2871         return PM_WN;\r
2872     case WhiteBishop:\r
2873         return PM_WB;\r
2874     case WhiteRook:\r
2875         return PM_WR;\r
2876     case WhiteQueen:\r
2877         return PM_WQ;\r
2878     case WhiteKing:\r
2879         return PM_WK;\r
2880 \r
2881     case BlackAngel:\r
2882         return PM_BA;\r
2883     case BlackMarshall:\r
2884         return PM_BC;\r
2885     case BlackFerz:\r
2886         return PM_BF;\r
2887     case BlackNightrider:\r
2888         return PM_BH;\r
2889     case BlackAlfil:\r
2890         return PM_BE;\r
2891     case BlackWazir:\r
2892         return PM_BW;\r
2893     case BlackUnicorn:\r
2894         return PM_BU;\r
2895     case BlackCannon:\r
2896         return PM_BO;\r
2897     case BlackGrasshopper:\r
2898         return PM_BG;\r
2899     case BlackMan:\r
2900         return PM_BM;\r
2901     case BlackSilver:\r
2902         return PM_BSG;\r
2903     case BlackLance:\r
2904         return PM_BL;\r
2905     case BlackFalcon:\r
2906         return PM_BV;\r
2907     case BlackCobra:\r
2908         return PM_BS;\r
2909     case BlackCardinal:\r
2910         return PM_BAB;\r
2911     case BlackDragon:\r
2912         return PM_BD;\r
2913 \r
2914     case WhiteAngel:\r
2915         return PM_WA;\r
2916     case WhiteMarshall:\r
2917         return PM_WC;\r
2918     case WhiteFerz:\r
2919         return PM_WF;\r
2920     case WhiteNightrider:\r
2921         return PM_WH;\r
2922     case WhiteAlfil:\r
2923         return PM_WE;\r
2924     case WhiteWazir:\r
2925         return PM_WW;\r
2926     case WhiteUnicorn:\r
2927         return PM_WU;\r
2928     case WhiteCannon:\r
2929         return PM_WO;\r
2930     case WhiteGrasshopper:\r
2931         return PM_WG;\r
2932     case WhiteMan:\r
2933         return PM_WM;\r
2934     case WhiteSilver:\r
2935         return PM_WSG;\r
2936     case WhiteLance:\r
2937         return PM_WL;\r
2938     case WhiteFalcon:\r
2939         return PM_WV;\r
2940     case WhiteCobra:\r
2941         return PM_WS;\r
2942     case WhiteCardinal:\r
2943         return PM_WAB;\r
2944     case WhiteDragon:\r
2945         return PM_WD;\r
2946     }\r
2947 \r
2948     return 0;\r
2949 }\r
2950 \r
2951 void CreatePiecesFromFont()\r
2952 {\r
2953     LOGFONT lf;\r
2954     HDC hdc_window = NULL;\r
2955     HDC hdc = NULL;\r
2956     HFONT hfont_old;\r
2957     int fontHeight;\r
2958     int i;\r
2959 \r
2960     if( fontBitmapSquareSize < 0 ) {\r
2961         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2962         return;\r
2963     }\r
2964 \r
2965     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2966         fontBitmapSquareSize = -1;\r
2967         return;\r
2968     }\r
2969 \r
2970     if( fontBitmapSquareSize != squareSize ) {\r
2971         hdc_window = GetDC( hwndMain );\r
2972         hdc = CreateCompatibleDC( hdc_window );\r
2973 \r
2974         if( hPieceFont != NULL ) {\r
2975             DeleteObject( hPieceFont );\r
2976         }\r
2977         else {\r
2978             for( i=0; i<=(int)BlackKing; i++ ) {\r
2979                 hPieceMask[i] = NULL;\r
2980                 hPieceFace[i] = NULL;\r
2981             }\r
2982         }\r
2983 \r
2984         fontHeight = 75;\r
2985 \r
2986         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2987             fontHeight = appData.fontPieceSize;\r
2988         }\r
2989 \r
2990         fontHeight = (fontHeight * squareSize) / 100;\r
2991 \r
2992         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2993         lf.lfWidth = 0;\r
2994         lf.lfEscapement = 0;\r
2995         lf.lfOrientation = 0;\r
2996         lf.lfWeight = FW_NORMAL;\r
2997         lf.lfItalic = 0;\r
2998         lf.lfUnderline = 0;\r
2999         lf.lfStrikeOut = 0;\r
3000         lf.lfCharSet = DEFAULT_CHARSET;\r
3001         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
3002         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
3003         lf.lfQuality = PROOF_QUALITY;\r
3004         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3005         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3006         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3007 \r
3008         hPieceFont = CreateFontIndirect( &lf );\r
3009 \r
3010         if( hPieceFont == NULL ) {\r
3011             fontBitmapSquareSize = -2;\r
3012         }\r
3013         else {\r
3014             /* Setup font-to-piece character table */\r
3015             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3016                 /* No (or wrong) global settings, try to detect the font */\r
3017                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3018                     /* Alpha */\r
3019                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3020                 }\r
3021                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3022                     /* DiagramTT* family */\r
3023                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3024                 }\r
3025                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3026                     /* Fairy symbols */\r
3027                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3028                 }\r
3029                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3030                     /* Good Companion (Some characters get warped as literal :-( */\r
3031                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3032                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3033                     SetCharTable(pieceToFontChar, s);\r
3034                 }\r
3035                 else {\r
3036                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3037                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3038                 }\r
3039             }\r
3040 \r
3041             /* Create bitmaps */\r
3042             hfont_old = SelectObject( hdc, hPieceFont );\r
3043 #if 0\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3056 \r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3070             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3071             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3072             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3073             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3074             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3075             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3076             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3077             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3078             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3079             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3080             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3081             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3082             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3083             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3084             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3085             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3086             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3087             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3088             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3089 #else\r
3090             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3091                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3092                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3093 #endif\r
3094             SelectObject( hdc, hfont_old );\r
3095 \r
3096             fontBitmapSquareSize = squareSize;\r
3097         }\r
3098     }\r
3099 \r
3100     if( hdc != NULL ) {\r
3101         DeleteDC( hdc );\r
3102     }\r
3103 \r
3104     if( hdc_window != NULL ) {\r
3105         ReleaseDC( hwndMain, hdc_window );\r
3106     }\r
3107 }\r
3108 \r
3109 HBITMAP\r
3110 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3111 {\r
3112   char name[128];\r
3113 \r
3114   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3115   if (gameInfo.event &&\r
3116       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3117       strcmp(name, "k80s") == 0) {\r
3118     strcpy(name, "tim");\r
3119   }\r
3120   return LoadBitmap(hinst, name);\r
3121 }\r
3122 \r
3123 \r
3124 /* Insert a color into the program's logical palette\r
3125    structure.  This code assumes the given color is\r
3126    the result of the RGB or PALETTERGB macro, and it\r
3127    knows how those macros work (which is documented).\r
3128 */\r
3129 VOID\r
3130 InsertInPalette(COLORREF color)\r
3131 {\r
3132   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3133 \r
3134   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3135     DisplayFatalError("Too many colors", 0, 1);\r
3136     pLogPal->palNumEntries--;\r
3137     return;\r
3138   }\r
3139 \r
3140   pe->peFlags = (char) 0;\r
3141   pe->peRed = (char) (0xFF & color);\r
3142   pe->peGreen = (char) (0xFF & (color >> 8));\r
3143   pe->peBlue = (char) (0xFF & (color >> 16));\r
3144   return;\r
3145 }\r
3146 \r
3147 \r
3148 VOID\r
3149 InitDrawingColors()\r
3150 {\r
3151   if (pLogPal == NULL) {\r
3152     /* Allocate enough memory for a logical palette with\r
3153      * PALETTESIZE entries and set the size and version fields\r
3154      * of the logical palette structure.\r
3155      */\r
3156     pLogPal = (NPLOGPALETTE)\r
3157       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3158                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3159     pLogPal->palVersion    = 0x300;\r
3160   }\r
3161   pLogPal->palNumEntries = 0;\r
3162 \r
3163   InsertInPalette(lightSquareColor);\r
3164   InsertInPalette(darkSquareColor);\r
3165   InsertInPalette(whitePieceColor);\r
3166   InsertInPalette(blackPieceColor);\r
3167   InsertInPalette(highlightSquareColor);\r
3168   InsertInPalette(premoveHighlightColor);\r
3169 \r
3170   /*  create a logical color palette according the information\r
3171    *  in the LOGPALETTE structure.\r
3172    */\r
3173   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3174 \r
3175   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3176   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3177   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3178   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3179   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3180   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3181   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3182   /* [AS] Force rendering of the font-based pieces */\r
3183   if( fontBitmapSquareSize > 0 ) {\r
3184     fontBitmapSquareSize = 0;\r
3185   }\r
3186 }\r
3187 \r
3188 \r
3189 int\r
3190 BoardWidth(int boardSize, int n)\r
3191 { /* [HGM] argument n added to allow different width and height */\r
3192   int lineGap = sizeInfo[boardSize].lineGap;\r
3193 \r
3194   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3195       lineGap = appData.overrideLineGap;\r
3196   }\r
3197 \r
3198   return (n + 1) * lineGap +\r
3199           n * sizeInfo[boardSize].squareSize;\r
3200 }\r
3201 \r
3202 /* Respond to board resize by dragging edge */\r
3203 VOID\r
3204 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3205 {\r
3206   BoardSize newSize = NUM_SIZES - 1;\r
3207   static int recurse = 0;\r
3208   if (IsIconic(hwndMain)) return;\r
3209   if (recurse > 0) return;\r
3210   recurse++;\r
3211   while (newSize > 0) {\r
3212         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3213         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3214            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3215     newSize--;\r
3216   } \r
3217   boardSize = newSize;\r
3218   InitDrawingSizes(boardSize, flags);\r
3219   recurse--;\r
3220 }\r
3221 \r
3222 \r
3223 \r
3224 VOID\r
3225 InitDrawingSizes(BoardSize boardSize, int flags)\r
3226 {\r
3227   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3228   ChessSquare piece;\r
3229   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3230   HDC hdc;\r
3231   SIZE clockSize, messageSize;\r
3232   HFONT oldFont;\r
3233   char buf[MSG_SIZ];\r
3234   char *str;\r
3235   HMENU hmenu = GetMenu(hwndMain);\r
3236   RECT crect, wrect, oldRect;\r
3237   int offby;\r
3238   LOGBRUSH logbrush;\r
3239 \r
3240   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3241   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3242 \r
3243   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3244   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3245 \r
3246   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3247   oldRect.top = boardY;\r
3248   oldRect.right = boardX + winWidth;\r
3249   oldRect.bottom = boardY + winHeight;\r
3250 \r
3251   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3252   smallLayout = sizeInfo[boardSize].smallLayout;\r
3253   squareSize = sizeInfo[boardSize].squareSize;\r
3254   lineGap = sizeInfo[boardSize].lineGap;\r
3255   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3256 \r
3257   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3258       lineGap = appData.overrideLineGap;\r
3259   }\r
3260 \r
3261   if (tinyLayout != oldTinyLayout) {\r
3262     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3263     if (tinyLayout) {\r
3264       style &= ~WS_SYSMENU;\r
3265       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3266                  "&Minimize\tCtrl+F4");\r
3267     } else {\r
3268       style |= WS_SYSMENU;\r
3269       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3270     }\r
3271     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3272 \r
3273     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3274       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3275         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3276     }\r
3277     DrawMenuBar(hwndMain);\r
3278   }\r
3279 \r
3280   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3281   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3282 \r
3283   /* Get text area sizes */\r
3284   hdc = GetDC(hwndMain);\r
3285   if (appData.clockMode) {\r
3286     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3287   } else {\r
3288     sprintf(buf, "White");\r
3289   }\r
3290   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3291   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3292   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3293   str = "We only care about the height here";\r
3294   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3295   SelectObject(hdc, oldFont);\r
3296   ReleaseDC(hwndMain, hdc);\r
3297 \r
3298   /* Compute where everything goes */\r
3299   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3300         /* [HGM] logo: if either logo is on, reserve space for it */\r
3301         logoHeight =  2*clockSize.cy;\r
3302         leftLogoRect.left   = OUTER_MARGIN;\r
3303         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3304         leftLogoRect.top    = OUTER_MARGIN;\r
3305         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3306 \r
3307         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3308         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3309         rightLogoRect.top    = OUTER_MARGIN;\r
3310         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3311 \r
3312 \r
3313     whiteRect.left = leftLogoRect.right;\r
3314     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3315     whiteRect.top = OUTER_MARGIN;\r
3316     whiteRect.bottom = whiteRect.top + logoHeight;\r
3317 \r
3318     blackRect.right = rightLogoRect.left;\r
3319     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3320     blackRect.top = whiteRect.top;\r
3321     blackRect.bottom = whiteRect.bottom;\r
3322   } else {\r
3323     whiteRect.left = OUTER_MARGIN;\r
3324     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3325     whiteRect.top = OUTER_MARGIN;\r
3326     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3327 \r
3328     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3329     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3330     blackRect.top = whiteRect.top;\r
3331     blackRect.bottom = whiteRect.bottom;\r
3332   }\r
3333 \r
3334   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3335   if (appData.showButtonBar) {\r
3336     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3337       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3338   } else {\r
3339     messageRect.right = OUTER_MARGIN + boardWidth;\r
3340   }\r
3341   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3342   messageRect.bottom = messageRect.top + messageSize.cy;\r
3343 \r
3344   boardRect.left = OUTER_MARGIN;\r
3345   boardRect.right = boardRect.left + boardWidth;\r
3346   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3347   boardRect.bottom = boardRect.top + boardHeight;\r
3348 \r
3349   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3350   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3351   oldBoardSize = boardSize;\r
3352   oldTinyLayout = tinyLayout;\r
3353   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3354   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3355     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3356   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3357   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3358   winHeight = winH; //       without disturbing window attachments\r
3359   GetWindowRect(hwndMain, &wrect);\r
3360   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3361                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3362 \r
3363   // [HGM] placement: let attached windows follow size change.\r
3364   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3365   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3366   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3367   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3368   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3369 \r
3370   /* compensate if menu bar wrapped */\r
3371   GetClientRect(hwndMain, &crect);\r
3372   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3373   winHeight += offby;\r
3374   switch (flags) {\r
3375   case WMSZ_TOPLEFT:\r
3376     SetWindowPos(hwndMain, NULL, \r
3377                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3378                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3379     break;\r
3380 \r
3381   case WMSZ_TOPRIGHT:\r
3382   case WMSZ_TOP:\r
3383     SetWindowPos(hwndMain, NULL, \r
3384                  wrect.left, wrect.bottom - winHeight, \r
3385                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3386     break;\r
3387 \r
3388   case WMSZ_BOTTOMLEFT:\r
3389   case WMSZ_LEFT:\r
3390     SetWindowPos(hwndMain, NULL, \r
3391                  wrect.right - winWidth, wrect.top, \r
3392                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3393     break;\r
3394 \r
3395   case WMSZ_BOTTOMRIGHT:\r
3396   case WMSZ_BOTTOM:\r
3397   case WMSZ_RIGHT:\r
3398   default:\r
3399     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3400                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3401     break;\r
3402   }\r
3403 \r
3404   hwndPause = NULL;\r
3405   for (i = 0; i < N_BUTTONS; i++) {\r
3406     if (buttonDesc[i].hwnd != NULL) {\r
3407       DestroyWindow(buttonDesc[i].hwnd);\r
3408       buttonDesc[i].hwnd = NULL;\r
3409     }\r
3410     if (appData.showButtonBar) {\r
3411       buttonDesc[i].hwnd =\r
3412         CreateWindow("BUTTON", buttonDesc[i].label,\r
3413                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3414                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3415                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3416                      (HMENU) buttonDesc[i].id,\r
3417                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3418       if (tinyLayout) {\r
3419         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3420                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3421                     MAKELPARAM(FALSE, 0));\r
3422       }\r
3423       if (buttonDesc[i].id == IDM_Pause)\r
3424         hwndPause = buttonDesc[i].hwnd;\r
3425       buttonDesc[i].wndproc = (WNDPROC)\r
3426         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3427     }\r
3428   }\r
3429   if (gridPen != NULL) DeleteObject(gridPen);\r
3430   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3431   if (premovePen != NULL) DeleteObject(premovePen);\r
3432   if (lineGap != 0) {\r
3433     logbrush.lbStyle = BS_SOLID;\r
3434     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3435     gridPen =\r
3436       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3437                    lineGap, &logbrush, 0, NULL);\r
3438     logbrush.lbColor = highlightSquareColor;\r
3439     highlightPen =\r
3440       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3441                    lineGap, &logbrush, 0, NULL);\r
3442 \r
3443     logbrush.lbColor = premoveHighlightColor; \r
3444     premovePen =\r
3445       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3446                    lineGap, &logbrush, 0, NULL);\r
3447 \r
3448     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3449     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3450       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3451       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3452         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3453       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3454         BOARD_WIDTH * (squareSize + lineGap);\r
3455       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3456     }\r
3457     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3458       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3459       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3460         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3461         lineGap / 2 + (i * (squareSize + lineGap));\r
3462       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3463         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3464       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3465     }\r
3466   }\r
3467 \r
3468   /* [HGM] Licensing requirement */\r
3469 #ifdef GOTHIC\r
3470   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3471 #endif\r
3472 #ifdef FALCON\r
3473   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3474 #endif\r
3475   GothicPopUp( "", VariantNormal);\r
3476 \r
3477 \r
3478 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3479 \r
3480   /* Load piece bitmaps for this board size */\r
3481   for (i=0; i<=2; i++) {\r
3482     for (piece = WhitePawn;\r
3483          (int) piece < (int) BlackPawn;\r
3484          piece = (ChessSquare) ((int) piece + 1)) {\r
3485       if (pieceBitmap[i][piece] != NULL)\r
3486         DeleteObject(pieceBitmap[i][piece]);\r
3487     }\r
3488   }\r
3489 \r
3490   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3491   // Orthodox Chess pieces\r
3492   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3493   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3494   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3495   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3496   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3497   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3498   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3499   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3500   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3501   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3502   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3503   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3504   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3505   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3506   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3507   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3508     // in Shogi, Hijack the unused Queen for Lance\r
3509     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3510     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3511     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3512   } else {\r
3513     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3514     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3515     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3516   }\r
3517 \r
3518   if(squareSize <= 72 && squareSize >= 33) { \r
3519     /* A & C are available in most sizes now */\r
3520     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3521       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3522       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3523       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3524       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3525       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3526       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3527       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3528       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3529       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3530       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3531       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3532       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3533     } else { // Smirf-like\r
3534       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3535       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3536       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3537     }\r
3538     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3539       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3540       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3541       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3542     } else { // WinBoard standard\r
3543       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3544       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3545       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3546     }\r
3547   }\r
3548 \r
3549 \r
3550   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3551     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3552     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3553     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3554     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3555     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3556     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3557     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3558     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3559     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3560     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3561     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3562     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3563     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3564     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3565     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3566     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3567     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3568     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3569     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3570     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3571     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3572     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3573     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3574     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3575     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3576     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3577     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3578     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3579     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3580     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3581 \r
3582     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3583       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3584       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3585       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3586       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3587       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3588       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3589       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3590       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3591       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3592       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3593       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3594       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3595     } else {\r
3596       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3597       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3598       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3599       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3600       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3601       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3602       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3603       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3604       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3605       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3606       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3607       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3608     }\r
3609 \r
3610   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3611     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3612     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3613     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3614     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3615     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3616     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3617     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3618     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3619     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3620     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3621     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3622     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3623     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3624     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3625   }\r
3626 \r
3627 \r
3628   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3629   /* special Shogi support in this size */\r
3630   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3631       for (piece = WhitePawn;\r
3632            (int) piece < (int) BlackPawn;\r
3633            piece = (ChessSquare) ((int) piece + 1)) {\r
3634         if (pieceBitmap[i][piece] != NULL)\r
3635           DeleteObject(pieceBitmap[i][piece]);\r
3636       }\r
3637     }\r
3638   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3639   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3640   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3641   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3642   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3643   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3644   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3645   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3646   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3647   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3648   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3649   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3650   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3651   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3652   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3653   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3654   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3655   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3656   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3657   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3658   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3659   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3660   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3661   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3662   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3663   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3664   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3665   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3666   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3667   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3668   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3669   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3670   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3671   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3672   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3673   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3674   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3675   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3676   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3677   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3678   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3679   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3680   minorSize = 0;\r
3681   }\r
3682 }\r
3683 \r
3684 HBITMAP\r
3685 PieceBitmap(ChessSquare p, int kind)\r
3686 {\r
3687   if ((int) p >= (int) BlackPawn)\r
3688     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3689 \r
3690   return pieceBitmap[kind][(int) p];\r
3691 }\r
3692 \r
3693 /***************************************************************/\r
3694 \r
3695 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3696 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3697 /*\r
3698 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3699 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3700 */\r
3701 \r
3702 VOID\r
3703 SquareToPos(int row, int column, int * x, int * y)\r
3704 {\r
3705   if (flipView) {\r
3706     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3707     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3708   } else {\r
3709     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3710     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3711   }\r
3712 }\r
3713 \r
3714 VOID\r
3715 DrawCoordsOnDC(HDC hdc)\r
3716 {\r
3717   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
3718   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
3719   char str[2] = { NULLCHAR, NULLCHAR };\r
3720   int oldMode, oldAlign, x, y, start, i;\r
3721   HFONT oldFont;\r
3722   HBRUSH oldBrush;\r
3723 \r
3724   if (!appData.showCoords)\r
3725     return;\r
3726 \r
3727   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3728 \r
3729   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3730   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3731   oldAlign = GetTextAlign(hdc);\r
3732   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3733 \r
3734   y = boardRect.top + lineGap;\r
3735   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3736 \r
3737   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3738   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3739     str[0] = files[start + i];\r
3740     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3741     y += squareSize + lineGap;\r
3742   }\r
3743 \r
3744   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3745 \r
3746   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3747   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3748     str[0] = ranks[start + i];\r
3749     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3750     x += squareSize + lineGap;\r
3751   }    \r
3752 \r
3753   SelectObject(hdc, oldBrush);\r
3754   SetBkMode(hdc, oldMode);\r
3755   SetTextAlign(hdc, oldAlign);\r
3756   SelectObject(hdc, oldFont);\r
3757 }\r
3758 \r
3759 VOID\r
3760 DrawGridOnDC(HDC hdc)\r
3761 {\r
3762   HPEN oldPen;\r
3763  \r
3764   if (lineGap != 0) {\r
3765     oldPen = SelectObject(hdc, gridPen);\r
3766     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3767     SelectObject(hdc, oldPen);\r
3768   }\r
3769 }\r
3770 \r
3771 #define HIGHLIGHT_PEN 0\r
3772 #define PREMOVE_PEN   1\r
3773 \r
3774 VOID\r
3775 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3776 {\r
3777   int x1, y1;\r
3778   HPEN oldPen, hPen;\r
3779   if (lineGap == 0) return;\r
3780   if (flipView) {\r
3781     x1 = boardRect.left +\r
3782       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3783     y1 = boardRect.top +\r
3784       lineGap/2 + y * (squareSize + lineGap);\r
3785   } else {\r
3786     x1 = boardRect.left +\r
3787       lineGap/2 + x * (squareSize + lineGap);\r
3788     y1 = boardRect.top +\r
3789       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3790   }\r
3791   hPen = pen ? premovePen : highlightPen;\r
3792   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3793   MoveToEx(hdc, x1, y1, NULL);\r
3794   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3795   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3796   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3797   LineTo(hdc, x1, y1);\r
3798   SelectObject(hdc, oldPen);\r
3799 }\r
3800 \r
3801 VOID\r
3802 DrawHighlightsOnDC(HDC hdc)\r
3803 {\r
3804   int i;\r
3805   for (i=0; i<2; i++) {\r
3806     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3807       DrawHighlightOnDC(hdc, TRUE,\r
3808                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3809                         HIGHLIGHT_PEN);\r
3810   }\r
3811   for (i=0; i<2; i++) {\r
3812     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3813         premoveHighlightInfo.sq[i].y >= 0) {\r
3814         DrawHighlightOnDC(hdc, TRUE,\r
3815                           premoveHighlightInfo.sq[i].x, \r
3816                           premoveHighlightInfo.sq[i].y,\r
3817                           PREMOVE_PEN);\r
3818     }\r
3819   }\r
3820 }\r
3821 \r
3822 /* Note: sqcolor is used only in monoMode */\r
3823 /* Note that this code is largely duplicated in woptions.c,\r
3824    function DrawSampleSquare, so that needs to be updated too */\r
3825 VOID\r
3826 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3827 {\r
3828   HBITMAP oldBitmap;\r
3829   HBRUSH oldBrush;\r
3830   int tmpSize;\r
3831 \r
3832   if (appData.blindfold) return;\r
3833 \r
3834   /* [AS] Use font-based pieces if needed */\r
3835   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3836     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3837     CreatePiecesFromFont();\r
3838 \r
3839     if( fontBitmapSquareSize == squareSize ) {\r
3840         int index = TranslatePieceToFontPiece(piece);\r
3841 \r
3842         SelectObject( tmphdc, hPieceMask[ index ] );\r
3843 \r
3844         BitBlt( hdc,\r
3845             x, y,\r
3846             squareSize, squareSize,\r
3847             tmphdc,\r
3848             0, 0,\r
3849             SRCAND );\r
3850 \r
3851         SelectObject( tmphdc, hPieceFace[ index ] );\r
3852 \r
3853         BitBlt( hdc,\r
3854             x, y,\r
3855             squareSize, squareSize,\r
3856             tmphdc,\r
3857             0, 0,\r
3858             SRCPAINT );\r
3859 \r
3860         return;\r
3861     }\r
3862   }\r
3863 \r
3864   if (appData.monoMode) {\r
3865     SelectObject(tmphdc, PieceBitmap(piece, \r
3866       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3867     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3868            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3869   } else {\r
3870     tmpSize = squareSize;\r
3871     if(minorSize &&\r
3872         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3873          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3874       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3875       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3876       x += (squareSize - minorSize)>>1;\r
3877       y += squareSize - minorSize - 2;\r
3878       tmpSize = minorSize;\r
3879     }\r
3880     if (color || appData.allWhite ) {\r
3881       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3882       if( color )\r
3883               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3884       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3885       if(appData.upsideDown && color==flipView)\r
3886         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3887       else\r
3888         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3889 #if 0\r
3890       /* Use black piece color for outline of white pieces */\r
3891       /* Not sure this looks really good (though xboard does it).\r
3892          Maybe better to have another selectable color, default black */\r
3893       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3894       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3895       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3896 #else\r
3897       /* Use black for outline of white pieces */\r
3898       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3899       if(appData.upsideDown && color==flipView)\r
3900         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3901       else\r
3902         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3903 #endif\r
3904     } else {\r
3905 #if 0\r
3906       /* Use white piece color for details of black pieces */\r
3907       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3908          WHITE_PIECE ones aren't always the right shape. */\r
3909       /* Not sure this looks really good (though xboard does it).\r
3910          Maybe better to have another selectable color, default medium gray? */\r
3911       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3912       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3913       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3914       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3915       SelectObject(hdc, blackPieceBrush);\r
3916       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3917 #else\r
3918       /* Use square color for details of black pieces */\r
3919       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3920       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3921       if(appData.upsideDown && !flipView)\r
3922         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3923       else\r
3924         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3925 #endif\r
3926     }\r
3927     SelectObject(hdc, oldBrush);\r
3928     SelectObject(tmphdc, oldBitmap);\r
3929   }\r
3930 }\r
3931 \r
3932 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3933 int GetBackTextureMode( int algo )\r
3934 {\r
3935     int result = BACK_TEXTURE_MODE_DISABLED;\r
3936 \r
3937     switch( algo ) \r
3938     {\r
3939         case BACK_TEXTURE_MODE_PLAIN:\r
3940             result = 1; /* Always use identity map */\r
3941             break;\r
3942         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3943             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3944             break;\r
3945     }\r
3946 \r
3947     return result;\r
3948 }\r
3949 \r
3950 /* \r
3951     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3952     to handle redraws cleanly (as random numbers would always be different).\r
3953 */\r
3954 VOID RebuildTextureSquareInfo()\r
3955 {\r
3956     BITMAP bi;\r
3957     int lite_w = 0;\r
3958     int lite_h = 0;\r
3959     int dark_w = 0;\r
3960     int dark_h = 0;\r
3961     int row;\r
3962     int col;\r
3963 \r
3964     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3965 \r
3966     if( liteBackTexture != NULL ) {\r
3967         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3968             lite_w = bi.bmWidth;\r
3969             lite_h = bi.bmHeight;\r
3970         }\r
3971     }\r
3972 \r
3973     if( darkBackTexture != NULL ) {\r
3974         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3975             dark_w = bi.bmWidth;\r
3976             dark_h = bi.bmHeight;\r
3977         }\r
3978     }\r
3979 \r
3980     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3981         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3982             if( (col + row) & 1 ) {\r
3983                 /* Lite square */\r
3984                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3985                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3986                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3987                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3988                 }\r
3989             }\r
3990             else {\r
3991                 /* Dark square */\r
3992                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3993                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3994                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3995                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3996                 }\r
3997             }\r
3998         }\r
3999     }\r
4000 }\r
4001 \r
4002 /* [AS] Arrow highlighting support */\r
4003 \r
4004 static int A_WIDTH = 5; /* Width of arrow body */\r
4005 \r
4006 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
4007 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
4008 \r
4009 static double Sqr( double x )\r
4010 {\r
4011     return x*x;\r
4012 }\r
4013 \r
4014 static int Round( double x )\r
4015 {\r
4016     return (int) (x + 0.5);\r
4017 }\r
4018 \r
4019 /* Draw an arrow between two points using current settings */\r
4020 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4021 {\r
4022     POINT arrow[7];\r
4023     double dx, dy, j, k, x, y;\r
4024 \r
4025     if( d_x == s_x ) {\r
4026         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4027 \r
4028         arrow[0].x = s_x + A_WIDTH;\r
4029         arrow[0].y = s_y;\r
4030 \r
4031         arrow[1].x = s_x + A_WIDTH;\r
4032         arrow[1].y = d_y - h;\r
4033 \r
4034         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4035         arrow[2].y = d_y - h;\r
4036 \r
4037         arrow[3].x = d_x;\r
4038         arrow[3].y = d_y;\r
4039 \r
4040         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4041         arrow[4].y = d_y - h;\r
4042 \r
4043         arrow[5].x = s_x - A_WIDTH;\r
4044         arrow[5].y = d_y - h;\r
4045 \r
4046         arrow[6].x = s_x - A_WIDTH;\r
4047         arrow[6].y = s_y;\r
4048     }\r
4049     else if( d_y == s_y ) {\r
4050         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4051 \r
4052         arrow[0].x = s_x;\r
4053         arrow[0].y = s_y + A_WIDTH;\r
4054 \r
4055         arrow[1].x = d_x - w;\r
4056         arrow[1].y = s_y + A_WIDTH;\r
4057 \r
4058         arrow[2].x = d_x - w;\r
4059         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4060 \r
4061         arrow[3].x = d_x;\r
4062         arrow[3].y = d_y;\r
4063 \r
4064         arrow[4].x = d_x - w;\r
4065         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4066 \r
4067         arrow[5].x = d_x - w;\r
4068         arrow[5].y = s_y - A_WIDTH;\r
4069 \r
4070         arrow[6].x = s_x;\r
4071         arrow[6].y = s_y - A_WIDTH;\r
4072     }\r
4073     else {\r
4074         /* [AS] Needed a lot of paper for this! :-) */\r
4075         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4076         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4077   \r
4078         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4079 \r
4080         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4081 \r
4082         x = s_x;\r
4083         y = s_y;\r
4084 \r
4085         arrow[0].x = Round(x - j);\r
4086         arrow[0].y = Round(y + j*dx);\r
4087 \r
4088         arrow[1].x = Round(x + j);\r
4089         arrow[1].y = Round(y - j*dx);\r
4090 \r
4091         if( d_x > s_x ) {\r
4092             x = (double) d_x - k;\r
4093             y = (double) d_y - k*dy;\r
4094         }\r
4095         else {\r
4096             x = (double) d_x + k;\r
4097             y = (double) d_y + k*dy;\r
4098         }\r
4099 \r
4100         arrow[2].x = Round(x + j);\r
4101         arrow[2].y = Round(y - j*dx);\r
4102 \r
4103         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4104         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4105 \r
4106         arrow[4].x = d_x;\r
4107         arrow[4].y = d_y;\r
4108 \r
4109         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4110         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4111 \r
4112         arrow[6].x = Round(x - j);\r
4113         arrow[6].y = Round(y + j*dx);\r
4114     }\r
4115 \r
4116     Polygon( hdc, arrow, 7 );\r
4117 }\r
4118 \r
4119 /* [AS] Draw an arrow between two squares */\r
4120 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4121 {\r
4122     int s_x, s_y, d_x, d_y;\r
4123     HPEN hpen;\r
4124     HPEN holdpen;\r
4125     HBRUSH hbrush;\r
4126     HBRUSH holdbrush;\r
4127     LOGBRUSH stLB;\r
4128 \r
4129     if( s_col == d_col && s_row == d_row ) {\r
4130         return;\r
4131     }\r
4132 \r
4133     /* Get source and destination points */\r
4134     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4135     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4136 \r
4137     if( d_y > s_y ) {\r
4138         d_y += squareSize / 4;\r
4139     }\r
4140     else if( d_y < s_y ) {\r
4141         d_y += 3 * squareSize / 4;\r
4142     }\r
4143     else {\r
4144         d_y += squareSize / 2;\r
4145     }\r
4146 \r
4147     if( d_x > s_x ) {\r
4148         d_x += squareSize / 4;\r
4149     }\r
4150     else if( d_x < s_x ) {\r
4151         d_x += 3 * squareSize / 4;\r
4152     }\r
4153     else {\r
4154         d_x += squareSize / 2;\r
4155     }\r
4156 \r
4157     s_x += squareSize / 2;\r
4158     s_y += squareSize / 2;\r
4159 \r
4160     /* Adjust width */\r
4161     A_WIDTH = squareSize / 14;\r
4162 \r
4163     /* Draw */\r
4164     stLB.lbStyle = BS_SOLID;\r
4165     stLB.lbColor = appData.highlightArrowColor;\r
4166     stLB.lbHatch = 0;\r
4167 \r
4168     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4169     holdpen = SelectObject( hdc, hpen );\r
4170     hbrush = CreateBrushIndirect( &stLB );\r
4171     holdbrush = SelectObject( hdc, hbrush );\r
4172 \r
4173     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4174 \r
4175     SelectObject( hdc, holdpen );\r
4176     SelectObject( hdc, holdbrush );\r
4177     DeleteObject( hpen );\r
4178     DeleteObject( hbrush );\r
4179 }\r
4180 \r
4181 BOOL HasHighlightInfo()\r
4182 {\r
4183     BOOL result = FALSE;\r
4184 \r
4185     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4186         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4187     {\r
4188         result = TRUE;\r
4189     }\r
4190 \r
4191     return result;\r
4192 }\r
4193 \r
4194 BOOL IsDrawArrowEnabled()\r
4195 {\r
4196     BOOL result = FALSE;\r
4197 \r
4198     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4199         result = TRUE;\r
4200     }\r
4201 \r
4202     return result;\r
4203 }\r
4204 \r
4205 VOID DrawArrowHighlight( HDC hdc )\r
4206 {\r
4207     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4208         DrawArrowBetweenSquares( hdc,\r
4209             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4210             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4211     }\r
4212 }\r
4213 \r
4214 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4215 {\r
4216     HRGN result = NULL;\r
4217 \r
4218     if( HasHighlightInfo() ) {\r
4219         int x1, y1, x2, y2;\r
4220         int sx, sy, dx, dy;\r
4221 \r
4222         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4223         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4224 \r
4225         sx = MIN( x1, x2 );\r
4226         sy = MIN( y1, y2 );\r
4227         dx = MAX( x1, x2 ) + squareSize;\r
4228         dy = MAX( y1, y2 ) + squareSize;\r
4229 \r
4230         result = CreateRectRgn( sx, sy, dx, dy );\r
4231     }\r
4232 \r
4233     return result;\r
4234 }\r
4235 \r
4236 /*\r
4237     Warning: this function modifies the behavior of several other functions. \r
4238     \r
4239     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4240     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4241     repaint is scattered all over the place, which is not good for features such as\r
4242     "arrow highlighting" that require a full repaint of the board.\r
4243 \r
4244     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4245     user interaction, when speed is not so important) but especially to avoid errors\r
4246     in the displayed graphics.\r
4247 \r
4248     In such patched places, I always try refer to this function so there is a single\r
4249     place to maintain knowledge.\r
4250     \r
4251     To restore the original behavior, just return FALSE unconditionally.\r
4252 */\r
4253 BOOL IsFullRepaintPreferrable()\r
4254 {\r
4255     BOOL result = FALSE;\r
4256 \r
4257     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4258         /* Arrow may appear on the board */\r
4259         result = TRUE;\r
4260     }\r
4261 \r
4262     return result;\r
4263 }\r
4264 \r
4265 /* \r
4266     This function is called by DrawPosition to know whether a full repaint must\r
4267     be forced or not.\r
4268 \r
4269     Only DrawPosition may directly call this function, which makes use of \r
4270     some state information. Other function should call DrawPosition specifying \r
4271     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4272 */\r
4273 BOOL DrawPositionNeedsFullRepaint()\r
4274 {\r
4275     BOOL result = FALSE;\r
4276 \r
4277     /* \r
4278         Probably a slightly better policy would be to trigger a full repaint\r
4279         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4280         but animation is fast enough that it's difficult to notice.\r
4281     */\r
4282     if( animInfo.piece == EmptySquare ) {\r
4283         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4284             result = TRUE;\r
4285         }\r
4286     }\r
4287 \r
4288     return result;\r
4289 }\r
4290 \r
4291 VOID\r
4292 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4293 {\r
4294   int row, column, x, y, square_color, piece_color;\r
4295   ChessSquare piece;\r
4296   HBRUSH oldBrush;\r
4297   HDC texture_hdc = NULL;\r
4298 \r
4299   /* [AS] Initialize background textures if needed */\r
4300   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4301       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4302       if( backTextureSquareSize != squareSize \r
4303        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4304           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4305           backTextureSquareSize = squareSize;\r
4306           RebuildTextureSquareInfo();\r
4307       }\r
4308 \r
4309       texture_hdc = CreateCompatibleDC( hdc );\r
4310   }\r
4311 \r
4312   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4313     for (column = 0; column < BOARD_WIDTH; column++) {\r
4314   \r
4315       SquareToPos(row, column, &x, &y);\r
4316 \r
4317       piece = board[row][column];\r
4318 \r
4319       square_color = ((column + row) % 2) == 1;\r
4320       if( gameInfo.variant == VariantXiangqi ) {\r
4321           square_color = !InPalace(row, column);\r
4322           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4323           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4324       }\r
4325       piece_color = (int) piece < (int) BlackPawn;\r
4326 \r
4327 \r
4328       /* [HGM] holdings file: light square or black */\r
4329       if(column == BOARD_LEFT-2) {\r
4330             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4331                 square_color = 1;\r
4332             else {\r
4333                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4334                 continue;\r
4335             }\r
4336       } else\r
4337       if(column == BOARD_RGHT + 1 ) {\r
4338             if( row < gameInfo.holdingsSize )\r
4339                 square_color = 1;\r
4340             else {\r
4341                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4342                 continue;\r
4343             }\r
4344       }\r
4345       if(column == BOARD_LEFT-1 ) /* left align */\r
4346             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4347       else if( column == BOARD_RGHT) /* right align */\r
4348             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4349       else\r
4350       if (appData.monoMode) {\r
4351         if (piece == EmptySquare) {\r
4352           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4353                  square_color ? WHITENESS : BLACKNESS);\r
4354         } else {\r
4355           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4356         }\r
4357       } \r
4358       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4359           /* [AS] Draw the square using a texture bitmap */\r
4360           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4361           int r = row, c = column; // [HGM] do not flip board in flipView\r
4362           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4363 \r
4364           DrawTile( x, y, \r
4365               squareSize, squareSize, \r
4366               hdc, \r
4367               texture_hdc,\r
4368               backTextureSquareInfo[r][c].mode,\r
4369               backTextureSquareInfo[r][c].x,\r
4370               backTextureSquareInfo[r][c].y );\r
4371 \r
4372           SelectObject( texture_hdc, hbm );\r
4373 \r
4374           if (piece != EmptySquare) {\r
4375               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4376           }\r
4377       }\r
4378       else {\r
4379         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4380 \r
4381         oldBrush = SelectObject(hdc, brush );\r
4382         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4383         SelectObject(hdc, oldBrush);\r
4384         if (piece != EmptySquare)\r
4385           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4386       }\r
4387     }\r
4388   }\r
4389 \r
4390   if( texture_hdc != NULL ) {\r
4391     DeleteDC( texture_hdc );\r
4392   }\r
4393 }\r
4394 \r
4395 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4396 void fputDW(FILE *f, int x)\r
4397 {\r
4398         fputc(x     & 255, f);\r
4399         fputc(x>>8  & 255, f);\r
4400         fputc(x>>16 & 255, f);\r
4401         fputc(x>>24 & 255, f);\r
4402 }\r
4403 \r
4404 #define MAX_CLIPS 200   /* more than enough */\r
4405 \r
4406 VOID\r
4407 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4408 {\r
4409 //  HBITMAP bufferBitmap;\r
4410   BITMAP bi;\r
4411 //  RECT Rect;\r
4412   HDC tmphdc;\r
4413   HBITMAP hbm;\r
4414   int w = 100, h = 50;\r
4415 \r
4416   if(logo == NULL) return;\r
4417 //  GetClientRect(hwndMain, &Rect);\r
4418 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4419 //                                      Rect.bottom-Rect.top+1);\r
4420   tmphdc = CreateCompatibleDC(hdc);\r
4421   hbm = SelectObject(tmphdc, logo);\r
4422   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4423             w = bi.bmWidth;\r
4424             h = bi.bmHeight;\r
4425   }\r
4426   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4427                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4428   SelectObject(tmphdc, hbm);\r
4429   DeleteDC(tmphdc);\r
4430 }\r
4431 \r
4432 VOID\r
4433 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4434 {\r
4435   static Board lastReq, lastDrawn;\r
4436   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4437   static int lastDrawnFlipView = 0;\r
4438   static int lastReqValid = 0, lastDrawnValid = 0;\r
4439   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4440   HDC tmphdc;\r
4441   HDC hdcmem;\r
4442   HBITMAP bufferBitmap;\r
4443   HBITMAP oldBitmap;\r
4444   RECT Rect;\r
4445   HRGN clips[MAX_CLIPS];\r
4446   ChessSquare dragged_piece = EmptySquare;\r
4447 \r
4448   /* I'm undecided on this - this function figures out whether a full\r
4449    * repaint is necessary on its own, so there's no real reason to have the\r
4450    * caller tell it that.  I think this can safely be set to FALSE - but\r
4451    * if we trust the callers not to request full repaints unnessesarily, then\r
4452    * we could skip some clipping work.  In other words, only request a full\r
4453    * redraw when the majority of pieces have changed positions (ie. flip, \r
4454    * gamestart and similar)  --Hawk\r
4455    */\r
4456   Boolean fullrepaint = repaint;\r
4457 \r
4458   if( DrawPositionNeedsFullRepaint() ) {\r
4459       fullrepaint = TRUE;\r
4460   }\r
4461 \r
4462 #if 0\r
4463   if( fullrepaint ) {\r
4464       static int repaint_count = 0;\r
4465       char buf[128];\r
4466 \r
4467       repaint_count++;\r
4468       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4469       OutputDebugString( buf );\r
4470   }\r
4471 #endif\r
4472 \r
4473   if (board == NULL) {\r
4474     if (!lastReqValid) {\r
4475       return;\r
4476     }\r
4477     board = lastReq;\r
4478   } else {\r
4479     CopyBoard(lastReq, board);\r
4480     lastReqValid = 1;\r
4481   }\r
4482 \r
4483   if (doingSizing) {\r
4484     return;\r
4485   }\r
4486 \r
4487   if (IsIconic(hwndMain)) {\r
4488     return;\r
4489   }\r
4490 \r
4491   if (hdc == NULL) {\r
4492     hdc = GetDC(hwndMain);\r
4493     if (!appData.monoMode) {\r
4494       SelectPalette(hdc, hPal, FALSE);\r
4495       RealizePalette(hdc);\r
4496     }\r
4497     releaseDC = TRUE;\r
4498   } else {\r
4499     releaseDC = FALSE;\r
4500   }\r
4501 \r
4502 #if 0\r
4503   fprintf(debugFP, "*******************************\n"\r
4504                    "repaint = %s\n"\r
4505                    "dragInfo.from (%d,%d)\n"\r
4506                    "dragInfo.start (%d,%d)\n"\r
4507                    "dragInfo.pos (%d,%d)\n"\r
4508                    "dragInfo.lastpos (%d,%d)\n", \r
4509                     repaint ? "TRUE" : "FALSE",\r
4510                     dragInfo.from.x, dragInfo.from.y, \r
4511                     dragInfo.start.x, dragInfo.start.y,\r
4512                     dragInfo.pos.x, dragInfo.pos.y,\r
4513                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4514   fprintf(debugFP, "prev:  ");\r
4515   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4516     for (column = 0; column < BOARD_WIDTH; column++) {\r
4517       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4518     }\r
4519   }\r
4520   fprintf(debugFP, "\n");\r
4521   fprintf(debugFP, "board: ");\r
4522   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4523     for (column = 0; column < BOARD_WIDTH; column++) {\r
4524       fprintf(debugFP, "%d ", board[row][column]);\r
4525     }\r
4526   }\r
4527   fprintf(debugFP, "\n");\r
4528   fflush(debugFP);\r
4529 #endif\r
4530 \r
4531   /* Create some work-DCs */\r
4532   hdcmem = CreateCompatibleDC(hdc);\r
4533   tmphdc = CreateCompatibleDC(hdc);\r
4534 \r
4535   /* If dragging is in progress, we temporarely remove the piece */\r
4536   /* [HGM] or temporarily decrease count if stacked              */\r
4537   /*       !! Moved to before board compare !!                   */\r
4538   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4539     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4540     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4541             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4542         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4543     } else \r
4544     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4545             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4546         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4547     } else \r
4548         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4549   }\r
4550 \r
4551   /* Figure out which squares need updating by comparing the \r
4552    * newest board with the last drawn board and checking if\r
4553    * flipping has changed.\r
4554    */\r
4555   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4556     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4557       for (column = 0; column < BOARD_WIDTH; column++) {\r
4558         if (lastDrawn[row][column] != board[row][column]) {\r
4559           SquareToPos(row, column, &x, &y);\r
4560           clips[num_clips++] =\r
4561             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4562         }\r
4563       }\r
4564     }\r
4565     for (i=0; i<2; i++) {\r
4566       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4567           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4568         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4569             lastDrawnHighlight.sq[i].y >= 0) {\r
4570           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4571                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4572           clips[num_clips++] =\r
4573             CreateRectRgn(x - lineGap, y - lineGap, \r
4574                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4575         }\r
4576         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4577           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4578           clips[num_clips++] =\r
4579             CreateRectRgn(x - lineGap, y - lineGap, \r
4580                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4581         }\r
4582       }\r
4583     }\r
4584     for (i=0; i<2; i++) {\r
4585       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4586           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4587         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4588             lastDrawnPremove.sq[i].y >= 0) {\r
4589           SquareToPos(lastDrawnPremove.sq[i].y,\r
4590                       lastDrawnPremove.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         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4596             premoveHighlightInfo.sq[i].y >= 0) {\r
4597           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4598                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4599           clips[num_clips++] =\r
4600             CreateRectRgn(x - lineGap, y - lineGap, \r
4601                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4602         }\r
4603       }\r
4604     }\r
4605   } else {\r
4606     fullrepaint = TRUE;\r
4607   }\r
4608 \r
4609   /* Create a buffer bitmap - this is the actual bitmap\r
4610    * being written to.  When all the work is done, we can\r
4611    * copy it to the real DC (the screen).  This avoids\r
4612    * the problems with flickering.\r
4613    */\r
4614   GetClientRect(hwndMain, &Rect);\r
4615   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4616                                         Rect.bottom-Rect.top+1);\r
4617   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4618   if (!appData.monoMode) {\r
4619     SelectPalette(hdcmem, hPal, FALSE);\r
4620   }\r
4621 \r
4622   /* Create clips for dragging */\r
4623   if (!fullrepaint) {\r
4624     if (dragInfo.from.x >= 0) {\r
4625       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4626       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4627     }\r
4628     if (dragInfo.start.x >= 0) {\r
4629       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4630       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4631     }\r
4632     if (dragInfo.pos.x >= 0) {\r
4633       x = dragInfo.pos.x - squareSize / 2;\r
4634       y = dragInfo.pos.y - squareSize / 2;\r
4635       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4636     }\r
4637     if (dragInfo.lastpos.x >= 0) {\r
4638       x = dragInfo.lastpos.x - squareSize / 2;\r
4639       y = dragInfo.lastpos.y - squareSize / 2;\r
4640       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4641     }\r
4642   }\r
4643 \r
4644   /* Are we animating a move?  \r
4645    * If so, \r
4646    *   - remove the piece from the board (temporarely)\r
4647    *   - calculate the clipping region\r
4648    */\r
4649   if (!fullrepaint) {\r
4650     if (animInfo.piece != EmptySquare) {\r
4651       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4652       x = boardRect.left + animInfo.lastpos.x;\r
4653       y = boardRect.top + animInfo.lastpos.y;\r
4654       x2 = boardRect.left + animInfo.pos.x;\r
4655       y2 = boardRect.top + animInfo.pos.y;\r
4656       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4657       /* Slight kludge.  The real problem is that after AnimateMove is\r
4658          done, the position on the screen does not match lastDrawn.\r
4659          This currently causes trouble only on e.p. captures in\r
4660          atomic, where the piece moves to an empty square and then\r
4661          explodes.  The old and new positions both had an empty square\r
4662          at the destination, but animation has drawn a piece there and\r
4663          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4664       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4665     }\r
4666   }\r
4667 \r
4668   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4669   if (num_clips == 0)\r
4670     fullrepaint = TRUE;\r
4671 \r
4672   /* Set clipping on the memory DC */\r
4673   if (!fullrepaint) {\r
4674     SelectClipRgn(hdcmem, clips[0]);\r
4675     for (x = 1; x < num_clips; x++) {\r
4676       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4677         abort();  // this should never ever happen!\r
4678     }\r
4679   }\r
4680 \r
4681   /* Do all the drawing to the memory DC */\r
4682   if(explodeInfo.radius) { // [HGM] atomic\r
4683         HBRUSH oldBrush;\r
4684         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4685         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4686         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4687         x += squareSize/2;\r
4688         y += squareSize/2;\r
4689         if(!fullrepaint) {\r
4690           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4691           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4692         }\r
4693         DrawGridOnDC(hdcmem);\r
4694         DrawHighlightsOnDC(hdcmem);\r
4695         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4696         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4697         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4698         SelectObject(hdcmem, oldBrush);\r
4699   } else {\r
4700     DrawGridOnDC(hdcmem);\r
4701     DrawHighlightsOnDC(hdcmem);\r
4702     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4703   }\r
4704   if(logoHeight) {\r
4705         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4706         if(appData.autoLogo) {\r
4707           \r
4708           switch(gameMode) { // pick logos based on game mode\r
4709             case IcsObserving:\r
4710                 whiteLogo = second.programLogo; // ICS logo\r
4711                 blackLogo = second.programLogo;\r
4712             default:\r
4713                 break;\r
4714             case IcsPlayingWhite:\r
4715                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4716                 blackLogo = second.programLogo; // ICS logo\r
4717                 break;\r
4718             case IcsPlayingBlack:\r
4719                 whiteLogo = second.programLogo; // ICS logo\r
4720                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4721                 break;\r
4722             case TwoMachinesPlay:\r
4723                 if(first.twoMachinesColor[0] == 'b') {\r
4724                     whiteLogo = second.programLogo;\r
4725                     blackLogo = first.programLogo;\r
4726                 }\r
4727                 break;\r
4728             case MachinePlaysWhite:\r
4729                 blackLogo = userLogo;\r
4730                 break;\r
4731             case MachinePlaysBlack:\r
4732                 whiteLogo = userLogo;\r
4733                 blackLogo = first.programLogo;\r
4734           }\r
4735         }\r
4736         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4737         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4738   }\r
4739 \r
4740   if( appData.highlightMoveWithArrow ) {\r
4741     DrawArrowHighlight(hdcmem);\r
4742   }\r
4743 \r
4744   DrawCoordsOnDC(hdcmem);\r
4745 \r
4746   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4747                  /* to make sure lastDrawn contains what is actually drawn */\r
4748 \r
4749   /* Put the dragged piece back into place and draw it (out of place!) */\r
4750     if (dragged_piece != EmptySquare) {\r
4751     /* [HGM] or restack */\r
4752     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4753                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4754     else\r
4755     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4756                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4757     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4758     x = dragInfo.pos.x - squareSize / 2;\r
4759     y = dragInfo.pos.y - squareSize / 2;\r
4760     DrawPieceOnDC(hdcmem, dragged_piece,\r
4761                   ((int) dragged_piece < (int) BlackPawn), \r
4762                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4763   }   \r
4764   \r
4765   /* Put the animated piece back into place and draw it */\r
4766   if (animInfo.piece != EmptySquare) {\r
4767     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4768     x = boardRect.left + animInfo.pos.x;\r
4769     y = boardRect.top + animInfo.pos.y;\r
4770     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4771                   ((int) animInfo.piece < (int) BlackPawn),\r
4772                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4773   }\r
4774 \r
4775   /* Release the bufferBitmap by selecting in the old bitmap \r
4776    * and delete the memory DC\r
4777    */\r
4778   SelectObject(hdcmem, oldBitmap);\r
4779   DeleteDC(hdcmem);\r
4780 \r
4781   /* Set clipping on the target DC */\r
4782   if (!fullrepaint) {\r
4783     SelectClipRgn(hdc, clips[0]);\r
4784     for (x = 1; x < num_clips; x++) {\r
4785       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4786         abort();   // this should never ever happen!\r
4787     } \r
4788   }\r
4789 \r
4790   /* Copy the new bitmap onto the screen in one go.\r
4791    * This way we avoid any flickering\r
4792    */\r
4793   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4794   BitBlt(hdc, boardRect.left, boardRect.top,\r
4795          boardRect.right - boardRect.left,\r
4796          boardRect.bottom - boardRect.top,\r
4797          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4798   if(saveDiagFlag) { \r
4799     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4800     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4801 \r
4802     GetObject(bufferBitmap, sizeof(b), &b);\r
4803     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4804         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4805         bih.biWidth = b.bmWidth;\r
4806         bih.biHeight = b.bmHeight;\r
4807         bih.biPlanes = 1;\r
4808         bih.biBitCount = b.bmBitsPixel;\r
4809         bih.biCompression = 0;\r
4810         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4811         bih.biXPelsPerMeter = 0;\r
4812         bih.biYPelsPerMeter = 0;\r
4813         bih.biClrUsed = 0;\r
4814         bih.biClrImportant = 0;\r
4815 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4816 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4817         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4818 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4819 \r
4820 #if 1\r
4821         wb = b.bmWidthBytes;\r
4822         // count colors\r
4823         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4824                 int k = ((int*) pData)[i];\r
4825                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4826                 if(j >= 16) break;\r
4827                 color[j] = k;\r
4828                 if(j >= nrColors) nrColors = j+1;\r
4829         }\r
4830         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4831                 INT p = 0;\r
4832                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4833                     for(w=0; w<(wb>>2); w+=2) {\r
4834                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4835                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4836                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4837                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4838                         pData[p++] = m | j<<4;\r
4839                     }\r
4840                     while(p&3) pData[p++] = 0;\r
4841                 }\r
4842                 fac = 3;\r
4843                 wb = ((wb+31)>>5)<<2;\r
4844         }\r
4845         // write BITMAPFILEHEADER\r
4846         fprintf(diagFile, "BM");\r
4847         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4848         fputDW(diagFile, 0);\r
4849         fputDW(diagFile, 0x36 + (fac?64:0));\r
4850         // write BITMAPINFOHEADER\r
4851         fputDW(diagFile, 40);\r
4852         fputDW(diagFile, b.bmWidth);\r
4853         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4854         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4855         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4856         fputDW(diagFile, 0);\r
4857         fputDW(diagFile, 0);\r
4858         fputDW(diagFile, 0);\r
4859         fputDW(diagFile, 0);\r
4860         fputDW(diagFile, 0);\r
4861         fputDW(diagFile, 0);\r
4862         // write color table\r
4863         if(fac)\r
4864         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4865         // write bitmap data\r
4866         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4867                 fputc(pData[i], diagFile);\r
4868 #endif\r
4869      }\r
4870   }\r
4871 \r
4872   SelectObject(tmphdc, oldBitmap);\r
4873 \r
4874   /* Massive cleanup */\r
4875   for (x = 0; x < num_clips; x++)\r
4876     DeleteObject(clips[x]);\r
4877 \r
4878   DeleteDC(tmphdc);\r
4879   DeleteObject(bufferBitmap);\r
4880 \r
4881   if (releaseDC) \r
4882     ReleaseDC(hwndMain, hdc);\r
4883   \r
4884   if (lastDrawnFlipView != flipView) {\r
4885     if (flipView)\r
4886       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4887     else\r
4888       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4889   }\r
4890 \r
4891 /*  CopyBoard(lastDrawn, board);*/\r
4892   lastDrawnHighlight = highlightInfo;\r
4893   lastDrawnPremove   = premoveHighlightInfo;\r
4894   lastDrawnFlipView = flipView;\r
4895   lastDrawnValid = 1;\r
4896 }\r
4897 \r
4898 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4899 int\r
4900 SaveDiagram(f)\r
4901      FILE *f;\r
4902 {\r
4903     saveDiagFlag = 1; diagFile = f;\r
4904     HDCDrawPosition(NULL, TRUE, NULL);\r
4905 \r
4906     saveDiagFlag = 0;\r
4907 \r
4908 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4909     \r
4910     fclose(f);\r
4911     return TRUE;\r
4912 }\r
4913 \r
4914 \r
4915 /*---------------------------------------------------------------------------*\\r
4916 | CLIENT PAINT PROCEDURE\r
4917 |   This is the main event-handler for the WM_PAINT message.\r
4918 |\r
4919 \*---------------------------------------------------------------------------*/\r
4920 VOID\r
4921 PaintProc(HWND hwnd)\r
4922 {\r
4923   HDC         hdc;\r
4924   PAINTSTRUCT ps;\r
4925   HFONT       oldFont;\r
4926 \r
4927   if((hdc = BeginPaint(hwnd, &ps))) {\r
4928     if (IsIconic(hwnd)) {\r
4929       DrawIcon(hdc, 2, 2, iconCurrent);\r
4930     } else {\r
4931       if (!appData.monoMode) {\r
4932         SelectPalette(hdc, hPal, FALSE);\r
4933         RealizePalette(hdc);\r
4934       }\r
4935       HDCDrawPosition(hdc, 1, NULL);\r
4936       oldFont =\r
4937         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4938       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4939                  ETO_CLIPPED|ETO_OPAQUE,\r
4940                  &messageRect, messageText, strlen(messageText), NULL);\r
4941       SelectObject(hdc, oldFont);\r
4942       DisplayBothClocks();\r
4943     }\r
4944     EndPaint(hwnd,&ps);\r
4945   }\r
4946 \r
4947   return;\r
4948 }\r
4949 \r
4950 \r
4951 /*\r
4952  * If the user selects on a border boundary, return -1; if off the board,\r
4953  *   return -2.  Otherwise map the event coordinate to the square.\r
4954  * The offset boardRect.left or boardRect.top must already have been\r
4955  *   subtracted from x.\r
4956  */\r
4957 int\r
4958 EventToSquare(int x)\r
4959 {\r
4960   if (x <= 0)\r
4961     return -2;\r
4962   if (x < lineGap)\r
4963     return -1;\r
4964   x -= lineGap;\r
4965   if ((x % (squareSize + lineGap)) >= squareSize)\r
4966     return -1;\r
4967   x /= (squareSize + lineGap);\r
4968   if (x >= BOARD_SIZE)\r
4969     return -2;\r
4970   return x;\r
4971 }\r
4972 \r
4973 typedef struct {\r
4974   char piece;\r
4975   int command;\r
4976   char* name;\r
4977 } DropEnable;\r
4978 \r
4979 DropEnable dropEnables[] = {\r
4980   { 'P', DP_Pawn, "Pawn" },\r
4981   { 'N', DP_Knight, "Knight" },\r
4982   { 'B', DP_Bishop, "Bishop" },\r
4983   { 'R', DP_Rook, "Rook" },\r
4984   { 'Q', DP_Queen, "Queen" },\r
4985 };\r
4986 \r
4987 VOID\r
4988 SetupDropMenu(HMENU hmenu)\r
4989 {\r
4990   int i, count, enable;\r
4991   char *p;\r
4992   extern char white_holding[], black_holding[];\r
4993   char item[MSG_SIZ];\r
4994 \r
4995   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4996     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4997                dropEnables[i].piece);\r
4998     count = 0;\r
4999     while (p && *p++ == dropEnables[i].piece) count++;\r
5000     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
5001     enable = count > 0 || !appData.testLegality\r
5002       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
5003                       && !appData.icsActive);\r
5004     ModifyMenu(hmenu, dropEnables[i].command,\r
5005                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
5006                dropEnables[i].command, item);\r
5007   }\r
5008 }\r
5009 \r
5010 /* Event handler for mouse messages */\r
5011 VOID\r
5012 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5013 {\r
5014   int x, y;\r
5015   POINT pt;\r
5016   static int recursive = 0;\r
5017   HMENU hmenu;\r
5018 //  BOOLEAN needsRedraw = FALSE;\r
5019   BOOLEAN saveAnimate;\r
5020   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5021   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5022   ChessMove moveType;\r
5023 \r
5024   if (recursive) {\r
5025     if (message == WM_MBUTTONUP) {\r
5026       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5027          to the middle button: we simulate pressing the left button too!\r
5028          */\r
5029       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5030       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5031     }\r
5032     return;\r
5033   }\r
5034   recursive++;\r
5035   \r
5036   pt.x = LOWORD(lParam);\r
5037   pt.y = HIWORD(lParam);\r
5038   x = EventToSquare(pt.x - boardRect.left);\r
5039   y = EventToSquare(pt.y - boardRect.top);\r
5040   if (!flipView && y >= 0) {\r
5041     y = BOARD_HEIGHT - 1 - y;\r
5042   }\r
5043   if (flipView && x >= 0) {\r
5044     x = BOARD_WIDTH - 1 - x;\r
5045   }\r
5046 \r
5047   switch (message) {\r
5048   case WM_LBUTTONDOWN:\r
5049     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5050         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5051         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5052         if(gameInfo.holdingsWidth && \r
5053                 (WhiteOnMove(currentMove) \r
5054                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5055                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5056             // click in right holdings, for determining promotion piece\r
5057             ChessSquare p = boards[currentMove][y][x];\r
5058             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5059             if(p != EmptySquare) {\r
5060                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5061                 fromX = fromY = -1;\r
5062                 break;\r
5063             }\r
5064         }\r
5065         DrawPosition(FALSE, boards[currentMove]);\r
5066         break;\r
5067     }\r
5068     ErrorPopDown();\r
5069     sameAgain = FALSE;\r
5070     if (y == -2) {\r
5071       /* Downclick vertically off board; check if on clock */\r
5072       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5073         if (gameMode == EditPosition) {\r
5074           SetWhiteToPlayEvent();\r
5075         } else if (gameMode == IcsPlayingBlack ||\r
5076                    gameMode == MachinePlaysWhite) {\r
5077           CallFlagEvent();\r
5078         } else if (gameMode == EditGame) {\r
5079           AdjustClock(flipClock, -1);\r
5080         }\r
5081       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5082         if (gameMode == EditPosition) {\r
5083           SetBlackToPlayEvent();\r
5084         } else if (gameMode == IcsPlayingWhite ||\r
5085                    gameMode == MachinePlaysBlack) {\r
5086           CallFlagEvent();\r
5087         } else if (gameMode == EditGame) {\r
5088           AdjustClock(!flipClock, -1);\r
5089         }\r
5090       }\r
5091       if (!appData.highlightLastMove) {\r
5092         ClearHighlights();\r
5093         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5094       }\r
5095       fromX = fromY = -1;\r
5096       dragInfo.start.x = dragInfo.start.y = -1;\r
5097       dragInfo.from = dragInfo.start;\r
5098       break;\r
5099     } else if (x < 0 || y < 0\r
5100       /* [HGM] block clicks between board and holdings */\r
5101               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5102               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5103               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5104         /* EditPosition, empty square, or different color piece;\r
5105            click-click move is possible */\r
5106                                ) {\r
5107       break;\r
5108     } else if (fromX == x && fromY == y) {\r
5109       /* Downclick on same square again */\r
5110       ClearHighlights();\r
5111       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5112       sameAgain = TRUE;  \r
5113     } else if (fromX != -1 &&\r
5114                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5115                                                                         ) {\r
5116       /* Downclick on different square. */\r
5117       /* [HGM] if on holdings file, should count as new first click ! */\r
5118       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5119         toX = x;\r
5120         toY = y;\r
5121         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5122            to make sure move is legal before showing promotion popup */\r
5123         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5124         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5125                 fromX = fromY = -1; \r
5126                 ClearHighlights();\r
5127                 DrawPosition(FALSE, boards[currentMove]);\r
5128                 break; \r
5129         } else \r
5130         if(moveType != ImpossibleMove) {\r
5131           if(moveType == IllegalMove) {\r
5132                 ;\r
5133           } else\r
5134           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5135           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5136             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5137               appData.alwaysPromoteToQueen)) {\r
5138                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5139                   if (!appData.highlightLastMove) {\r
5140                       ClearHighlights();\r
5141                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5142                   }\r
5143           } else\r
5144           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5145                   SetHighlights(fromX, fromY, toX, toY);\r
5146                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5147                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5148                      If promotion to Q is legal, all are legal! */\r
5149                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5150                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5151                     // kludge to temporarily execute move on display, without promoting yet\r
5152                     promotionChoice = TRUE;\r
5153                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5154                     boards[currentMove][toY][toX] = p;\r
5155                     DrawPosition(FALSE, boards[currentMove]);\r
5156                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5157                     boards[currentMove][toY][toX] = q;\r
5158                   } else\r
5159                   PromotionPopup(hwnd);\r
5160           } else {       /* not a promotion */\r
5161              if (appData.animate || appData.highlightLastMove) {\r
5162                  SetHighlights(fromX, fromY, toX, toY);\r
5163              } else {\r
5164                  ClearHighlights();\r
5165              }\r
5166              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5167              if (appData.animate && !appData.highlightLastMove) {\r
5168                   ClearHighlights();\r
5169                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5170              }\r
5171           }\r
5172           fromX = fromY = -1;\r
5173           break;\r
5174         }\r
5175         if (gotPremove) {\r
5176             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5177             DrawPosition(forceFullRepaint || FALSE, NULL);\r
5178         } else ClearHighlights();\r
5179         fromX = fromY = -1;\r
5180         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5181     }\r
5182     /* First downclick, or restart on a square with same color piece */\r
5183     if (!frozen && OKToStartUserMove(x, y)) {\r
5184       fromX = x;\r
5185       fromY = y;\r
5186       dragInfo.lastpos = pt;\r
5187       dragInfo.from.x = fromX;\r
5188       dragInfo.from.y = fromY;\r
5189       dragInfo.start = dragInfo.from;\r
5190       SetCapture(hwndMain);\r
5191     } else {\r
5192       fromX = fromY = -1;\r
5193       dragInfo.start.x = dragInfo.start.y = -1;\r
5194       dragInfo.from = dragInfo.start;\r
5195       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5196     }\r
5197     break;\r
5198 \r
5199   case WM_LBUTTONUP:\r
5200     ReleaseCapture();\r
5201     if (fromX == -1) break;\r
5202     if (x == fromX && y == fromY) {\r
5203       dragInfo.from.x = dragInfo.from.y = -1;\r
5204       /* Upclick on same square */\r
5205       if (sameAgain) {\r
5206         /* Clicked same square twice: abort click-click move */\r
5207         fromX = fromY = -1;\r
5208         gotPremove = 0;\r
5209         ClearPremoveHighlights();\r
5210       } else {\r
5211         /* First square clicked: start click-click move */\r
5212         SetHighlights(fromX, fromY, -1, -1);\r
5213       }\r
5214       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5215     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5216       /* Errant click; ignore */\r
5217       break;\r
5218     } else {\r
5219       /* Finish drag move. */\r
5220     if (appData.debugMode) {\r
5221         fprintf(debugFP, "release\n");\r
5222     }\r
5223       dragInfo.from.x = dragInfo.from.y = -1;\r
5224       toX = x;\r
5225       toY = y;\r
5226       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5227       appData.animate = appData.animate && !appData.animateDragging;\r
5228       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5229       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5230                 fromX = fromY = -1; \r
5231                 ClearHighlights();\r
5232                 DrawPosition(FALSE, boards[currentMove]);\r
5233                 appData.animate = saveAnimate;\r
5234                 break; \r
5235       } else \r
5236       if(moveType != ImpossibleMove) {\r
5237           /* [HGM] use move type to determine if move is promotion.\r
5238              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5239           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5240             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5241               appData.alwaysPromoteToQueen)) \r
5242                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5243           else \r
5244           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5245                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5246                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5247                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5248                     // kludge to temporarily execute move on display, wthout promotng yet\r
5249                     promotionChoice = TRUE;\r
5250                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5251                     boards[currentMove][toY][toX] = p;\r
5252                     DrawPosition(FALSE, boards[currentMove]);\r
5253                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5254                     boards[currentMove][toY][toX] = q;\r
5255                     appData.animate = saveAnimate;\r
5256                     break;\r
5257                   } else\r
5258                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5259           } else {\r
5260             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5261                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5262                                         moveType == WhiteCapturesEnPassant || \r
5263                                         moveType == BlackCapturesEnPassant   ) )\r
5264                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5265             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5266           }\r
5267       }\r
5268       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5269       appData.animate = saveAnimate;\r
5270       fromX = fromY = -1;\r
5271       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5272         ClearHighlights();\r
5273       }\r
5274       if (appData.animate || appData.animateDragging ||\r
5275           appData.highlightDragging || gotPremove) {\r
5276         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5277       }\r
5278     }\r
5279     dragInfo.start.x = dragInfo.start.y = -1; \r
5280     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5281     break;\r
5282 \r
5283   case WM_MOUSEMOVE:\r
5284     if ((appData.animateDragging || appData.highlightDragging)\r
5285         && (wParam & MK_LBUTTON)\r
5286         && dragInfo.from.x >= 0) \r
5287     {\r
5288       BOOL full_repaint = FALSE;\r
5289 \r
5290       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5291       if (appData.animateDragging) {\r
5292         dragInfo.pos = pt;\r
5293       }\r
5294       if (appData.highlightDragging) {\r
5295         SetHighlights(fromX, fromY, x, y);\r
5296         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5297             full_repaint = TRUE;\r
5298         }\r
5299       }\r
5300       \r
5301       DrawPosition( full_repaint, NULL);\r
5302       \r
5303       dragInfo.lastpos = dragInfo.pos;\r
5304     }\r
5305     break;\r
5306 \r
5307   case WM_MOUSEWHEEL: // [DM]\r
5308     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5309        /* Mouse Wheel is being rolled forward\r
5310         * Play moves forward\r
5311         */\r
5312        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5313                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5314        /* Mouse Wheel is being rolled backward\r
5315         * Play moves backward\r
5316         */\r
5317        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5318                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5319     }\r
5320     break;\r
5321 \r
5322   case WM_MBUTTONDOWN:\r
5323   case WM_RBUTTONDOWN:\r
5324     ErrorPopDown();\r
5325     ReleaseCapture();\r
5326     fromX = fromY = -1;\r
5327     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5328     dragInfo.start.x = dragInfo.start.y = -1;\r
5329     dragInfo.from = dragInfo.start;\r
5330     dragInfo.lastpos = dragInfo.pos;\r
5331     if (appData.highlightDragging) {\r
5332       ClearHighlights();\r
5333     }\r
5334     if(y == -2) {\r
5335       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5336       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5337           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5338       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5339           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5340       }\r
5341     }\r
5342     DrawPosition(TRUE, NULL);\r
5343 \r
5344     switch (gameMode) {\r
5345     case EditPosition:\r
5346     case IcsExamining:\r
5347       if (x < 0 || y < 0) break;\r
5348       fromX = x;\r
5349       fromY = y;\r
5350       if (message == WM_MBUTTONDOWN) {\r
5351         buttonCount = 3;  /* even if system didn't think so */\r
5352         if (wParam & MK_SHIFT) \r
5353           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5354         else\r
5355           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5356       } else { /* message == WM_RBUTTONDOWN */\r
5357 #if 0\r
5358         if (buttonCount == 3) {\r
5359           if (wParam & MK_SHIFT) \r
5360             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5361           else\r
5362             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5363         } else {\r
5364           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5365         }\r
5366 #else\r
5367         /* Just have one menu, on the right button.  Windows users don't\r
5368            think to try the middle one, and sometimes other software steals\r
5369            it, or it doesn't really exist. */\r
5370         if(gameInfo.variant != VariantShogi)\r
5371             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5372         else\r
5373             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5374 #endif\r
5375       }\r
5376       break;\r
5377     case IcsPlayingWhite:\r
5378     case IcsPlayingBlack:\r
5379     case EditGame:\r
5380     case MachinePlaysWhite:\r
5381     case MachinePlaysBlack:\r
5382       if (appData.testLegality &&\r
5383           gameInfo.variant != VariantBughouse &&\r
5384           gameInfo.variant != VariantCrazyhouse) break;\r
5385       if (x < 0 || y < 0) break;\r
5386       fromX = x;\r
5387       fromY = y;\r
5388       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5389       SetupDropMenu(hmenu);\r
5390       MenuPopup(hwnd, pt, hmenu, -1);\r
5391       break;\r
5392     default:\r
5393       break;\r
5394     }\r
5395     break;\r
5396   }\r
5397 \r
5398   recursive--;\r
5399 }\r
5400 \r
5401 /* Preprocess messages for buttons in main window */\r
5402 LRESULT CALLBACK\r
5403 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5404 {\r
5405   int id = GetWindowLong(hwnd, GWL_ID);\r
5406   int i, dir;\r
5407 \r
5408   for (i=0; i<N_BUTTONS; i++) {\r
5409     if (buttonDesc[i].id == id) break;\r
5410   }\r
5411   if (i == N_BUTTONS) return 0;\r
5412   switch (message) {\r
5413   case WM_KEYDOWN:\r
5414     switch (wParam) {\r
5415     case VK_LEFT:\r
5416     case VK_RIGHT:\r
5417       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5418       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5419       return TRUE;\r
5420     }\r
5421     break;\r
5422   case WM_CHAR:\r
5423     switch (wParam) {\r
5424     case '\r':\r
5425       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5426       return TRUE;\r
5427     default:\r
5428       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5429         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5430         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5431         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5432         SetFocus(h);\r
5433         SendMessage(h, WM_CHAR, wParam, lParam);\r
5434         return TRUE;\r
5435       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5436         PopUpMoveDialog((char)wParam);\r
5437       }\r
5438       break;\r
5439     }\r
5440     break;\r
5441   }\r
5442   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5443 }\r
5444 \r
5445 /* Process messages for Promotion dialog box */\r
5446 LRESULT CALLBACK\r
5447 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5448 {\r
5449   char promoChar;\r
5450 \r
5451   switch (message) {\r
5452   case WM_INITDIALOG: /* message: initialize dialog box */\r
5453     /* Center the dialog over the application window */\r
5454     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5455     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5456       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5457        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5458                SW_SHOW : SW_HIDE);\r
5459     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5460     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5461        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5462          PieceToChar(WhiteAngel) != '~') ||\r
5463         (PieceToChar(BlackAngel) >= 'A' &&\r
5464          PieceToChar(BlackAngel) != '~')   ) ?\r
5465                SW_SHOW : SW_HIDE);\r
5466     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5467        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5468          PieceToChar(WhiteMarshall) != '~') ||\r
5469         (PieceToChar(BlackMarshall) >= 'A' &&\r
5470          PieceToChar(BlackMarshall) != '~')   ) ?\r
5471                SW_SHOW : SW_HIDE);\r
5472     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5473     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5474        gameInfo.variant != VariantShogi ?\r
5475                SW_SHOW : SW_HIDE);\r
5476     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5477        gameInfo.variant != VariantShogi ?\r
5478                SW_SHOW : SW_HIDE);\r
5479     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5480        gameInfo.variant == VariantShogi ?\r
5481                SW_SHOW : SW_HIDE);\r
5482     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5483        gameInfo.variant == VariantShogi ?\r
5484                SW_SHOW : SW_HIDE);\r
5485     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5486        gameInfo.variant == VariantSuper ?\r
5487                SW_SHOW : SW_HIDE);\r
5488     return TRUE;\r
5489 \r
5490   case WM_COMMAND: /* message: received a command */\r
5491     switch (LOWORD(wParam)) {\r
5492     case IDCANCEL:\r
5493       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5494       ClearHighlights();\r
5495       DrawPosition(FALSE, NULL);\r
5496       return TRUE;\r
5497     case PB_King:\r
5498       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5499       break;\r
5500     case PB_Queen:\r
5501       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5502       break;\r
5503     case PB_Rook:\r
5504       promoChar = PieceToChar(BlackRook);\r
5505       break;\r
5506     case PB_Bishop:\r
5507       promoChar = PieceToChar(BlackBishop);\r
5508       break;\r
5509     case PB_Chancellor:\r
5510       promoChar = PieceToChar(BlackMarshall);\r
5511       break;\r
5512     case PB_Archbishop:\r
5513       promoChar = PieceToChar(BlackAngel);\r
5514       break;\r
5515     case PB_Knight:\r
5516       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5517       break;\r
5518     default:\r
5519       return FALSE;\r
5520     }\r
5521     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5522     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5523        only show the popup when we are already sure the move is valid or\r
5524        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5525        will figure out it is a promotion from the promoChar. */\r
5526     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5527     if (!appData.highlightLastMove) {\r
5528       ClearHighlights();\r
5529       DrawPosition(FALSE, NULL);\r
5530     }\r
5531     return TRUE;\r
5532   }\r
5533   return FALSE;\r
5534 }\r
5535 \r
5536 /* Pop up promotion dialog */\r
5537 VOID\r
5538 PromotionPopup(HWND hwnd)\r
5539 {\r
5540   FARPROC lpProc;\r
5541 \r
5542   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5543   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5544     hwnd, (DLGPROC)lpProc);\r
5545   FreeProcInstance(lpProc);\r
5546 }\r
5547 \r
5548 /* Toggle ShowThinking */\r
5549 VOID\r
5550 ToggleShowThinking()\r
5551 {\r
5552   appData.showThinking = !appData.showThinking;\r
5553   ShowThinkingEvent();\r
5554 }\r
5555 \r
5556 VOID\r
5557 LoadGameDialog(HWND hwnd, char* title)\r
5558 {\r
5559   UINT number = 0;\r
5560   FILE *f;\r
5561   char fileTitle[MSG_SIZ];\r
5562   f = OpenFileDialog(hwnd, "rb", "",\r
5563                      appData.oldSaveStyle ? "gam" : "pgn",\r
5564                      GAME_FILT,\r
5565                      title, &number, fileTitle, NULL);\r
5566   if (f != NULL) {\r
5567     cmailMsgLoaded = FALSE;\r
5568     if (number == 0) {\r
5569       int error = GameListBuild(f);\r
5570       if (error) {\r
5571         DisplayError("Cannot build game list", error);\r
5572       } else if (!ListEmpty(&gameList) &&\r
5573                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5574         GameListPopUp(f, fileTitle);\r
5575         return;\r
5576       }\r
5577       GameListDestroy();\r
5578       number = 1;\r
5579     }\r
5580     LoadGame(f, number, fileTitle, FALSE);\r
5581   }\r
5582 }\r
5583 \r
5584 VOID\r
5585 ChangedConsoleFont()\r
5586 {\r
5587   CHARFORMAT cfmt;\r
5588   CHARRANGE tmpsel, sel;\r
5589   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5590   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5591   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5592   PARAFORMAT paraf;\r
5593 \r
5594   cfmt.cbSize = sizeof(CHARFORMAT);\r
5595   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5596   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5597   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5598    * size.  This was undocumented in the version of MSVC++ that I had\r
5599    * when I wrote the code, but is apparently documented now.\r
5600    */\r
5601   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5602   cfmt.bCharSet = f->lf.lfCharSet;\r
5603   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5604   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5605   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5606   /* Why are the following seemingly needed too? */\r
5607   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5608   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5609   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5610   tmpsel.cpMin = 0;\r
5611   tmpsel.cpMax = -1; /*999999?*/\r
5612   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5613   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5614   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5615    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5616    */\r
5617   paraf.cbSize = sizeof(paraf);\r
5618   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5619   paraf.dxStartIndent = 0;\r
5620   paraf.dxOffset = WRAP_INDENT;\r
5621   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5622   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5623 }\r
5624 \r
5625 /*---------------------------------------------------------------------------*\\r
5626  *\r
5627  * Window Proc for main window\r
5628  *\r
5629 \*---------------------------------------------------------------------------*/\r
5630 \r
5631 /* Process messages for main window, etc. */\r
5632 LRESULT CALLBACK\r
5633 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5634 {\r
5635   FARPROC lpProc;\r
5636   int wmId, wmEvent;\r
5637   char *defName;\r
5638   FILE *f;\r
5639   UINT number;\r
5640   char fileTitle[MSG_SIZ];\r
5641   char buf[MSG_SIZ];\r
5642   static SnapData sd;\r
5643 \r
5644   switch (message) {\r
5645 \r
5646   case WM_PAINT: /* message: repaint portion of window */\r
5647     PaintProc(hwnd);\r
5648     break;\r
5649 \r
5650   case WM_ERASEBKGND:\r
5651     if (IsIconic(hwnd)) {\r
5652       /* Cheat; change the message */\r
5653       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5654     } else {\r
5655       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5656     }\r
5657     break;\r
5658 \r
5659   case WM_LBUTTONDOWN:\r
5660   case WM_MBUTTONDOWN:\r
5661   case WM_RBUTTONDOWN:\r
5662   case WM_LBUTTONUP:\r
5663   case WM_MBUTTONUP:\r
5664   case WM_RBUTTONUP:\r
5665   case WM_MOUSEMOVE:\r
5666   case WM_MOUSEWHEEL:\r
5667     MouseEvent(hwnd, message, wParam, lParam);\r
5668     break;\r
5669 \r
5670   JAWS_KB_NAVIGATION\r
5671 \r
5672   case WM_CHAR:\r
5673     \r
5674     JAWS_ALT_INTERCEPT\r
5675 \r
5676     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5677         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5678         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5679         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5680         SetFocus(h);\r
5681         SendMessage(h, message, wParam, lParam);\r
5682     } else if(lParam != KF_REPEAT) {\r
5683         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5684                 PopUpMoveDialog((char)wParam);\r
5685         } else if((char)wParam == 003) CopyGameToClipboard();\r
5686          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5687     }\r
5688 \r
5689     break;\r
5690 \r
5691   case WM_PALETTECHANGED:\r
5692     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5693       int nnew;\r
5694       HDC hdc = GetDC(hwndMain);\r
5695       SelectPalette(hdc, hPal, TRUE);\r
5696       nnew = RealizePalette(hdc);\r
5697       if (nnew > 0) {\r
5698         paletteChanged = TRUE;\r
5699 #if 0\r
5700         UpdateColors(hdc);\r
5701 #else\r
5702         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5703 #endif\r
5704       }\r
5705       ReleaseDC(hwnd, hdc);\r
5706     }\r
5707     break;\r
5708 \r
5709   case WM_QUERYNEWPALETTE:\r
5710     if (!appData.monoMode /*&& paletteChanged*/) {\r
5711       int nnew;\r
5712       HDC hdc = GetDC(hwndMain);\r
5713       paletteChanged = FALSE;\r
5714       SelectPalette(hdc, hPal, FALSE);\r
5715       nnew = RealizePalette(hdc);\r
5716       if (nnew > 0) {\r
5717         InvalidateRect(hwnd, &boardRect, FALSE);\r
5718       }\r
5719       ReleaseDC(hwnd, hdc);\r
5720       return TRUE;\r
5721     }\r
5722     return FALSE;\r
5723 \r
5724   case WM_COMMAND: /* message: command from application menu */\r
5725     wmId    = LOWORD(wParam);\r
5726     wmEvent = HIWORD(wParam);\r
5727 \r
5728     switch (wmId) {\r
5729     case IDM_NewGame:\r
5730       ResetGameEvent();\r
5731       AnalysisPopDown();\r
5732       SAY("new game enter a move to play against the computer with white");\r
5733       break;\r
5734 \r
5735     case IDM_NewGameFRC:\r
5736       if( NewGameFRC() == 0 ) {\r
5737         ResetGameEvent();\r
5738         AnalysisPopDown();\r
5739       }\r
5740       break;\r
5741 \r
5742     case IDM_NewVariant:\r
5743       NewVariantPopup(hwnd);\r
5744       break;\r
5745 \r
5746     case IDM_LoadGame:\r
5747       LoadGameDialog(hwnd, "Load Game from File");\r
5748       break;\r
5749 \r
5750     case IDM_LoadNextGame:\r
5751       ReloadGame(1);\r
5752       break;\r
5753 \r
5754     case IDM_LoadPrevGame:\r
5755       ReloadGame(-1);\r
5756       break;\r
5757 \r
5758     case IDM_ReloadGame:\r
5759       ReloadGame(0);\r
5760       break;\r
5761 \r
5762     case IDM_LoadPosition:\r
5763       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5764         Reset(FALSE, TRUE);\r
5765       }\r
5766       number = 1;\r
5767       f = OpenFileDialog(hwnd, "rb", "",\r
5768                          appData.oldSaveStyle ? "pos" : "fen",\r
5769                          POSITION_FILT,\r
5770                          "Load Position from File", &number, fileTitle, NULL);\r
5771       if (f != NULL) {\r
5772         LoadPosition(f, number, fileTitle);\r
5773       }\r
5774       break;\r
5775 \r
5776     case IDM_LoadNextPosition:\r
5777       ReloadPosition(1);\r
5778       break;\r
5779 \r
5780     case IDM_LoadPrevPosition:\r
5781       ReloadPosition(-1);\r
5782       break;\r
5783 \r
5784     case IDM_ReloadPosition:\r
5785       ReloadPosition(0);\r
5786       break;\r
5787 \r
5788     case IDM_SaveGame:\r
5789       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5790       f = OpenFileDialog(hwnd, "a", defName,\r
5791                          appData.oldSaveStyle ? "gam" : "pgn",\r
5792                          GAME_FILT,\r
5793                          "Save Game to File", NULL, fileTitle, NULL);\r
5794       if (f != NULL) {\r
5795         SaveGame(f, 0, "");\r
5796       }\r
5797       break;\r
5798 \r
5799     case IDM_SavePosition:\r
5800       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5801       f = OpenFileDialog(hwnd, "a", defName,\r
5802                          appData.oldSaveStyle ? "pos" : "fen",\r
5803                          POSITION_FILT,\r
5804                          "Save Position to File", NULL, fileTitle, NULL);\r
5805       if (f != NULL) {\r
5806         SavePosition(f, 0, "");\r
5807       }\r
5808       break;\r
5809 \r
5810     case IDM_SaveDiagram:\r
5811       defName = "diagram";\r
5812       f = OpenFileDialog(hwnd, "wb", defName,\r
5813                          "bmp",\r
5814                          DIAGRAM_FILT,\r
5815                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5816       if (f != NULL) {\r
5817         SaveDiagram(f);\r
5818       }\r
5819       break;\r
5820 \r
5821     case IDM_CopyGame:\r
5822       CopyGameToClipboard();\r
5823       break;\r
5824 \r
5825     case IDM_PasteGame:\r
5826       PasteGameFromClipboard();\r
5827       break;\r
5828 \r
5829     case IDM_CopyGameListToClipboard:\r
5830       CopyGameListToClipboard();\r
5831       break;\r
5832 \r
5833     /* [AS] Autodetect FEN or PGN data */\r
5834     case IDM_PasteAny:\r
5835       PasteGameOrFENFromClipboard();\r
5836       break;\r
5837 \r
5838     /* [AS] Move history */\r
5839     case IDM_ShowMoveHistory:\r
5840         if( MoveHistoryIsUp() ) {\r
5841             MoveHistoryPopDown();\r
5842         }\r
5843         else {\r
5844             MoveHistoryPopUp();\r
5845         }\r
5846         break;\r
5847 \r
5848     /* [AS] Eval graph */\r
5849     case IDM_ShowEvalGraph:\r
5850         if( EvalGraphIsUp() ) {\r
5851             EvalGraphPopDown();\r
5852         }\r
5853         else {\r
5854             EvalGraphPopUp();\r
5855             SetFocus(hwndMain);\r
5856         }\r
5857         break;\r
5858 \r
5859     /* [AS] Engine output */\r
5860     case IDM_ShowEngineOutput:\r
5861         if( EngineOutputIsUp() ) {\r
5862             EngineOutputPopDown();\r
5863         }\r
5864         else {\r
5865             EngineOutputPopUp();\r
5866         }\r
5867         break;\r
5868 \r
5869     /* [AS] User adjudication */\r
5870     case IDM_UserAdjudication_White:\r
5871         UserAdjudicationEvent( +1 );\r
5872         break;\r
5873 \r
5874     case IDM_UserAdjudication_Black:\r
5875         UserAdjudicationEvent( -1 );\r
5876         break;\r
5877 \r
5878     case IDM_UserAdjudication_Draw:\r
5879         UserAdjudicationEvent( 0 );\r
5880         break;\r
5881 \r
5882     /* [AS] Game list options dialog */\r
5883     case IDM_GameListOptions:\r
5884       GameListOptions();\r
5885       break;\r
5886 \r
5887     case IDM_NewChat:\r
5888       ChatPopUp();\r
5889       break;\r
5890 \r
5891     case IDM_CopyPosition:\r
5892       CopyFENToClipboard();\r
5893       break;\r
5894 \r
5895     case IDM_PastePosition:\r
5896       PasteFENFromClipboard();\r
5897       break;\r
5898 \r
5899     case IDM_MailMove:\r
5900       MailMoveEvent();\r
5901       break;\r
5902 \r
5903     case IDM_ReloadCMailMsg:\r
5904       Reset(TRUE, TRUE);\r
5905       ReloadCmailMsgEvent(FALSE);\r
5906       break;\r
5907 \r
5908     case IDM_Minimize:\r
5909       ShowWindow(hwnd, SW_MINIMIZE);\r
5910       break;\r
5911 \r
5912     case IDM_Exit:\r
5913       ExitEvent(0);\r
5914       break;\r
5915 \r
5916     case IDM_MachineWhite:\r
5917       MachineWhiteEvent();\r
5918       /*\r
5919        * refresh the tags dialog only if it's visible\r
5920        */\r
5921       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5922           char *tags;\r
5923           tags = PGNTags(&gameInfo);\r
5924           TagsPopUp(tags, CmailMsg());\r
5925           free(tags);\r
5926       }\r
5927       SAY("computer starts playing white");\r
5928       break;\r
5929 \r
5930     case IDM_MachineBlack:\r
5931       MachineBlackEvent();\r
5932       /*\r
5933        * refresh the tags dialog only if it's visible\r
5934        */\r
5935       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5936           char *tags;\r
5937           tags = PGNTags(&gameInfo);\r
5938           TagsPopUp(tags, CmailMsg());\r
5939           free(tags);\r
5940       }\r
5941       SAY("computer starts playing black");\r
5942       break;\r
5943 \r
5944     case IDM_TwoMachines:\r
5945       TwoMachinesEvent();\r
5946       /*\r
5947        * refresh the tags dialog only if it's visible\r
5948        */\r
5949       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5950           char *tags;\r
5951           tags = PGNTags(&gameInfo);\r
5952           TagsPopUp(tags, CmailMsg());\r
5953           free(tags);\r
5954       }\r
5955       SAY("programs start playing each other");\r
5956       break;\r
5957 \r
5958     case IDM_AnalysisMode:\r
5959       if (!first.analysisSupport) {\r
5960         sprintf(buf, "%s does not support analysis", first.tidy);\r
5961         DisplayError(buf, 0);\r
5962       } else {\r
5963         SAY("analyzing current position");\r
5964         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5965         if (appData.icsActive) {\r
5966                if (gameMode != IcsObserving) {\r
5967                        sprintf(buf, "You are not observing a game");\r
5968                        DisplayError(buf, 0);\r
5969                        /* secure check */\r
5970                        if (appData.icsEngineAnalyze) {\r
5971                                if (appData.debugMode) \r
5972                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5973                                ExitAnalyzeMode();\r
5974                                ModeHighlight();\r
5975                                break;\r
5976                        }\r
5977                        break;\r
5978                } else {\r
5979                        /* if enable, user want disable icsEngineAnalyze */\r
5980                        if (appData.icsEngineAnalyze) {\r
5981                                ExitAnalyzeMode();\r
5982                                ModeHighlight();\r
5983                                break;\r
5984                        }\r
5985                        appData.icsEngineAnalyze = TRUE;\r
5986                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5987                }\r
5988         } \r
5989         if (!appData.showThinking) ToggleShowThinking();\r
5990         AnalyzeModeEvent();\r
5991       }\r
5992       break;\r
5993 \r
5994     case IDM_AnalyzeFile:\r
5995       if (!first.analysisSupport) {\r
5996         char buf[MSG_SIZ];\r
5997         sprintf(buf, "%s does not support analysis", first.tidy);\r
5998         DisplayError(buf, 0);\r
5999       } else {\r
6000         if (!appData.showThinking) ToggleShowThinking();\r
6001         AnalyzeFileEvent();\r
6002         LoadGameDialog(hwnd, "Analyze Game from File");\r
6003         AnalysisPeriodicEvent(1);\r
6004       }\r
6005       break;\r
6006 \r
6007     case IDM_IcsClient:\r
6008       IcsClientEvent();\r
6009       break;\r
6010 \r
6011     case IDM_EditGame:\r
6012       EditGameEvent();\r
6013       SAY("edit game");\r
6014       break;\r
6015 \r
6016     case IDM_EditPosition:\r
6017       EditPositionEvent();\r
6018       SAY("to set up a position type a FEN");\r
6019       break;\r
6020 \r
6021     case IDM_Training:\r
6022       TrainingEvent();\r
6023       break;\r
6024 \r
6025     case IDM_ShowGameList:\r
6026       ShowGameListProc();\r
6027       break;\r
6028 \r
6029     case IDM_EditTags:\r
6030       EditTagsProc();\r
6031       break;\r
6032 \r
6033     case IDM_EditComment:\r
6034       if (commentDialogUp && editComment) {\r
6035         CommentPopDown();\r
6036       } else {\r
6037         EditCommentEvent();\r
6038       }\r
6039       break;\r
6040 \r
6041     case IDM_Pause:\r
6042       PauseEvent();\r
6043       break;\r
6044 \r
6045     case IDM_Accept:\r
6046       AcceptEvent();\r
6047       break;\r
6048 \r
6049     case IDM_Decline:\r
6050       DeclineEvent();\r
6051       break;\r
6052 \r
6053     case IDM_Rematch:\r
6054       RematchEvent();\r
6055       break;\r
6056 \r
6057     case IDM_CallFlag:\r
6058       CallFlagEvent();\r
6059       break;\r
6060 \r
6061     case IDM_Draw:\r
6062       DrawEvent();\r
6063       break;\r
6064 \r
6065     case IDM_Adjourn:\r
6066       AdjournEvent();\r
6067       break;\r
6068 \r
6069     case IDM_Abort:\r
6070       AbortEvent();\r
6071       break;\r
6072 \r
6073     case IDM_Resign:\r
6074       ResignEvent();\r
6075       break;\r
6076 \r
6077     case IDM_StopObserving:\r
6078       StopObservingEvent();\r
6079       break;\r
6080 \r
6081     case IDM_StopExamining:\r
6082       StopExaminingEvent();\r
6083       break;\r
6084 \r
6085     case IDM_TypeInMove:\r
6086       PopUpMoveDialog('\000');\r
6087       break;\r
6088 \r
6089     case IDM_TypeInName:\r
6090       PopUpNameDialog('\000');\r
6091       break;\r
6092 \r
6093     case IDM_Backward:\r
6094       BackwardEvent();\r
6095       SetFocus(hwndMain);\r
6096       break;\r
6097 \r
6098     JAWS_MENU_ITEMS\r
6099 \r
6100     case IDM_Forward:\r
6101       ForwardEvent();\r
6102       SetFocus(hwndMain);\r
6103       break;\r
6104 \r
6105     case IDM_ToStart:\r
6106       ToStartEvent();\r
6107       SetFocus(hwndMain);\r
6108       break;\r
6109 \r
6110     case IDM_ToEnd:\r
6111       ToEndEvent();\r
6112       SetFocus(hwndMain);\r
6113       break;\r
6114 \r
6115     case IDM_Revert:\r
6116       RevertEvent();\r
6117       break;\r
6118 \r
6119     case IDM_TruncateGame:\r
6120       TruncateGameEvent();\r
6121       break;\r
6122 \r
6123     case IDM_MoveNow:\r
6124       MoveNowEvent();\r
6125       break;\r
6126 \r
6127     case IDM_RetractMove:\r
6128       RetractMoveEvent();\r
6129       break;\r
6130 \r
6131     case IDM_FlipView:\r
6132       flipView = !flipView;\r
6133       DrawPosition(FALSE, NULL);\r
6134       break;\r
6135 \r
6136     case IDM_FlipClock:\r
6137       flipClock = !flipClock;\r
6138       DisplayBothClocks();\r
6139       DrawPosition(FALSE, NULL);\r
6140       break;\r
6141 \r
6142     case IDM_MuteSounds:\r
6143       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6144       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6145                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6146       break;\r
6147 \r
6148     case IDM_GeneralOptions:\r
6149       GeneralOptionsPopup(hwnd);\r
6150       DrawPosition(TRUE, NULL);\r
6151       break;\r
6152 \r
6153     case IDM_BoardOptions:\r
6154       BoardOptionsPopup(hwnd);\r
6155       break;\r
6156 \r
6157     case IDM_EnginePlayOptions:\r
6158       EnginePlayOptionsPopup(hwnd);\r
6159       break;\r
6160 \r
6161     case IDM_Engine1Options:\r
6162       EngineOptionsPopup(hwnd, &first);\r
6163       break;\r
6164 \r
6165     case IDM_Engine2Options:\r
6166       EngineOptionsPopup(hwnd, &second);\r
6167       break;\r
6168 \r
6169     case IDM_OptionsUCI:\r
6170       UciOptionsPopup(hwnd);\r
6171       break;\r
6172 \r
6173     case IDM_IcsOptions:\r
6174       IcsOptionsPopup(hwnd);\r
6175       break;\r
6176 \r
6177     case IDM_Fonts:\r
6178       FontsOptionsPopup(hwnd);\r
6179       break;\r
6180 \r
6181     case IDM_Sounds:\r
6182       SoundOptionsPopup(hwnd);\r
6183       break;\r
6184 \r
6185     case IDM_CommPort:\r
6186       CommPortOptionsPopup(hwnd);\r
6187       break;\r
6188 \r
6189     case IDM_LoadOptions:\r
6190       LoadOptionsPopup(hwnd);\r
6191       break;\r
6192 \r
6193     case IDM_SaveOptions:\r
6194       SaveOptionsPopup(hwnd);\r
6195       break;\r
6196 \r
6197     case IDM_TimeControl:\r
6198       TimeControlOptionsPopup(hwnd);\r
6199       break;\r
6200 \r
6201     case IDM_SaveSettings:\r
6202       SaveSettings(settingsFileName);\r
6203       break;\r
6204 \r
6205     case IDM_SaveSettingsOnExit:\r
6206       saveSettingsOnExit = !saveSettingsOnExit;\r
6207       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6208                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6209                                          MF_CHECKED : MF_UNCHECKED));\r
6210       break;\r
6211 \r
6212     case IDM_Hint:\r
6213       HintEvent();\r
6214       break;\r
6215 \r
6216     case IDM_Book:\r
6217       BookEvent();\r
6218       break;\r
6219 \r
6220     case IDM_AboutGame:\r
6221       AboutGameEvent();\r
6222       break;\r
6223 \r
6224     case IDM_Debug:\r
6225       appData.debugMode = !appData.debugMode;\r
6226       if (appData.debugMode) {\r
6227         char dir[MSG_SIZ];\r
6228         GetCurrentDirectory(MSG_SIZ, dir);\r
6229         SetCurrentDirectory(installDir);\r
6230         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6231         SetCurrentDirectory(dir);\r
6232         setbuf(debugFP, NULL);\r
6233       } else {\r
6234         fclose(debugFP);\r
6235         debugFP = NULL;\r
6236       }\r
6237       break;\r
6238 \r
6239     case IDM_HELPCONTENTS:\r
6240       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6241           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6242           MessageBox (GetFocus(),\r
6243                     "Unable to activate help",\r
6244                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6245       }\r
6246       break;\r
6247 \r
6248     case IDM_HELPSEARCH:\r
6249         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6250             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6251         MessageBox (GetFocus(),\r
6252                     "Unable to activate help",\r
6253                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6254       }\r
6255       break;\r
6256 \r
6257     case IDM_HELPHELP:\r
6258       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6259         MessageBox (GetFocus(),\r
6260                     "Unable to activate help",\r
6261                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6262       }\r
6263       break;\r
6264 \r
6265     case IDM_ABOUT:\r
6266       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6267       DialogBox(hInst, \r
6268         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6269         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6270       FreeProcInstance(lpProc);\r
6271       break;\r
6272 \r
6273     case IDM_DirectCommand1:\r
6274       AskQuestionEvent("Direct Command",\r
6275                        "Send to chess program:", "", "1");\r
6276       break;\r
6277     case IDM_DirectCommand2:\r
6278       AskQuestionEvent("Direct Command",\r
6279                        "Send to second chess program:", "", "2");\r
6280       break;\r
6281 \r
6282     case EP_WhitePawn:\r
6283       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6284       fromX = fromY = -1;\r
6285       break;\r
6286 \r
6287     case EP_WhiteKnight:\r
6288       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6289       fromX = fromY = -1;\r
6290       break;\r
6291 \r
6292     case EP_WhiteBishop:\r
6293       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6294       fromX = fromY = -1;\r
6295       break;\r
6296 \r
6297     case EP_WhiteRook:\r
6298       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6299       fromX = fromY = -1;\r
6300       break;\r
6301 \r
6302     case EP_WhiteQueen:\r
6303       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6304       fromX = fromY = -1;\r
6305       break;\r
6306 \r
6307     case EP_WhiteFerz:\r
6308       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6309       fromX = fromY = -1;\r
6310       break;\r
6311 \r
6312     case EP_WhiteWazir:\r
6313       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6314       fromX = fromY = -1;\r
6315       break;\r
6316 \r
6317     case EP_WhiteAlfil:\r
6318       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6319       fromX = fromY = -1;\r
6320       break;\r
6321 \r
6322     case EP_WhiteCannon:\r
6323       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6324       fromX = fromY = -1;\r
6325       break;\r
6326 \r
6327     case EP_WhiteCardinal:\r
6328       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6329       fromX = fromY = -1;\r
6330       break;\r
6331 \r
6332     case EP_WhiteMarshall:\r
6333       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6334       fromX = fromY = -1;\r
6335       break;\r
6336 \r
6337     case EP_WhiteKing:\r
6338       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6339       fromX = fromY = -1;\r
6340       break;\r
6341 \r
6342     case EP_BlackPawn:\r
6343       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6344       fromX = fromY = -1;\r
6345       break;\r
6346 \r
6347     case EP_BlackKnight:\r
6348       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6349       fromX = fromY = -1;\r
6350       break;\r
6351 \r
6352     case EP_BlackBishop:\r
6353       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6354       fromX = fromY = -1;\r
6355       break;\r
6356 \r
6357     case EP_BlackRook:\r
6358       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6359       fromX = fromY = -1;\r
6360       break;\r
6361 \r
6362     case EP_BlackQueen:\r
6363       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6364       fromX = fromY = -1;\r
6365       break;\r
6366 \r
6367     case EP_BlackFerz:\r
6368       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6369       fromX = fromY = -1;\r
6370       break;\r
6371 \r
6372     case EP_BlackWazir:\r
6373       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6374       fromX = fromY = -1;\r
6375       break;\r
6376 \r
6377     case EP_BlackAlfil:\r
6378       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6379       fromX = fromY = -1;\r
6380       break;\r
6381 \r
6382     case EP_BlackCannon:\r
6383       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6384       fromX = fromY = -1;\r
6385       break;\r
6386 \r
6387     case EP_BlackCardinal:\r
6388       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6389       fromX = fromY = -1;\r
6390       break;\r
6391 \r
6392     case EP_BlackMarshall:\r
6393       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6394       fromX = fromY = -1;\r
6395       break;\r
6396 \r
6397     case EP_BlackKing:\r
6398       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6399       fromX = fromY = -1;\r
6400       break;\r
6401 \r
6402     case EP_EmptySquare:\r
6403       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6404       fromX = fromY = -1;\r
6405       break;\r
6406 \r
6407     case EP_ClearBoard:\r
6408       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6409       fromX = fromY = -1;\r
6410       break;\r
6411 \r
6412     case EP_White:\r
6413       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6414       fromX = fromY = -1;\r
6415       break;\r
6416 \r
6417     case EP_Black:\r
6418       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6419       fromX = fromY = -1;\r
6420       break;\r
6421 \r
6422     case EP_Promote:\r
6423       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6424       fromX = fromY = -1;\r
6425       break;\r
6426 \r
6427     case EP_Demote:\r
6428       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6429       fromX = fromY = -1;\r
6430       break;\r
6431 \r
6432     case DP_Pawn:\r
6433       DropMenuEvent(WhitePawn, fromX, fromY);\r
6434       fromX = fromY = -1;\r
6435       break;\r
6436 \r
6437     case DP_Knight:\r
6438       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6439       fromX = fromY = -1;\r
6440       break;\r
6441 \r
6442     case DP_Bishop:\r
6443       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6444       fromX = fromY = -1;\r
6445       break;\r
6446 \r
6447     case DP_Rook:\r
6448       DropMenuEvent(WhiteRook, fromX, fromY);\r
6449       fromX = fromY = -1;\r
6450       break;\r
6451 \r
6452     case DP_Queen:\r
6453       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6454       fromX = fromY = -1;\r
6455       break;\r
6456 \r
6457     default:\r
6458       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6459     }\r
6460     break;\r
6461 \r
6462   case WM_TIMER:\r
6463     switch (wParam) {\r
6464     case CLOCK_TIMER_ID:\r
6465       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6466       clockTimerEvent = 0;\r
6467       DecrementClocks(); /* call into back end */\r
6468       break;\r
6469     case LOAD_GAME_TIMER_ID:\r
6470       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6471       loadGameTimerEvent = 0;\r
6472       AutoPlayGameLoop(); /* call into back end */\r
6473       break;\r
6474     case ANALYSIS_TIMER_ID:\r
6475       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6476                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6477         AnalysisPeriodicEvent(0);\r
6478       } else {\r
6479         KillTimer(hwnd, analysisTimerEvent);\r
6480         analysisTimerEvent = 0;\r
6481       }\r
6482       break;\r
6483     case DELAYED_TIMER_ID:\r
6484       KillTimer(hwnd, delayedTimerEvent);\r
6485       delayedTimerEvent = 0;\r
6486       delayedTimerCallback();\r
6487       break;\r
6488     }\r
6489     break;\r
6490 \r
6491   case WM_USER_Input:\r
6492     InputEvent(hwnd, message, wParam, lParam);\r
6493     break;\r
6494 \r
6495   /* [AS] Also move "attached" child windows */\r
6496   case WM_WINDOWPOSCHANGING:\r
6497 \r
6498     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6499         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6500 \r
6501         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6502             /* Window is moving */\r
6503             RECT rcMain;\r
6504 \r
6505 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6506             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6507             rcMain.right  = boardX + winWidth;\r
6508             rcMain.top    = boardY;\r
6509             rcMain.bottom = boardY + winHeight;\r
6510             \r
6511             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6512             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6513             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6514             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6515             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6516             boardX = lpwp->x;\r
6517             boardY = lpwp->y;\r
6518         }\r
6519     }\r
6520     break;\r
6521 \r
6522   /* [AS] Snapping */\r
6523   case WM_ENTERSIZEMOVE:\r
6524     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6525     if (hwnd == hwndMain) {\r
6526       doingSizing = TRUE;\r
6527       lastSizing = 0;\r
6528     }\r
6529     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6530     break;\r
6531 \r
6532   case WM_SIZING:\r
6533     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6534     if (hwnd == hwndMain) {\r
6535       lastSizing = wParam;\r
6536     }\r
6537     break;\r
6538 \r
6539   case WM_MOVING:\r
6540     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6541       return OnMoving( &sd, hwnd, wParam, lParam );\r
6542 \r
6543   case WM_EXITSIZEMOVE:\r
6544     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6545     if (hwnd == hwndMain) {\r
6546       RECT client;\r
6547       doingSizing = FALSE;\r
6548       InvalidateRect(hwnd, &boardRect, FALSE);\r
6549       GetClientRect(hwnd, &client);\r
6550       ResizeBoard(client.right, client.bottom, lastSizing);\r
6551       lastSizing = 0;\r
6552       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6553     }\r
6554     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6555     break;\r
6556 \r
6557   case WM_DESTROY: /* message: window being destroyed */\r
6558     PostQuitMessage(0);\r
6559     break;\r
6560 \r
6561   case WM_CLOSE:\r
6562     if (hwnd == hwndMain) {\r
6563       ExitEvent(0);\r
6564     }\r
6565     break;\r
6566 \r
6567   default:      /* Passes it on if unprocessed */\r
6568     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6569   }\r
6570   return 0;\r
6571 }\r
6572 \r
6573 /*---------------------------------------------------------------------------*\\r
6574  *\r
6575  * Misc utility routines\r
6576  *\r
6577 \*---------------------------------------------------------------------------*/\r
6578 \r
6579 /*\r
6580  * Decent random number generator, at least not as bad as Windows\r
6581  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6582  */\r
6583 unsigned int randstate;\r
6584 \r
6585 int\r
6586 myrandom(void)\r
6587 {\r
6588   randstate = randstate * 1664525 + 1013904223;\r
6589   return (int) randstate & 0x7fffffff;\r
6590 }\r
6591 \r
6592 void\r
6593 mysrandom(unsigned int seed)\r
6594 {\r
6595   randstate = seed;\r
6596 }\r
6597 \r
6598 \r
6599 /* \r
6600  * returns TRUE if user selects a different color, FALSE otherwise \r
6601  */\r
6602 \r
6603 BOOL\r
6604 ChangeColor(HWND hwnd, COLORREF *which)\r
6605 {\r
6606   static BOOL firstTime = TRUE;\r
6607   static DWORD customColors[16];\r
6608   CHOOSECOLOR cc;\r
6609   COLORREF newcolor;\r
6610   int i;\r
6611   ColorClass ccl;\r
6612 \r
6613   if (firstTime) {\r
6614     /* Make initial colors in use available as custom colors */\r
6615     /* Should we put the compiled-in defaults here instead? */\r
6616     i = 0;\r
6617     customColors[i++] = lightSquareColor & 0xffffff;\r
6618     customColors[i++] = darkSquareColor & 0xffffff;\r
6619     customColors[i++] = whitePieceColor & 0xffffff;\r
6620     customColors[i++] = blackPieceColor & 0xffffff;\r
6621     customColors[i++] = highlightSquareColor & 0xffffff;\r
6622     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6623 \r
6624     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6625       customColors[i++] = textAttribs[ccl].color;\r
6626     }\r
6627     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6628     firstTime = FALSE;\r
6629   }\r
6630 \r
6631   cc.lStructSize = sizeof(cc);\r
6632   cc.hwndOwner = hwnd;\r
6633   cc.hInstance = NULL;\r
6634   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6635   cc.lpCustColors = (LPDWORD) customColors;\r
6636   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6637 \r
6638   if (!ChooseColor(&cc)) return FALSE;\r
6639 \r
6640   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6641   if (newcolor == *which) return FALSE;\r
6642   *which = newcolor;\r
6643   return TRUE;\r
6644 \r
6645   /*\r
6646   InitDrawingColors();\r
6647   InvalidateRect(hwnd, &boardRect, FALSE);\r
6648   */\r
6649 }\r
6650 \r
6651 BOOLEAN\r
6652 MyLoadSound(MySound *ms)\r
6653 {\r
6654   BOOL ok = FALSE;\r
6655   struct stat st;\r
6656   FILE *f;\r
6657 \r
6658   if (ms->data) free(ms->data);\r
6659   ms->data = NULL;\r
6660 \r
6661   switch (ms->name[0]) {\r
6662   case NULLCHAR:\r
6663     /* Silence */\r
6664     ok = TRUE;\r
6665     break;\r
6666   case '$':\r
6667     /* System sound from Control Panel.  Don't preload here. */\r
6668     ok = TRUE;\r
6669     break;\r
6670   case '!':\r
6671     if (ms->name[1] == NULLCHAR) {\r
6672       /* "!" alone = silence */\r
6673       ok = TRUE;\r
6674     } else {\r
6675       /* Builtin wave resource.  Error if not found. */\r
6676       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6677       if (h == NULL) break;\r
6678       ms->data = (void *)LoadResource(hInst, h);\r
6679       if (h == NULL) break;\r
6680       ok = TRUE;\r
6681     }\r
6682     break;\r
6683   default:\r
6684     /* .wav file.  Error if not found. */\r
6685     f = fopen(ms->name, "rb");\r
6686     if (f == NULL) break;\r
6687     if (fstat(fileno(f), &st) < 0) break;\r
6688     ms->data = malloc(st.st_size);\r
6689     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6690     fclose(f);\r
6691     ok = TRUE;\r
6692     break;\r
6693   }\r
6694   if (!ok) {\r
6695     char buf[MSG_SIZ];\r
6696     sprintf(buf, "Error loading sound %s", ms->name);\r
6697     DisplayError(buf, GetLastError());\r
6698   }\r
6699   return ok;\r
6700 }\r
6701 \r
6702 BOOLEAN\r
6703 MyPlaySound(MySound *ms)\r
6704 {\r
6705   BOOLEAN ok = FALSE;\r
6706 \r
6707   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6708   switch (ms->name[0]) {\r
6709   case NULLCHAR:\r
6710         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6711     /* Silence */\r
6712     ok = TRUE;\r
6713     break;\r
6714   case '$':\r
6715     /* System sound from Control Panel (deprecated feature).\r
6716        "$" alone or an unset sound name gets default beep (still in use). */\r
6717     if (ms->name[1]) {\r
6718       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6719     }\r
6720     if (!ok) ok = MessageBeep(MB_OK);\r
6721     break; \r
6722   case '!':\r
6723     /* Builtin wave resource, or "!" alone for silence */\r
6724     if (ms->name[1]) {\r
6725       if (ms->data == NULL) return FALSE;\r
6726       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6727     } else {\r
6728       ok = TRUE;\r
6729     }\r
6730     break;\r
6731   default:\r
6732     /* .wav file.  Error if not found. */\r
6733     if (ms->data == NULL) return FALSE;\r
6734     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6735     break;\r
6736   }\r
6737   /* Don't print an error: this can happen innocently if the sound driver\r
6738      is busy; for instance, if another instance of WinBoard is playing\r
6739      a sound at about the same time. */\r
6740 #if 0\r
6741   if (!ok) {\r
6742     char buf[MSG_SIZ];\r
6743     sprintf(buf, "Error playing sound %s", ms->name);\r
6744     DisplayError(buf, GetLastError());\r
6745   }\r
6746 #endif\r
6747   return ok;\r
6748 }\r
6749 \r
6750 \r
6751 LRESULT CALLBACK\r
6752 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6753 {\r
6754   BOOL ok;\r
6755   OPENFILENAME *ofn;\r
6756   static UINT *number; /* gross that this is static */\r
6757 \r
6758   switch (message) {\r
6759   case WM_INITDIALOG: /* message: initialize dialog box */\r
6760     /* Center the dialog over the application window */\r
6761     ofn = (OPENFILENAME *) lParam;\r
6762     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6763       number = (UINT *) ofn->lCustData;\r
6764       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6765     } else {\r
6766       number = NULL;\r
6767     }\r
6768     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6769     return FALSE;  /* Allow for further processing */\r
6770 \r
6771   case WM_COMMAND:\r
6772     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6773       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6774     }\r
6775     return FALSE;  /* Allow for further processing */\r
6776   }\r
6777   return FALSE;\r
6778 }\r
6779 \r
6780 UINT APIENTRY\r
6781 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6782 {\r
6783   static UINT *number;\r
6784   OPENFILENAME *ofname;\r
6785   OFNOTIFY *ofnot;\r
6786   switch (uiMsg) {\r
6787   case WM_INITDIALOG:\r
6788     ofname = (OPENFILENAME *)lParam;\r
6789     number = (UINT *)(ofname->lCustData);\r
6790     break;\r
6791   case WM_NOTIFY:\r
6792     ofnot = (OFNOTIFY *)lParam;\r
6793     if (ofnot->hdr.code == CDN_FILEOK) {\r
6794       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6795     }\r
6796     break;\r
6797   }\r
6798   return 0;\r
6799 }\r
6800 \r
6801 \r
6802 FILE *\r
6803 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6804                char *nameFilt, char *dlgTitle, UINT *number,\r
6805                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6806 {\r
6807   OPENFILENAME openFileName;\r
6808   char buf1[MSG_SIZ];\r
6809   FILE *f;\r
6810 \r
6811   if (fileName == NULL) fileName = buf1;\r
6812   if (defName == NULL) {\r
6813     strcpy(fileName, "*.");\r
6814     strcat(fileName, defExt);\r
6815   } else {\r
6816     strcpy(fileName, defName);\r
6817   }\r
6818   if (fileTitle) strcpy(fileTitle, "");\r
6819   if (number) *number = 0;\r
6820 \r
6821   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6822   openFileName.hwndOwner         = hwnd;\r
6823   openFileName.hInstance         = (HANDLE) hInst;\r
6824   openFileName.lpstrFilter       = nameFilt;\r
6825   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6826   openFileName.nMaxCustFilter    = 0L;\r
6827   openFileName.nFilterIndex      = 1L;\r
6828   openFileName.lpstrFile         = fileName;\r
6829   openFileName.nMaxFile          = MSG_SIZ;\r
6830   openFileName.lpstrFileTitle    = fileTitle;\r
6831   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6832   openFileName.lpstrInitialDir   = NULL;\r
6833   openFileName.lpstrTitle        = dlgTitle;\r
6834   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6835     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6836     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6837     | (oldDialog ? 0 : OFN_EXPLORER);\r
6838   openFileName.nFileOffset       = 0;\r
6839   openFileName.nFileExtension    = 0;\r
6840   openFileName.lpstrDefExt       = defExt;\r
6841   openFileName.lCustData         = (LONG) number;\r
6842   openFileName.lpfnHook          = oldDialog ?\r
6843     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6844   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6845 \r
6846   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6847                         GetOpenFileName(&openFileName)) {\r
6848     /* open the file */\r
6849     f = fopen(openFileName.lpstrFile, write);\r
6850     if (f == NULL) {\r
6851       MessageBox(hwnd, "File open failed", NULL,\r
6852                  MB_OK|MB_ICONEXCLAMATION);\r
6853       return NULL;\r
6854     }\r
6855   } else {\r
6856     int err = CommDlgExtendedError();\r
6857     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6858     return FALSE;\r
6859   }\r
6860   return f;\r
6861 }\r
6862 \r
6863 \r
6864 \r
6865 VOID APIENTRY\r
6866 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6867 {\r
6868   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6869 \r
6870   /*\r
6871    * Get the first pop-up menu in the menu template. This is the\r
6872    * menu that TrackPopupMenu displays.\r
6873    */\r
6874   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6875 \r
6876   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6877 \r
6878   /*\r
6879    * TrackPopup uses screen coordinates, so convert the\r
6880    * coordinates of the mouse click to screen coordinates.\r
6881    */\r
6882   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6883 \r
6884   /* Draw and track the floating pop-up menu. */\r
6885   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6886                  pt.x, pt.y, 0, hwnd, NULL);\r
6887 \r
6888   /* Destroy the menu.*/\r
6889   DestroyMenu(hmenu);\r
6890 }\r
6891    \r
6892 typedef struct {\r
6893   HWND hDlg, hText;\r
6894   int sizeX, sizeY, newSizeX, newSizeY;\r
6895   HDWP hdwp;\r
6896 } ResizeEditPlusButtonsClosure;\r
6897 \r
6898 BOOL CALLBACK\r
6899 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6900 {\r
6901   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6902   RECT rect;\r
6903   POINT pt;\r
6904 \r
6905   if (hChild == cl->hText) return TRUE;\r
6906   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6907   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6908   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6909   ScreenToClient(cl->hDlg, &pt);\r
6910   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6911     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6912   return TRUE;\r
6913 }\r
6914 \r
6915 /* Resize a dialog that has a (rich) edit field filling most of\r
6916    the top, with a row of buttons below */\r
6917 VOID\r
6918 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6919 {\r
6920   RECT rectText;\r
6921   int newTextHeight, newTextWidth;\r
6922   ResizeEditPlusButtonsClosure cl;\r
6923   \r
6924   /*if (IsIconic(hDlg)) return;*/\r
6925   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6926   \r
6927   cl.hdwp = BeginDeferWindowPos(8);\r
6928 \r
6929   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6930   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6931   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6932   if (newTextHeight < 0) {\r
6933     newSizeY += -newTextHeight;\r
6934     newTextHeight = 0;\r
6935   }\r
6936   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6937     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6938 \r
6939   cl.hDlg = hDlg;\r
6940   cl.hText = hText;\r
6941   cl.sizeX = sizeX;\r
6942   cl.sizeY = sizeY;\r
6943   cl.newSizeX = newSizeX;\r
6944   cl.newSizeY = newSizeY;\r
6945   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6946 \r
6947   EndDeferWindowPos(cl.hdwp);\r
6948 }\r
6949 \r
6950 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6951 {\r
6952     RECT    rChild, rParent;\r
6953     int     wChild, hChild, wParent, hParent;\r
6954     int     wScreen, hScreen, xNew, yNew;\r
6955     HDC     hdc;\r
6956 \r
6957     /* Get the Height and Width of the child window */\r
6958     GetWindowRect (hwndChild, &rChild);\r
6959     wChild = rChild.right - rChild.left;\r
6960     hChild = rChild.bottom - rChild.top;\r
6961 \r
6962     /* Get the Height and Width of the parent window */\r
6963     GetWindowRect (hwndParent, &rParent);\r
6964     wParent = rParent.right - rParent.left;\r
6965     hParent = rParent.bottom - rParent.top;\r
6966 \r
6967     /* Get the display limits */\r
6968     hdc = GetDC (hwndChild);\r
6969     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6970     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6971     ReleaseDC(hwndChild, hdc);\r
6972 \r
6973     /* Calculate new X position, then adjust for screen */\r
6974     xNew = rParent.left + ((wParent - wChild) /2);\r
6975     if (xNew < 0) {\r
6976         xNew = 0;\r
6977     } else if ((xNew+wChild) > wScreen) {\r
6978         xNew = wScreen - wChild;\r
6979     }\r
6980 \r
6981     /* Calculate new Y position, then adjust for screen */\r
6982     if( mode == 0 ) {\r
6983         yNew = rParent.top  + ((hParent - hChild) /2);\r
6984     }\r
6985     else {\r
6986         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6987     }\r
6988 \r
6989     if (yNew < 0) {\r
6990         yNew = 0;\r
6991     } else if ((yNew+hChild) > hScreen) {\r
6992         yNew = hScreen - hChild;\r
6993     }\r
6994 \r
6995     /* Set it, and return */\r
6996     return SetWindowPos (hwndChild, NULL,\r
6997                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6998 }\r
6999 \r
7000 /* Center one window over another */\r
7001 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
7002 {\r
7003     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
7004 }\r
7005 \r
7006 /*---------------------------------------------------------------------------*\\r
7007  *\r
7008  * Startup Dialog functions\r
7009  *\r
7010 \*---------------------------------------------------------------------------*/\r
7011 void\r
7012 InitComboStrings(HANDLE hwndCombo, char **cd)\r
7013 {\r
7014   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7015 \r
7016   while (*cd != NULL) {\r
7017     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
7018     cd++;\r
7019   }\r
7020 }\r
7021 \r
7022 void\r
7023 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
7024 {\r
7025   char buf1[ARG_MAX];\r
7026   int len;\r
7027 \r
7028   if (str[0] == '@') {\r
7029     FILE* f = fopen(str + 1, "r");\r
7030     if (f == NULL) {\r
7031       DisplayFatalError(str + 1, errno, 2);\r
7032       return;\r
7033     }\r
7034     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7035     fclose(f);\r
7036     buf1[len] = NULLCHAR;\r
7037     str = buf1;\r
7038   }\r
7039 \r
7040   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7041 \r
7042   for (;;) {\r
7043     char buf[MSG_SIZ];\r
7044     char *end = strchr(str, '\n');\r
7045     if (end == NULL) return;\r
7046     memcpy(buf, str, end - str);\r
7047     buf[end - str] = NULLCHAR;\r
7048     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7049     str = end + 1;\r
7050   }\r
7051 }\r
7052 \r
7053 void\r
7054 SetStartupDialogEnables(HWND hDlg)\r
7055 {\r
7056   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7057     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7058     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7059   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7060     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7061   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7062     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7063   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7064     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7065   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7066     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7067     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7068     IsDlgButtonChecked(hDlg, OPT_View));\r
7069 }\r
7070 \r
7071 char *\r
7072 QuoteForFilename(char *filename)\r
7073 {\r
7074   int dquote, space;\r
7075   dquote = strchr(filename, '"') != NULL;\r
7076   space = strchr(filename, ' ') != NULL;\r
7077   if (dquote || space) {\r
7078     if (dquote) {\r
7079       return "'";\r
7080     } else {\r
7081       return "\"";\r
7082     }\r
7083   } else {\r
7084     return "";\r
7085   }\r
7086 }\r
7087 \r
7088 VOID\r
7089 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7090 {\r
7091   char buf[MSG_SIZ];\r
7092   char *q;\r
7093 \r
7094   InitComboStringsFromOption(hwndCombo, nthnames);\r
7095   q = QuoteForFilename(nthcp);\r
7096   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7097   if (*nthdir != NULLCHAR) {\r
7098     q = QuoteForFilename(nthdir);\r
7099     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7100   }\r
7101   if (*nthcp == NULLCHAR) {\r
7102     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7103   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7104     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7105     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7106   }\r
7107 }\r
7108 \r
7109 LRESULT CALLBACK\r
7110 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7111 {\r
7112   char buf[MSG_SIZ];\r
7113   HANDLE hwndCombo;\r
7114   char *p;\r
7115 \r
7116   switch (message) {\r
7117   case WM_INITDIALOG:\r
7118     /* Center the dialog */\r
7119     CenterWindow (hDlg, GetDesktopWindow());\r
7120     /* Initialize the dialog items */\r
7121     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7122                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7123                   firstChessProgramNames);\r
7124     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7125                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7126                   secondChessProgramNames);\r
7127     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7128     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7129     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7130     if (*appData.icsHelper != NULLCHAR) {\r
7131       char *q = QuoteForFilename(appData.icsHelper);\r
7132       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7133     }\r
7134     if (*appData.icsHost == NULLCHAR) {\r
7135       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7136       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7137     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7138       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7139       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7140     }\r
7141 \r
7142     if (appData.icsActive) {\r
7143       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7144     }\r
7145     else if (appData.noChessProgram) {\r
7146       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7147     }\r
7148     else {\r
7149       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7150     }\r
7151 \r
7152     SetStartupDialogEnables(hDlg);\r
7153     return TRUE;\r
7154 \r
7155   case WM_COMMAND:\r
7156     switch (LOWORD(wParam)) {\r
7157     case IDOK:\r
7158       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7159         strcpy(buf, "/fcp=");\r
7160         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7161         p = buf;\r
7162         ParseArgs(StringGet, &p);\r
7163         strcpy(buf, "/scp=");\r
7164         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7165         p = buf;\r
7166         ParseArgs(StringGet, &p);\r
7167         appData.noChessProgram = FALSE;\r
7168         appData.icsActive = FALSE;\r
7169       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7170         strcpy(buf, "/ics /icshost=");\r
7171         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7172         p = buf;\r
7173         ParseArgs(StringGet, &p);\r
7174         if (appData.zippyPlay) {\r
7175           strcpy(buf, "/fcp=");\r
7176           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7177           p = buf;\r
7178           ParseArgs(StringGet, &p);\r
7179         }\r
7180       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7181         appData.noChessProgram = TRUE;\r
7182         appData.icsActive = FALSE;\r
7183       } else {\r
7184         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7185                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7186         return TRUE;\r
7187       }\r
7188       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7189         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7190         p = buf;\r
7191         ParseArgs(StringGet, &p);\r
7192       }\r
7193       EndDialog(hDlg, TRUE);\r
7194       return TRUE;\r
7195 \r
7196     case IDCANCEL:\r
7197       ExitEvent(0);\r
7198       return TRUE;\r
7199 \r
7200     case IDM_HELPCONTENTS:\r
7201       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7202         MessageBox (GetFocus(),\r
7203                     "Unable to activate help",\r
7204                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7205       }\r
7206       break;\r
7207 \r
7208     default:\r
7209       SetStartupDialogEnables(hDlg);\r
7210       break;\r
7211     }\r
7212     break;\r
7213   }\r
7214   return FALSE;\r
7215 }\r
7216 \r
7217 /*---------------------------------------------------------------------------*\\r
7218  *\r
7219  * About box dialog functions\r
7220  *\r
7221 \*---------------------------------------------------------------------------*/\r
7222 \r
7223 /* Process messages for "About" dialog box */\r
7224 LRESULT CALLBACK\r
7225 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7226 {\r
7227   switch (message) {\r
7228   case WM_INITDIALOG: /* message: initialize dialog box */\r
7229     /* Center the dialog over the application window */\r
7230     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7231     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7232     JAWS_COPYRIGHT\r
7233     return (TRUE);\r
7234 \r
7235   case WM_COMMAND: /* message: received a command */\r
7236     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7237         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7238       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7239       return (TRUE);\r
7240     }\r
7241     break;\r
7242   }\r
7243   return (FALSE);\r
7244 }\r
7245 \r
7246 /*---------------------------------------------------------------------------*\\r
7247  *\r
7248  * Comment Dialog functions\r
7249  *\r
7250 \*---------------------------------------------------------------------------*/\r
7251 \r
7252 LRESULT CALLBACK\r
7253 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7254 {\r
7255   static HANDLE hwndText = NULL;\r
7256   int len, newSizeX, newSizeY, flags;\r
7257   static int sizeX, sizeY;\r
7258   char *str;\r
7259   RECT rect;\r
7260   MINMAXINFO *mmi;\r
7261 \r
7262   switch (message) {\r
7263   case WM_INITDIALOG: /* message: initialize dialog box */\r
7264     /* Initialize the dialog items */\r
7265     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7266     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7267     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7268     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7269     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7270     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7271     SetWindowText(hDlg, commentTitle);\r
7272     if (editComment) {\r
7273       SetFocus(hwndText);\r
7274     } else {\r
7275       SetFocus(GetDlgItem(hDlg, IDOK));\r
7276     }\r
7277     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7278                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7279                 MAKELPARAM(FALSE, 0));\r
7280     /* Size and position the dialog */\r
7281     if (!commentDialog) {\r
7282       commentDialog = hDlg;\r
7283       flags = SWP_NOZORDER;\r
7284       GetClientRect(hDlg, &rect);\r
7285       sizeX = rect.right;\r
7286       sizeY = rect.bottom;\r
7287       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7288           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7289         WINDOWPLACEMENT wp;\r
7290         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7291         wp.length = sizeof(WINDOWPLACEMENT);\r
7292         wp.flags = 0;\r
7293         wp.showCmd = SW_SHOW;\r
7294         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7295         wp.rcNormalPosition.left = commentX;\r
7296         wp.rcNormalPosition.right = commentX + commentW;\r
7297         wp.rcNormalPosition.top = commentY;\r
7298         wp.rcNormalPosition.bottom = commentY + commentH;\r
7299         SetWindowPlacement(hDlg, &wp);\r
7300 \r
7301         GetClientRect(hDlg, &rect);\r
7302         newSizeX = rect.right;\r
7303         newSizeY = rect.bottom;\r
7304         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7305                               newSizeX, newSizeY);\r
7306         sizeX = newSizeX;\r
7307         sizeY = newSizeY;\r
7308       }\r
7309     }\r
7310     return FALSE;\r
7311 \r
7312   case WM_COMMAND: /* message: received a command */\r
7313     switch (LOWORD(wParam)) {\r
7314     case IDOK:\r
7315       if (editComment) {\r
7316         char *p, *q;\r
7317         /* Read changed options from the dialog box */\r
7318         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7319         len = GetWindowTextLength(hwndText);\r
7320         str = (char *) malloc(len + 1);\r
7321         GetWindowText(hwndText, str, len + 1);\r
7322         p = q = str;\r
7323         while (*q) {\r
7324           if (*q == '\r')\r
7325             q++;\r
7326           else\r
7327             *p++ = *q++;\r
7328         }\r
7329         *p = NULLCHAR;\r
7330         ReplaceComment(commentIndex, str);\r
7331         free(str);\r
7332       }\r
7333       CommentPopDown();\r
7334       return TRUE;\r
7335 \r
7336     case IDCANCEL:\r
7337     case OPT_CancelComment:\r
7338       CommentPopDown();\r
7339       return TRUE;\r
7340 \r
7341     case OPT_ClearComment:\r
7342       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7343       break;\r
7344 \r
7345     case OPT_EditComment:\r
7346       EditCommentEvent();\r
7347       return TRUE;\r
7348 \r
7349     default:\r
7350       break;\r
7351     }\r
7352     break;\r
7353 \r
7354   case WM_SIZE:\r
7355     newSizeX = LOWORD(lParam);\r
7356     newSizeY = HIWORD(lParam);\r
7357     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7358     sizeX = newSizeX;\r
7359     sizeY = newSizeY;\r
7360     break;\r
7361 \r
7362   case WM_GETMINMAXINFO:\r
7363     /* Prevent resizing window too small */\r
7364     mmi = (MINMAXINFO *) lParam;\r
7365     mmi->ptMinTrackSize.x = 100;\r
7366     mmi->ptMinTrackSize.y = 100;\r
7367     break;\r
7368   }\r
7369   return FALSE;\r
7370 }\r
7371 \r
7372 VOID\r
7373 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7374 {\r
7375   FARPROC lpProc;\r
7376   char *p, *q;\r
7377 \r
7378   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7379 \r
7380   if (str == NULL) str = "";\r
7381   p = (char *) malloc(2 * strlen(str) + 2);\r
7382   q = p;\r
7383   while (*str) {\r
7384     if (*str == '\n') *q++ = '\r';\r
7385     *q++ = *str++;\r
7386   }\r
7387   *q = NULLCHAR;\r
7388   if (commentText != NULL) free(commentText);\r
7389 \r
7390   commentIndex = index;\r
7391   commentTitle = title;\r
7392   commentText = p;\r
7393   editComment = edit;\r
7394 \r
7395   if (commentDialog) {\r
7396     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7397     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7398   } else {\r
7399     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7400     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7401                  hwndMain, (DLGPROC)lpProc);\r
7402     FreeProcInstance(lpProc);\r
7403   }\r
7404   commentDialogUp = TRUE;\r
7405 }\r
7406 \r
7407 \r
7408 /*---------------------------------------------------------------------------*\\r
7409  *\r
7410  * Type-in move dialog functions\r
7411  * \r
7412 \*---------------------------------------------------------------------------*/\r
7413 \r
7414 LRESULT CALLBACK\r
7415 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7416 {\r
7417   char move[MSG_SIZ];\r
7418   HWND hInput;\r
7419   ChessMove moveType;\r
7420   int fromX, fromY, toX, toY;\r
7421   char promoChar;\r
7422 \r
7423   switch (message) {\r
7424   case WM_INITDIALOG:\r
7425     move[0] = (char) lParam;\r
7426     move[1] = NULLCHAR;\r
7427     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7428     hInput = GetDlgItem(hDlg, OPT_Move);\r
7429     SetWindowText(hInput, move);\r
7430     SetFocus(hInput);\r
7431     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7432     return FALSE;\r
7433 \r
7434   case WM_COMMAND:\r
7435     switch (LOWORD(wParam)) {\r
7436     case IDOK:\r
7437       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7438       { int n; Board board;\r
7439         // [HGM] FENedit\r
7440         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7441                 EditPositionPasteFEN(move);\r
7442                 EndDialog(hDlg, TRUE);\r
7443                 return TRUE;\r
7444         }\r
7445         // [HGM] movenum: allow move number to be typed in any mode\r
7446         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7447           currentMove = 2*n-1;\r
7448           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7449           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7450           EndDialog(hDlg, TRUE);\r
7451           DrawPosition(TRUE, boards[currentMove]);\r
7452           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7453           else DisplayMessage("", "");\r
7454           return TRUE;\r
7455         }\r
7456       }\r
7457       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7458         gameMode != Training) {\r
7459         DisplayMoveError("Displayed move is not current");\r
7460       } else {\r
7461 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7462         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7463           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7464         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7465         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7466           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7467           if (gameMode != Training)\r
7468               forwardMostMove = currentMove;\r
7469           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7470         } else {\r
7471           DisplayMoveError("Could not parse move");\r
7472         }\r
7473       }\r
7474       EndDialog(hDlg, TRUE);\r
7475       return TRUE;\r
7476     case IDCANCEL:\r
7477       EndDialog(hDlg, FALSE);\r
7478       return TRUE;\r
7479     default:\r
7480       break;\r
7481     }\r
7482     break;\r
7483   }\r
7484   return FALSE;\r
7485 }\r
7486 \r
7487 VOID\r
7488 PopUpMoveDialog(char firstchar)\r
7489 {\r
7490     FARPROC lpProc;\r
7491     \r
7492     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7493         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7494         gameMode == AnalyzeMode || gameMode == EditGame || \r
7495         gameMode == EditPosition || gameMode == IcsExamining ||\r
7496         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7497         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7498                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7499                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7500         gameMode == Training) {\r
7501       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7502       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7503         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7504       FreeProcInstance(lpProc);\r
7505     }\r
7506 }\r
7507 \r
7508 /*---------------------------------------------------------------------------*\\r
7509  *\r
7510  * Type-in name dialog functions\r
7511  * \r
7512 \*---------------------------------------------------------------------------*/\r
7513 \r
7514 LRESULT CALLBACK\r
7515 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7516 {\r
7517   char move[MSG_SIZ];\r
7518   HWND hInput;\r
7519 \r
7520   switch (message) {\r
7521   case WM_INITDIALOG:\r
7522     move[0] = (char) lParam;\r
7523     move[1] = NULLCHAR;\r
7524     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7525     hInput = GetDlgItem(hDlg, OPT_Name);\r
7526     SetWindowText(hInput, move);\r
7527     SetFocus(hInput);\r
7528     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7529     return FALSE;\r
7530 \r
7531   case WM_COMMAND:\r
7532     switch (LOWORD(wParam)) {\r
7533     case IDOK:\r
7534       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7535       appData.userName = strdup(move);\r
7536       SetUserLogo();\r
7537 \r
7538       EndDialog(hDlg, TRUE);\r
7539       return TRUE;\r
7540     case IDCANCEL:\r
7541       EndDialog(hDlg, FALSE);\r
7542       return TRUE;\r
7543     default:\r
7544       break;\r
7545     }\r
7546     break;\r
7547   }\r
7548   return FALSE;\r
7549 }\r
7550 \r
7551 VOID\r
7552 PopUpNameDialog(char firstchar)\r
7553 {\r
7554     FARPROC lpProc;\r
7555     \r
7556       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7557       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7558         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7559       FreeProcInstance(lpProc);\r
7560 }\r
7561 \r
7562 /*---------------------------------------------------------------------------*\\r
7563  *\r
7564  *  Error dialogs\r
7565  * \r
7566 \*---------------------------------------------------------------------------*/\r
7567 \r
7568 /* Nonmodal error box */\r
7569 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7570                              WPARAM wParam, LPARAM lParam);\r
7571 \r
7572 VOID\r
7573 ErrorPopUp(char *title, char *content)\r
7574 {\r
7575   FARPROC lpProc;\r
7576   char *p, *q;\r
7577   BOOLEAN modal = hwndMain == NULL;\r
7578 \r
7579   p = content;\r
7580   q = errorMessage;\r
7581   while (*p) {\r
7582     if (*p == '\n') {\r
7583       if (modal) {\r
7584         *q++ = ' ';\r
7585         p++;\r
7586       } else {\r
7587         *q++ = '\r';\r
7588         *q++ = *p++;\r
7589       }\r
7590     } else {\r
7591       *q++ = *p++;\r
7592     }\r
7593   }\r
7594   *q = NULLCHAR;\r
7595   strncpy(errorTitle, title, sizeof(errorTitle));\r
7596   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7597   \r
7598   if (modal) {\r
7599     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7600   } else {\r
7601     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7602     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7603                  hwndMain, (DLGPROC)lpProc);\r
7604     FreeProcInstance(lpProc);\r
7605   }\r
7606 }\r
7607 \r
7608 VOID\r
7609 ErrorPopDown()\r
7610 {\r
7611   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7612   if (errorDialog == NULL) return;\r
7613   DestroyWindow(errorDialog);\r
7614   errorDialog = NULL;\r
7615 }\r
7616 \r
7617 LRESULT CALLBACK\r
7618 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7619 {\r
7620   HANDLE hwndText;\r
7621   RECT rChild;\r
7622 \r
7623   switch (message) {\r
7624   case WM_INITDIALOG:\r
7625     GetWindowRect(hDlg, &rChild);\r
7626 \r
7627     /*\r
7628     SetWindowPos(hDlg, NULL, rChild.left,\r
7629       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7630       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7631     */\r
7632 \r
7633     /* \r
7634         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7635         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7636         and it doesn't work when you resize the dialog.\r
7637         For now, just give it a default position.\r
7638     */\r
7639     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7640 \r
7641     errorDialog = hDlg;\r
7642     SetWindowText(hDlg, errorTitle);\r
7643     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7644     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7645     return FALSE;\r
7646 \r
7647   case WM_COMMAND:\r
7648     switch (LOWORD(wParam)) {\r
7649     case IDOK:\r
7650     case IDCANCEL:\r
7651       if (errorDialog == hDlg) errorDialog = NULL;\r
7652       DestroyWindow(hDlg);\r
7653       return TRUE;\r
7654 \r
7655     default:\r
7656       break;\r
7657     }\r
7658     break;\r
7659   }\r
7660   return FALSE;\r
7661 }\r
7662 \r
7663 #ifdef GOTHIC\r
7664 HWND gothicDialog = NULL;\r
7665 \r
7666 LRESULT CALLBACK\r
7667 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7668 {\r
7669   HANDLE hwndText;\r
7670   RECT rChild;\r
7671   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7672 \r
7673   switch (message) {\r
7674   case WM_INITDIALOG:\r
7675     GetWindowRect(hDlg, &rChild);\r
7676 \r
7677     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7678                                                              SWP_NOZORDER);\r
7679 \r
7680     /* \r
7681         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7682         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7683         and it doesn't work when you resize the dialog.\r
7684         For now, just give it a default position.\r
7685     */\r
7686     gothicDialog = hDlg;\r
7687     SetWindowText(hDlg, errorTitle);\r
7688     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7689     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7690     return FALSE;\r
7691 \r
7692   case WM_COMMAND:\r
7693     switch (LOWORD(wParam)) {\r
7694     case IDOK:\r
7695     case IDCANCEL:\r
7696       if (errorDialog == hDlg) errorDialog = NULL;\r
7697       DestroyWindow(hDlg);\r
7698       return TRUE;\r
7699 \r
7700     default:\r
7701       break;\r
7702     }\r
7703     break;\r
7704   }\r
7705   return FALSE;\r
7706 }\r
7707 \r
7708 VOID\r
7709 GothicPopUp(char *title, VariantClass variant)\r
7710 {\r
7711   FARPROC lpProc;\r
7712   static char *lastTitle;\r
7713 \r
7714   strncpy(errorTitle, title, sizeof(errorTitle));\r
7715   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7716 \r
7717   if(lastTitle != title && gothicDialog != NULL) {\r
7718     DestroyWindow(gothicDialog);\r
7719     gothicDialog = NULL;\r
7720   }\r
7721   if(variant != VariantNormal && gothicDialog == NULL) {\r
7722     title = lastTitle;\r
7723     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7724     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7725                  hwndMain, (DLGPROC)lpProc);\r
7726     FreeProcInstance(lpProc);\r
7727   }\r
7728 }\r
7729 #endif\r
7730 \r
7731 /*---------------------------------------------------------------------------*\\r
7732  *\r
7733  *  Ics Interaction console functions\r
7734  *\r
7735 \*---------------------------------------------------------------------------*/\r
7736 \r
7737 #define HISTORY_SIZE 64\r
7738 static char *history[HISTORY_SIZE];\r
7739 int histIn = 0, histP = 0;\r
7740 \r
7741 VOID\r
7742 SaveInHistory(char *cmd)\r
7743 {\r
7744   if (history[histIn] != NULL) {\r
7745     free(history[histIn]);\r
7746     history[histIn] = NULL;\r
7747   }\r
7748   if (*cmd == NULLCHAR) return;\r
7749   history[histIn] = StrSave(cmd);\r
7750   histIn = (histIn + 1) % HISTORY_SIZE;\r
7751   if (history[histIn] != NULL) {\r
7752     free(history[histIn]);\r
7753     history[histIn] = NULL;\r
7754   }\r
7755   histP = histIn;\r
7756 }\r
7757 \r
7758 char *\r
7759 PrevInHistory(char *cmd)\r
7760 {\r
7761   int newhp;\r
7762   if (histP == histIn) {\r
7763     if (history[histIn] != NULL) free(history[histIn]);\r
7764     history[histIn] = StrSave(cmd);\r
7765   }\r
7766   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7767   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7768   histP = newhp;\r
7769   return history[histP];\r
7770 }\r
7771 \r
7772 char *\r
7773 NextInHistory()\r
7774 {\r
7775   if (histP == histIn) return NULL;\r
7776   histP = (histP + 1) % HISTORY_SIZE;\r
7777   return history[histP];\r
7778 }\r
7779 \r
7780 typedef struct {\r
7781   char *item;\r
7782   char *command;\r
7783   BOOLEAN getname;\r
7784   BOOLEAN immediate;\r
7785 } IcsTextMenuEntry;\r
7786 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7787 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7788 \r
7789 void\r
7790 ParseIcsTextMenu(char *icsTextMenuString)\r
7791 {\r
7792 //  int flags = 0;\r
7793   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7794   char *p = icsTextMenuString;\r
7795   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7796     free(e->item);\r
7797     e->item = NULL;\r
7798     if (e->command != NULL) {\r
7799       free(e->command);\r
7800       e->command = NULL;\r
7801     }\r
7802     e++;\r
7803   }\r
7804   e = icsTextMenuEntry;\r
7805   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7806     if (*p == ';' || *p == '\n') {\r
7807       e->item = strdup("-");\r
7808       e->command = NULL;\r
7809       p++;\r
7810     } else if (*p == '-') {\r
7811       e->item = strdup("-");\r
7812       e->command = NULL;\r
7813       p++;\r
7814       if (*p) p++;\r
7815     } else {\r
7816       char *q, *r, *s, *t;\r
7817       char c;\r
7818       q = strchr(p, ',');\r
7819       if (q == NULL) break;\r
7820       *q = NULLCHAR;\r
7821       r = strchr(q + 1, ',');\r
7822       if (r == NULL) break;\r
7823       *r = NULLCHAR;\r
7824       s = strchr(r + 1, ',');\r
7825       if (s == NULL) break;\r
7826       *s = NULLCHAR;\r
7827       c = ';';\r
7828       t = strchr(s + 1, c);\r
7829       if (t == NULL) {\r
7830         c = '\n';\r
7831         t = strchr(s + 1, c);\r
7832       }\r
7833       if (t != NULL) *t = NULLCHAR;\r
7834       e->item = strdup(p);\r
7835       e->command = strdup(q + 1);\r
7836       e->getname = *(r + 1) != '0';\r
7837       e->immediate = *(s + 1) != '0';\r
7838       *q = ',';\r
7839       *r = ',';\r
7840       *s = ',';\r
7841       if (t == NULL) break;\r
7842       *t = c;\r
7843       p = t + 1;\r
7844     }\r
7845     e++;\r
7846   } \r
7847 }\r
7848 \r
7849 HMENU\r
7850 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7851 {\r
7852   HMENU hmenu, h;\r
7853   int i = 0;\r
7854   hmenu = LoadMenu(hInst, "TextMenu");\r
7855   h = GetSubMenu(hmenu, 0);\r
7856   while (e->item) {\r
7857     if (strcmp(e->item, "-") == 0) {\r
7858       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7859     } else {\r
7860       if (e->item[0] == '|') {\r
7861         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7862                    IDM_CommandX + i, &e->item[1]);\r
7863       } else {\r
7864         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7865       }\r
7866     }\r
7867     e++;\r
7868     i++;\r
7869   } \r
7870   return hmenu;\r
7871 }\r
7872 \r
7873 WNDPROC consoleTextWindowProc;\r
7874 \r
7875 void\r
7876 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7877 {\r
7878   char buf[MSG_SIZ], name[MSG_SIZ];\r
7879   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7880   CHARRANGE sel;\r
7881 \r
7882   if (!getname) {\r
7883     SetWindowText(hInput, command);\r
7884     if (immediate) {\r
7885       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7886     } else {\r
7887       sel.cpMin = 999999;\r
7888       sel.cpMax = 999999;\r
7889       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7890       SetFocus(hInput);\r
7891     }\r
7892     return;\r
7893   }    \r
7894   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7895   if (sel.cpMin == sel.cpMax) {\r
7896     /* Expand to surrounding word */\r
7897     TEXTRANGE tr;\r
7898     do {\r
7899       tr.chrg.cpMax = sel.cpMin;\r
7900       tr.chrg.cpMin = --sel.cpMin;\r
7901       if (sel.cpMin < 0) break;\r
7902       tr.lpstrText = name;\r
7903       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7904     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7905     sel.cpMin++;\r
7906 \r
7907     do {\r
7908       tr.chrg.cpMin = sel.cpMax;\r
7909       tr.chrg.cpMax = ++sel.cpMax;\r
7910       tr.lpstrText = name;\r
7911       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7912     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7913     sel.cpMax--;\r
7914 \r
7915     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7916       MessageBeep(MB_ICONEXCLAMATION);\r
7917       return;\r
7918     }\r
7919     tr.chrg = sel;\r
7920     tr.lpstrText = name;\r
7921     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7922   } else {\r
7923     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7924       MessageBeep(MB_ICONEXCLAMATION);\r
7925       return;\r
7926     }\r
7927     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7928   }\r
7929   if (immediate) {\r
7930     sprintf(buf, "%s %s", command, name);\r
7931     SetWindowText(hInput, buf);\r
7932     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7933   } else {\r
7934     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7935     SetWindowText(hInput, buf);\r
7936     sel.cpMin = 999999;\r
7937     sel.cpMax = 999999;\r
7938     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7939     SetFocus(hInput);\r
7940   }\r
7941 }\r
7942 \r
7943 LRESULT CALLBACK \r
7944 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7945 {\r
7946   HWND hInput;\r
7947   CHARRANGE sel;\r
7948 \r
7949   switch (message) {\r
7950   case WM_KEYDOWN:\r
7951     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7952     switch (wParam) {\r
7953     case VK_PRIOR:\r
7954       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7955       return 0;\r
7956     case VK_NEXT:\r
7957       sel.cpMin = 999999;\r
7958       sel.cpMax = 999999;\r
7959       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7960       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7961       return 0;\r
7962     }\r
7963     break;\r
7964   case WM_CHAR:\r
7965    if(wParam != '\022') {\r
7966     if (wParam == '\t') {\r
7967       if (GetKeyState(VK_SHIFT) < 0) {\r
7968         /* shifted */\r
7969         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7970         if (buttonDesc[0].hwnd) {\r
7971           SetFocus(buttonDesc[0].hwnd);\r
7972         } else {\r
7973           SetFocus(hwndMain);\r
7974         }\r
7975       } else {\r
7976         /* unshifted */\r
7977         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7978       }\r
7979     } else {\r
7980       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7981       JAWS_DELETE( SetFocus(hInput); )\r
7982       SendMessage(hInput, message, wParam, lParam);\r
7983     }\r
7984     return 0;\r
7985    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7986   case WM_RBUTTONUP:\r
7987     if (GetKeyState(VK_SHIFT) & ~1) {\r
7988       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7989         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7990     } else {\r
7991       POINT pt;\r
7992       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7993       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7994       if (sel.cpMin == sel.cpMax) {\r
7995         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7996         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7997       }\r
7998       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7999         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8000       }\r
8001       pt.x = LOWORD(lParam);\r
8002       pt.y = HIWORD(lParam);\r
8003       MenuPopup(hwnd, pt, hmenu, -1);\r
8004     }\r
8005     return 0;\r
8006   case WM_PASTE:\r
8007     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8008     SetFocus(hInput);\r
8009     return SendMessage(hInput, message, wParam, lParam);\r
8010   case WM_MBUTTONDOWN:\r
8011     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8012   case WM_RBUTTONDOWN:\r
8013     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
8014       /* Move selection here if it was empty */\r
8015       POINT pt;\r
8016       pt.x = LOWORD(lParam);\r
8017       pt.y = HIWORD(lParam);\r
8018       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8019       if (sel.cpMin == sel.cpMax) {\r
8020         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
8021         sel.cpMax = sel.cpMin;\r
8022         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8023       }\r
8024       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
8025     }\r
8026     return 0;\r
8027   case WM_COMMAND:\r
8028     switch (LOWORD(wParam)) {\r
8029     case IDM_QuickPaste:\r
8030       {\r
8031         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8032         if (sel.cpMin == sel.cpMax) {\r
8033           MessageBeep(MB_ICONEXCLAMATION);\r
8034           return 0;\r
8035         }\r
8036         SendMessage(hwnd, WM_COPY, 0, 0);\r
8037         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8038         SendMessage(hInput, WM_PASTE, 0, 0);\r
8039         SetFocus(hInput);\r
8040         return 0;\r
8041       }\r
8042     case IDM_Cut:\r
8043       SendMessage(hwnd, WM_CUT, 0, 0);\r
8044       return 0;\r
8045     case IDM_Paste:\r
8046       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8047       return 0;\r
8048     case IDM_Copy:\r
8049       SendMessage(hwnd, WM_COPY, 0, 0);\r
8050       return 0;\r
8051     default:\r
8052       {\r
8053         int i = LOWORD(wParam) - IDM_CommandX;\r
8054         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8055             icsTextMenuEntry[i].command != NULL) {\r
8056           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8057                    icsTextMenuEntry[i].getname,\r
8058                    icsTextMenuEntry[i].immediate);\r
8059           return 0;\r
8060         }\r
8061       }\r
8062       break;\r
8063     }\r
8064     break;\r
8065   }\r
8066   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8067 }\r
8068 \r
8069 WNDPROC consoleInputWindowProc;\r
8070 \r
8071 LRESULT CALLBACK\r
8072 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8073 {\r
8074   char buf[MSG_SIZ];\r
8075   char *p;\r
8076   static BOOL sendNextChar = FALSE;\r
8077   static BOOL quoteNextChar = FALSE;\r
8078   InputSource *is = consoleInputSource;\r
8079   CHARFORMAT cf;\r
8080   CHARRANGE sel;\r
8081 \r
8082   switch (message) {\r
8083   case WM_CHAR:\r
8084     if (!appData.localLineEditing || sendNextChar) {\r
8085       is->buf[0] = (CHAR) wParam;\r
8086       is->count = 1;\r
8087       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8088       sendNextChar = FALSE;\r
8089       return 0;\r
8090     }\r
8091     if (quoteNextChar) {\r
8092       buf[0] = (char) wParam;\r
8093       buf[1] = NULLCHAR;\r
8094       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8095       quoteNextChar = FALSE;\r
8096       return 0;\r
8097     }\r
8098     switch (wParam) {\r
8099     case '\r':   /* Enter key */\r
8100       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8101       if (consoleEcho) SaveInHistory(is->buf);\r
8102       is->buf[is->count++] = '\n';\r
8103       is->buf[is->count] = NULLCHAR;\r
8104       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8105       if (consoleEcho) {\r
8106         ConsoleOutput(is->buf, is->count, TRUE);\r
8107       } else if (appData.localLineEditing) {\r
8108         ConsoleOutput("\n", 1, TRUE);\r
8109       }\r
8110       /* fall thru */\r
8111     case '\033': /* Escape key */\r
8112       SetWindowText(hwnd, "");\r
8113       cf.cbSize = sizeof(CHARFORMAT);\r
8114       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8115       if (consoleEcho) {\r
8116         cf.crTextColor = textAttribs[ColorNormal].color;\r
8117       } else {\r
8118         cf.crTextColor = COLOR_ECHOOFF;\r
8119       }\r
8120       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8121       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8122       return 0;\r
8123     case '\t':   /* Tab key */\r
8124       if (GetKeyState(VK_SHIFT) < 0) {\r
8125         /* shifted */\r
8126         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8127       } else {\r
8128         /* unshifted */\r
8129         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8130         if (buttonDesc[0].hwnd) {\r
8131           SetFocus(buttonDesc[0].hwnd);\r
8132         } else {\r
8133           SetFocus(hwndMain);\r
8134         }\r
8135       }\r
8136       return 0;\r
8137     case '\023': /* Ctrl+S */\r
8138       sendNextChar = TRUE;\r
8139       return 0;\r
8140     case '\021': /* Ctrl+Q */\r
8141       quoteNextChar = TRUE;\r
8142       return 0;\r
8143     JAWS_REPLAY\r
8144     default:\r
8145       break;\r
8146     }\r
8147     break;\r
8148   case WM_KEYDOWN:\r
8149     switch (wParam) {\r
8150     case VK_UP:\r
8151       GetWindowText(hwnd, buf, MSG_SIZ);\r
8152       p = PrevInHistory(buf);\r
8153       if (p != NULL) {\r
8154         SetWindowText(hwnd, p);\r
8155         sel.cpMin = 999999;\r
8156         sel.cpMax = 999999;\r
8157         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8158         return 0;\r
8159       }\r
8160       break;\r
8161     case VK_DOWN:\r
8162       p = NextInHistory();\r
8163       if (p != NULL) {\r
8164         SetWindowText(hwnd, p);\r
8165         sel.cpMin = 999999;\r
8166         sel.cpMax = 999999;\r
8167         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8168         return 0;\r
8169       }\r
8170       break;\r
8171     case VK_HOME:\r
8172     case VK_END:\r
8173       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8174       /* fall thru */\r
8175     case VK_PRIOR:\r
8176     case VK_NEXT:\r
8177       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8178       return 0;\r
8179     }\r
8180     break;\r
8181   case WM_MBUTTONDOWN:\r
8182     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8183       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8184     break;\r
8185   case WM_RBUTTONUP:\r
8186     if (GetKeyState(VK_SHIFT) & ~1) {\r
8187       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8188         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8189     } else {\r
8190       POINT pt;\r
8191       HMENU hmenu;\r
8192       hmenu = LoadMenu(hInst, "InputMenu");\r
8193       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8194       if (sel.cpMin == sel.cpMax) {\r
8195         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8196         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8197       }\r
8198       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8199         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8200       }\r
8201       pt.x = LOWORD(lParam);\r
8202       pt.y = HIWORD(lParam);\r
8203       MenuPopup(hwnd, pt, hmenu, -1);\r
8204     }\r
8205     return 0;\r
8206   case WM_COMMAND:\r
8207     switch (LOWORD(wParam)) { \r
8208     case IDM_Undo:\r
8209       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8210       return 0;\r
8211     case IDM_SelectAll:\r
8212       sel.cpMin = 0;\r
8213       sel.cpMax = -1; /*999999?*/\r
8214       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8215       return 0;\r
8216     case IDM_Cut:\r
8217       SendMessage(hwnd, WM_CUT, 0, 0);\r
8218       return 0;\r
8219     case IDM_Paste:\r
8220       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8221       return 0;\r
8222     case IDM_Copy:\r
8223       SendMessage(hwnd, WM_COPY, 0, 0);\r
8224       return 0;\r
8225     }\r
8226     break;\r
8227   }\r
8228   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8229 }\r
8230 \r
8231 #define CO_MAX  100000\r
8232 #define CO_TRIM   1000\r
8233 \r
8234 LRESULT CALLBACK\r
8235 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8236 {\r
8237   static SnapData sd;\r
8238   static HWND hText, hInput /*, hFocus*/;\r
8239 //  InputSource *is = consoleInputSource;\r
8240   RECT rect;\r
8241   static int sizeX, sizeY;\r
8242   int newSizeX, newSizeY;\r
8243   MINMAXINFO *mmi;\r
8244 \r
8245   switch (message) {\r
8246   case WM_INITDIALOG: /* message: initialize dialog box */\r
8247     hwndConsole = hDlg;\r
8248     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8249     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8250     SetFocus(hInput);\r
8251     consoleTextWindowProc = (WNDPROC)\r
8252       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8253     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8254     consoleInputWindowProc = (WNDPROC)\r
8255       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8256     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8257     Colorize(ColorNormal, TRUE);\r
8258     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8259     ChangedConsoleFont();\r
8260     GetClientRect(hDlg, &rect);\r
8261     sizeX = rect.right;\r
8262     sizeY = rect.bottom;\r
8263     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8264         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8265       WINDOWPLACEMENT wp;\r
8266       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8267       wp.length = sizeof(WINDOWPLACEMENT);\r
8268       wp.flags = 0;\r
8269       wp.showCmd = SW_SHOW;\r
8270       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8271       wp.rcNormalPosition.left = wpConsole.x;\r
8272       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8273       wp.rcNormalPosition.top = wpConsole.y;\r
8274       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8275       SetWindowPlacement(hDlg, &wp);\r
8276     }\r
8277 #if 1\r
8278    // [HGM] Chessknight's change 2004-07-13\r
8279    else { /* Determine Defaults */\r
8280        WINDOWPLACEMENT wp;\r
8281        wpConsole.x = winWidth + 1;\r
8282        wpConsole.y = boardY;\r
8283        wpConsole.width = screenWidth -  winWidth;\r
8284        wpConsole.height = winHeight;\r
8285        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8286        wp.length = sizeof(WINDOWPLACEMENT);\r
8287        wp.flags = 0;\r
8288        wp.showCmd = SW_SHOW;\r
8289        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8290        wp.rcNormalPosition.left = wpConsole.x;\r
8291        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8292        wp.rcNormalPosition.top = wpConsole.y;\r
8293        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8294        SetWindowPlacement(hDlg, &wp);\r
8295     }\r
8296 #endif\r
8297     return FALSE;\r
8298 \r
8299   case WM_SETFOCUS:\r
8300     SetFocus(hInput);\r
8301     return 0;\r
8302 \r
8303   case WM_CLOSE:\r
8304     ExitEvent(0);\r
8305     /* not reached */\r
8306     break;\r
8307 \r
8308   case WM_SIZE:\r
8309     if (IsIconic(hDlg)) break;\r
8310     newSizeX = LOWORD(lParam);\r
8311     newSizeY = HIWORD(lParam);\r
8312     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8313       RECT rectText, rectInput;\r
8314       POINT pt;\r
8315       int newTextHeight, newTextWidth;\r
8316       GetWindowRect(hText, &rectText);\r
8317       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8318       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8319       if (newTextHeight < 0) {\r
8320         newSizeY += -newTextHeight;\r
8321         newTextHeight = 0;\r
8322       }\r
8323       SetWindowPos(hText, NULL, 0, 0,\r
8324         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8325       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8326       pt.x = rectInput.left;\r
8327       pt.y = rectInput.top + newSizeY - sizeY;\r
8328       ScreenToClient(hDlg, &pt);\r
8329       SetWindowPos(hInput, NULL, \r
8330         pt.x, pt.y, /* needs client coords */   \r
8331         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8332         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8333     }\r
8334     sizeX = newSizeX;\r
8335     sizeY = newSizeY;\r
8336     break;\r
8337 \r
8338   case WM_GETMINMAXINFO:\r
8339     /* Prevent resizing window too small */\r
8340     mmi = (MINMAXINFO *) lParam;\r
8341     mmi->ptMinTrackSize.x = 100;\r
8342     mmi->ptMinTrackSize.y = 100;\r
8343     break;\r
8344 \r
8345   /* [AS] Snapping */\r
8346   case WM_ENTERSIZEMOVE:\r
8347     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8348 \r
8349   case WM_SIZING:\r
8350     return OnSizing( &sd, hDlg, wParam, lParam );\r
8351 \r
8352   case WM_MOVING:\r
8353     return OnMoving( &sd, hDlg, wParam, lParam );\r
8354 \r
8355   case WM_EXITSIZEMOVE:\r
8356     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8357   }\r
8358 \r
8359   return DefWindowProc(hDlg, message, wParam, lParam);\r
8360 }\r
8361 \r
8362 \r
8363 VOID\r
8364 ConsoleCreate()\r
8365 {\r
8366   HWND hCons;\r
8367   if (hwndConsole) return;\r
8368   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8369   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8370 }\r
8371 \r
8372 \r
8373 VOID\r
8374 ConsoleOutput(char* data, int length, int forceVisible)\r
8375 {\r
8376   HWND hText;\r
8377   int trim, exlen;\r
8378   char *p, *q;\r
8379   char buf[CO_MAX+1];\r
8380   POINT pEnd;\r
8381   RECT rect;\r
8382   static int delayLF = 0;\r
8383   CHARRANGE savesel, sel;\r
8384 \r
8385   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8386   p = data;\r
8387   q = buf;\r
8388   if (delayLF) {\r
8389     *q++ = '\r';\r
8390     *q++ = '\n';\r
8391     delayLF = 0;\r
8392   }\r
8393   while (length--) {\r
8394     if (*p == '\n') {\r
8395       if (*++p) {\r
8396         *q++ = '\r';\r
8397         *q++ = '\n';\r
8398       } else {\r
8399         delayLF = 1;\r
8400       }\r
8401     } else if (*p == '\007') {\r
8402        MyPlaySound(&sounds[(int)SoundBell]);\r
8403        p++;\r
8404     } else {\r
8405       *q++ = *p++;\r
8406     }\r
8407   }\r
8408   *q = NULLCHAR;\r
8409   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8410   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8411   /* Save current selection */\r
8412   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8413   exlen = GetWindowTextLength(hText);\r
8414   /* Find out whether current end of text is visible */\r
8415   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8416   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8417   /* Trim existing text if it's too long */\r
8418   if (exlen + (q - buf) > CO_MAX) {\r
8419     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8420     sel.cpMin = 0;\r
8421     sel.cpMax = trim;\r
8422     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8423     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8424     exlen -= trim;\r
8425     savesel.cpMin -= trim;\r
8426     savesel.cpMax -= trim;\r
8427     if (exlen < 0) exlen = 0;\r
8428     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8429     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8430   }\r
8431   /* Append the new text */\r
8432   sel.cpMin = exlen;\r
8433   sel.cpMax = exlen;\r
8434   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8435   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8436   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8437   if (forceVisible || exlen == 0 ||\r
8438       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8439        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8440     /* Scroll to make new end of text visible if old end of text\r
8441        was visible or new text is an echo of user typein */\r
8442     sel.cpMin = 9999999;\r
8443     sel.cpMax = 9999999;\r
8444     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8445     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8446     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8447     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8448   }\r
8449   if (savesel.cpMax == exlen || forceVisible) {\r
8450     /* Move insert point to new end of text if it was at the old\r
8451        end of text or if the new text is an echo of user typein */\r
8452     sel.cpMin = 9999999;\r
8453     sel.cpMax = 9999999;\r
8454     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8455   } else {\r
8456     /* Restore previous selection */\r
8457     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8458   }\r
8459   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8460 }\r
8461 \r
8462 /*---------*/\r
8463 \r
8464 \r
8465 void\r
8466 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8467 {\r
8468   char buf[100];\r
8469   char *str;\r
8470   COLORREF oldFg, oldBg;\r
8471   HFONT oldFont;\r
8472   RECT rect;\r
8473 \r
8474   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8475 \r
8476   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8477   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8478   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8479 \r
8480   rect.left = x;\r
8481   rect.right = x + squareSize;\r
8482   rect.top  = y;\r
8483   rect.bottom = y + squareSize;\r
8484   str = buf;\r
8485 \r
8486   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8487                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8488              y, ETO_CLIPPED|ETO_OPAQUE,\r
8489              &rect, str, strlen(str), NULL);\r
8490 \r
8491   (void) SetTextColor(hdc, oldFg);\r
8492   (void) SetBkColor(hdc, oldBg);\r
8493   (void) SelectObject(hdc, oldFont);\r
8494 }\r
8495 \r
8496 void\r
8497 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8498               RECT *rect, char *color, char *flagFell)\r
8499 {\r
8500   char buf[100];\r
8501   char *str;\r
8502   COLORREF oldFg, oldBg;\r
8503   HFONT oldFont;\r
8504 \r
8505   if (appData.clockMode) {\r
8506     if (tinyLayout)\r
8507       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8508     else\r
8509       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8510     str = buf;\r
8511   } else {\r
8512     str = color;\r
8513   }\r
8514 \r
8515   if (highlight) {\r
8516     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8517     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8518   } else {\r
8519     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8520     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8521   }\r
8522   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8523 \r
8524   JAWS_SILENCE\r
8525 \r
8526   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8527              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8528              rect, str, strlen(str), NULL);\r
8529   if(logoHeight > 0 && appData.clockMode) {\r
8530       RECT r;\r
8531       sprintf(buf, "%s %s", buf+7, flagFell);\r
8532       r.top = rect->top + logoHeight/2;\r
8533       r.left = rect->left;\r
8534       r.right = rect->right;\r
8535       r.bottom = rect->bottom;\r
8536       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8537                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8538                  &r, str, strlen(str), NULL);\r
8539   }\r
8540   (void) SetTextColor(hdc, oldFg);\r
8541   (void) SetBkColor(hdc, oldBg);\r
8542   (void) SelectObject(hdc, oldFont);\r
8543 }\r
8544 \r
8545 \r
8546 int\r
8547 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8548            OVERLAPPED *ovl)\r
8549 {\r
8550   int ok, err;\r
8551 \r
8552   /* [AS]  */\r
8553   if( count <= 0 ) {\r
8554     if (appData.debugMode) {\r
8555       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8556     }\r
8557 \r
8558     return ERROR_INVALID_USER_BUFFER;\r
8559   }\r
8560 \r
8561   ResetEvent(ovl->hEvent);\r
8562   ovl->Offset = ovl->OffsetHigh = 0;\r
8563   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8564   if (ok) {\r
8565     err = NO_ERROR;\r
8566   } else {\r
8567     err = GetLastError();\r
8568     if (err == ERROR_IO_PENDING) {\r
8569       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8570       if (ok)\r
8571         err = NO_ERROR;\r
8572       else\r
8573         err = GetLastError();\r
8574     }\r
8575   }\r
8576   return err;\r
8577 }\r
8578 \r
8579 int\r
8580 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8581             OVERLAPPED *ovl)\r
8582 {\r
8583   int ok, err;\r
8584 \r
8585   ResetEvent(ovl->hEvent);\r
8586   ovl->Offset = ovl->OffsetHigh = 0;\r
8587   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8588   if (ok) {\r
8589     err = NO_ERROR;\r
8590   } else {\r
8591     err = GetLastError();\r
8592     if (err == ERROR_IO_PENDING) {\r
8593       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8594       if (ok)\r
8595         err = NO_ERROR;\r
8596       else\r
8597         err = GetLastError();\r
8598     }\r
8599   }\r
8600   return err;\r
8601 }\r
8602 \r
8603 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8604 void CheckForInputBufferFull( InputSource * is )\r
8605 {\r
8606     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8607         /* Look for end of line */\r
8608         char * p = is->buf;\r
8609         \r
8610         while( p < is->next && *p != '\n' ) {\r
8611             p++;\r
8612         }\r
8613 \r
8614         if( p >= is->next ) {\r
8615             if (appData.debugMode) {\r
8616                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8617             }\r
8618 \r
8619             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8620             is->count = (DWORD) -1;\r
8621             is->next = is->buf;\r
8622         }\r
8623     }\r
8624 }\r
8625 \r
8626 DWORD\r
8627 InputThread(LPVOID arg)\r
8628 {\r
8629   InputSource *is;\r
8630   OVERLAPPED ovl;\r
8631 \r
8632   is = (InputSource *) arg;\r
8633   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8634   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8635   while (is->hThread != NULL) {\r
8636     is->error = DoReadFile(is->hFile, is->next,\r
8637                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8638                            &is->count, &ovl);\r
8639     if (is->error == NO_ERROR) {\r
8640       is->next += is->count;\r
8641     } else {\r
8642       if (is->error == ERROR_BROKEN_PIPE) {\r
8643         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8644         is->count = 0;\r
8645       } else {\r
8646         is->count = (DWORD) -1;\r
8647         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8648         break; \r
8649       }\r
8650     }\r
8651 \r
8652     CheckForInputBufferFull( is );\r
8653 \r
8654     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8655 \r
8656     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8657 \r
8658     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8659   }\r
8660 \r
8661   CloseHandle(ovl.hEvent);\r
8662   CloseHandle(is->hFile);\r
8663 \r
8664   if (appData.debugMode) {\r
8665     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8666   }\r
8667 \r
8668   return 0;\r
8669 }\r
8670 \r
8671 \r
8672 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8673 DWORD\r
8674 NonOvlInputThread(LPVOID arg)\r
8675 {\r
8676   InputSource *is;\r
8677   char *p, *q;\r
8678   int i;\r
8679   char prev;\r
8680 \r
8681   is = (InputSource *) arg;\r
8682   while (is->hThread != NULL) {\r
8683     is->error = ReadFile(is->hFile, is->next,\r
8684                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8685                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8686     if (is->error == NO_ERROR) {\r
8687       /* Change CRLF to LF */\r
8688       if (is->next > is->buf) {\r
8689         p = is->next - 1;\r
8690         i = is->count + 1;\r
8691       } else {\r
8692         p = is->next;\r
8693         i = is->count;\r
8694       }\r
8695       q = p;\r
8696       prev = NULLCHAR;\r
8697       while (i > 0) {\r
8698         if (prev == '\r' && *p == '\n') {\r
8699           *(q-1) = '\n';\r
8700           is->count--;\r
8701         } else { \r
8702           *q++ = *p;\r
8703         }\r
8704         prev = *p++;\r
8705         i--;\r
8706       }\r
8707       *q = NULLCHAR;\r
8708       is->next = q;\r
8709     } else {\r
8710       if (is->error == ERROR_BROKEN_PIPE) {\r
8711         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8712         is->count = 0; \r
8713       } else {\r
8714         is->count = (DWORD) -1;\r
8715       }\r
8716     }\r
8717 \r
8718     CheckForInputBufferFull( is );\r
8719 \r
8720     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8721 \r
8722     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8723 \r
8724     if (is->count < 0) break;  /* Quit on error */\r
8725   }\r
8726   CloseHandle(is->hFile);\r
8727   return 0;\r
8728 }\r
8729 \r
8730 DWORD\r
8731 SocketInputThread(LPVOID arg)\r
8732 {\r
8733   InputSource *is;\r
8734 \r
8735   is = (InputSource *) arg;\r
8736   while (is->hThread != NULL) {\r
8737     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8738     if ((int)is->count == SOCKET_ERROR) {\r
8739       is->count = (DWORD) -1;\r
8740       is->error = WSAGetLastError();\r
8741     } else {\r
8742       is->error = NO_ERROR;\r
8743       is->next += is->count;\r
8744       if (is->count == 0 && is->second == is) {\r
8745         /* End of file on stderr; quit with no message */\r
8746         break;\r
8747       }\r
8748     }\r
8749     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8750 \r
8751     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8752 \r
8753     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8754   }\r
8755   return 0;\r
8756 }\r
8757 \r
8758 VOID\r
8759 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8760 {\r
8761   InputSource *is;\r
8762 \r
8763   is = (InputSource *) lParam;\r
8764   if (is->lineByLine) {\r
8765     /* Feed in lines one by one */\r
8766     char *p = is->buf;\r
8767     char *q = p;\r
8768     while (q < is->next) {\r
8769       if (*q++ == '\n') {\r
8770         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8771         p = q;\r
8772       }\r
8773     }\r
8774     \r
8775     /* Move any partial line to the start of the buffer */\r
8776     q = is->buf;\r
8777     while (p < is->next) {\r
8778       *q++ = *p++;\r
8779     }\r
8780     is->next = q;\r
8781 \r
8782     if (is->error != NO_ERROR || is->count == 0) {\r
8783       /* Notify backend of the error.  Note: If there was a partial\r
8784          line at the end, it is not flushed through. */\r
8785       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8786     }\r
8787   } else {\r
8788     /* Feed in the whole chunk of input at once */\r
8789     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8790     is->next = is->buf;\r
8791   }\r
8792 }\r
8793 \r
8794 /*---------------------------------------------------------------------------*\\r
8795  *\r
8796  *  Menu enables. Used when setting various modes.\r
8797  *\r
8798 \*---------------------------------------------------------------------------*/\r
8799 \r
8800 typedef struct {\r
8801   int item;\r
8802   int flags;\r
8803 } Enables;\r
8804 \r
8805 VOID\r
8806 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8807 {\r
8808   while (enab->item > 0) {\r
8809     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8810     enab++;\r
8811   }\r
8812 }\r
8813 \r
8814 Enables gnuEnables[] = {\r
8815   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8825   { -1, -1 }\r
8826 };\r
8827 \r
8828 Enables icsEnables[] = {\r
8829   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8835   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8841   { -1, -1 }\r
8842 };\r
8843 \r
8844 #ifdef ZIPPY\r
8845 Enables zippyEnables[] = {\r
8846   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8849   { -1, -1 }\r
8850 };\r
8851 #endif\r
8852 \r
8853 Enables ncpEnables[] = {\r
8854   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8855   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8856   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8857   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8858   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8859   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8860   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8861   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8862   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8863   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8866   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8868   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8869   { -1, -1 }\r
8870 };\r
8871 \r
8872 Enables trainingOnEnables[] = {\r
8873   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8881   { -1, -1 }\r
8882 };\r
8883 \r
8884 Enables trainingOffEnables[] = {\r
8885   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8887   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8888   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8893   { -1, -1 }\r
8894 };\r
8895 \r
8896 /* These modify either ncpEnables or gnuEnables */\r
8897 Enables cmailEnables[] = {\r
8898   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8900   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8901   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8902   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8903   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8904   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8905   { -1, -1 }\r
8906 };\r
8907 \r
8908 Enables machineThinkingEnables[] = {\r
8909   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8910   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8911   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8912   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8913   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8914   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8915   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8916   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8917   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8918   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8919   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8920   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8921   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8922   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8923   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8924   { -1, -1 }\r
8925 };\r
8926 \r
8927 Enables userThinkingEnables[] = {\r
8928   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8929   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8930   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8931   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8932   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8933   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8934   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8935   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8936   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8937   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8938   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8939   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8940   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8941   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8942   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8943   { -1, -1 }\r
8944 };\r
8945 \r
8946 /*---------------------------------------------------------------------------*\\r
8947  *\r
8948  *  Front-end interface functions exported by XBoard.\r
8949  *  Functions appear in same order as prototypes in frontend.h.\r
8950  * \r
8951 \*---------------------------------------------------------------------------*/\r
8952 VOID\r
8953 ModeHighlight()\r
8954 {\r
8955   static UINT prevChecked = 0;\r
8956   static int prevPausing = 0;\r
8957   UINT nowChecked;\r
8958 \r
8959   if (pausing != prevPausing) {\r
8960     prevPausing = pausing;\r
8961     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8962                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8963     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8964   }\r
8965 \r
8966   switch (gameMode) {\r
8967   case BeginningOfGame:\r
8968     if (appData.icsActive)\r
8969       nowChecked = IDM_IcsClient;\r
8970     else if (appData.noChessProgram)\r
8971       nowChecked = IDM_EditGame;\r
8972     else\r
8973       nowChecked = IDM_MachineBlack;\r
8974     break;\r
8975   case MachinePlaysBlack:\r
8976     nowChecked = IDM_MachineBlack;\r
8977     break;\r
8978   case MachinePlaysWhite:\r
8979     nowChecked = IDM_MachineWhite;\r
8980     break;\r
8981   case TwoMachinesPlay:\r
8982     nowChecked = IDM_TwoMachines;\r
8983     break;\r
8984   case AnalyzeMode:\r
8985     nowChecked = IDM_AnalysisMode;\r
8986     break;\r
8987   case AnalyzeFile:\r
8988     nowChecked = IDM_AnalyzeFile;\r
8989     break;\r
8990   case EditGame:\r
8991     nowChecked = IDM_EditGame;\r
8992     break;\r
8993   case PlayFromGameFile:\r
8994     nowChecked = IDM_LoadGame;\r
8995     break;\r
8996   case EditPosition:\r
8997     nowChecked = IDM_EditPosition;\r
8998     break;\r
8999   case Training:\r
9000     nowChecked = IDM_Training;\r
9001     break;\r
9002   case IcsPlayingWhite:\r
9003   case IcsPlayingBlack:\r
9004   case IcsObserving:\r
9005   case IcsIdle:\r
9006     nowChecked = IDM_IcsClient;\r
9007     break;\r
9008   default:\r
9009   case EndOfGame:\r
9010     nowChecked = 0;\r
9011     break;\r
9012   }\r
9013   if (prevChecked != 0)\r
9014     (void) CheckMenuItem(GetMenu(hwndMain),\r
9015                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
9016   if (nowChecked != 0)\r
9017     (void) CheckMenuItem(GetMenu(hwndMain),\r
9018                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
9019 \r
9020   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
9021     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
9022                           MF_BYCOMMAND|MF_ENABLED);\r
9023   } else {\r
9024     (void) EnableMenuItem(GetMenu(hwndMain), \r
9025                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
9026   }\r
9027 \r
9028   prevChecked = nowChecked;\r
9029 \r
9030   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
9031   if (appData.icsActive) {\r
9032        if (appData.icsEngineAnalyze) {\r
9033                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9034                        MF_BYCOMMAND|MF_CHECKED);\r
9035        } else {\r
9036                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9037                        MF_BYCOMMAND|MF_UNCHECKED);\r
9038        }\r
9039   }\r
9040 }\r
9041 \r
9042 VOID\r
9043 SetICSMode()\r
9044 {\r
9045   HMENU hmenu = GetMenu(hwndMain);\r
9046   SetMenuEnables(hmenu, icsEnables);\r
9047   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9048     MF_BYPOSITION|MF_ENABLED);\r
9049 #ifdef ZIPPY\r
9050   if (appData.zippyPlay) {\r
9051     SetMenuEnables(hmenu, zippyEnables);\r
9052     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9053          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9054           MF_BYCOMMAND|MF_ENABLED);\r
9055   }\r
9056 #endif\r
9057 }\r
9058 \r
9059 VOID\r
9060 SetGNUMode()\r
9061 {\r
9062   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9063 }\r
9064 \r
9065 VOID\r
9066 SetNCPMode()\r
9067 {\r
9068   HMENU hmenu = GetMenu(hwndMain);\r
9069   SetMenuEnables(hmenu, ncpEnables);\r
9070   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9071     MF_BYPOSITION|MF_GRAYED);\r
9072     DrawMenuBar(hwndMain);\r
9073 }\r
9074 \r
9075 VOID\r
9076 SetCmailMode()\r
9077 {\r
9078   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9079 }\r
9080 \r
9081 VOID \r
9082 SetTrainingModeOn()\r
9083 {\r
9084   int i;\r
9085   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9086   for (i = 0; i < N_BUTTONS; i++) {\r
9087     if (buttonDesc[i].hwnd != NULL)\r
9088       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9089   }\r
9090   CommentPopDown();\r
9091 }\r
9092 \r
9093 VOID SetTrainingModeOff()\r
9094 {\r
9095   int i;\r
9096   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9097   for (i = 0; i < N_BUTTONS; i++) {\r
9098     if (buttonDesc[i].hwnd != NULL)\r
9099       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9100   }\r
9101 }\r
9102 \r
9103 \r
9104 VOID\r
9105 SetUserThinkingEnables()\r
9106 {\r
9107   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9108 }\r
9109 \r
9110 VOID\r
9111 SetMachineThinkingEnables()\r
9112 {\r
9113   HMENU hMenu = GetMenu(hwndMain);\r
9114   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9115 \r
9116   SetMenuEnables(hMenu, machineThinkingEnables);\r
9117 \r
9118   if (gameMode == MachinePlaysBlack) {\r
9119     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9120   } else if (gameMode == MachinePlaysWhite) {\r
9121     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9122   } else if (gameMode == TwoMachinesPlay) {\r
9123     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9124   }\r
9125 }\r
9126 \r
9127 \r
9128 VOID\r
9129 DisplayTitle(char *str)\r
9130 {\r
9131   char title[MSG_SIZ], *host;\r
9132   if (str[0] != NULLCHAR) {\r
9133     strcpy(title, str);\r
9134   } else if (appData.icsActive) {\r
9135     if (appData.icsCommPort[0] != NULLCHAR)\r
9136       host = "ICS";\r
9137     else \r
9138       host = appData.icsHost;\r
9139     sprintf(title, "%s: %s", szTitle, host);\r
9140   } else if (appData.noChessProgram) {\r
9141     strcpy(title, szTitle);\r
9142   } else {\r
9143     strcpy(title, szTitle);\r
9144     strcat(title, ": ");\r
9145     strcat(title, first.tidy);\r
9146   }\r
9147   SetWindowText(hwndMain, title);\r
9148 }\r
9149 \r
9150 \r
9151 VOID\r
9152 DisplayMessage(char *str1, char *str2)\r
9153 {\r
9154   HDC hdc;\r
9155   HFONT oldFont;\r
9156   int remain = MESSAGE_TEXT_MAX - 1;\r
9157   int len;\r
9158 \r
9159   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9160   messageText[0] = NULLCHAR;\r
9161   if (*str1) {\r
9162     len = strlen(str1);\r
9163     if (len > remain) len = remain;\r
9164     strncpy(messageText, str1, len);\r
9165     messageText[len] = NULLCHAR;\r
9166     remain -= len;\r
9167   }\r
9168   if (*str2 && remain >= 2) {\r
9169     if (*str1) {\r
9170       strcat(messageText, "  ");\r
9171       remain -= 2;\r
9172     }\r
9173     len = strlen(str2);\r
9174     if (len > remain) len = remain;\r
9175     strncat(messageText, str2, len);\r
9176   }\r
9177   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9178 \r
9179   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9180 \r
9181   SAYMACHINEMOVE();\r
9182 \r
9183   hdc = GetDC(hwndMain);\r
9184   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9185   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9186              &messageRect, messageText, strlen(messageText), NULL);\r
9187   (void) SelectObject(hdc, oldFont);\r
9188   (void) ReleaseDC(hwndMain, hdc);\r
9189 }\r
9190 \r
9191 VOID\r
9192 DisplayError(char *str, int error)\r
9193 {\r
9194   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9195   int len;\r
9196 \r
9197   if (error == 0) {\r
9198     strcpy(buf, str);\r
9199   } else {\r
9200     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9201                         NULL, error, LANG_NEUTRAL,\r
9202                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9203     if (len > 0) {\r
9204       sprintf(buf, "%s:\n%s", str, buf2);\r
9205     } else {\r
9206       ErrorMap *em = errmap;\r
9207       while (em->err != 0 && em->err != error) em++;\r
9208       if (em->err != 0) {\r
9209         sprintf(buf, "%s:\n%s", str, em->msg);\r
9210       } else {\r
9211         sprintf(buf, "%s:\nError code %d", str, error);\r
9212       }\r
9213     }\r
9214   }\r
9215   \r
9216   ErrorPopUp("Error", buf);\r
9217 }\r
9218 \r
9219 \r
9220 VOID\r
9221 DisplayMoveError(char *str)\r
9222 {\r
9223   fromX = fromY = -1;\r
9224   ClearHighlights();\r
9225   DrawPosition(FALSE, NULL);\r
9226   if (appData.popupMoveErrors) {\r
9227     ErrorPopUp("Error", str);\r
9228   } else {\r
9229     DisplayMessage(str, "");\r
9230     moveErrorMessageUp = TRUE;\r
9231   }\r
9232 }\r
9233 \r
9234 VOID\r
9235 DisplayFatalError(char *str, int error, int exitStatus)\r
9236 {\r
9237   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9238   int len;\r
9239   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9240 \r
9241   if (error != 0) {\r
9242     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9243                         NULL, error, LANG_NEUTRAL,\r
9244                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9245     if (len > 0) {\r
9246       sprintf(buf, "%s:\n%s", str, buf2);\r
9247     } else {\r
9248       ErrorMap *em = errmap;\r
9249       while (em->err != 0 && em->err != error) em++;\r
9250       if (em->err != 0) {\r
9251         sprintf(buf, "%s:\n%s", str, em->msg);\r
9252       } else {\r
9253         sprintf(buf, "%s:\nError code %d", str, error);\r
9254       }\r
9255     }\r
9256     str = buf;\r
9257   }\r
9258   if (appData.debugMode) {\r
9259     fprintf(debugFP, "%s: %s\n", label, str);\r
9260   }\r
9261   if (appData.popupExitMessage) {\r
9262     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9263                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9264   }\r
9265   ExitEvent(exitStatus);\r
9266 }\r
9267 \r
9268 \r
9269 VOID\r
9270 DisplayInformation(char *str)\r
9271 {\r
9272   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9273 }\r
9274 \r
9275 \r
9276 VOID\r
9277 DisplayNote(char *str)\r
9278 {\r
9279   ErrorPopUp("Note", str);\r
9280 }\r
9281 \r
9282 \r
9283 typedef struct {\r
9284   char *title, *question, *replyPrefix;\r
9285   ProcRef pr;\r
9286 } QuestionParams;\r
9287 \r
9288 LRESULT CALLBACK\r
9289 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9290 {\r
9291   static QuestionParams *qp;\r
9292   char reply[MSG_SIZ];\r
9293   int len, err;\r
9294 \r
9295   switch (message) {\r
9296   case WM_INITDIALOG:\r
9297     qp = (QuestionParams *) lParam;\r
9298     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9299     SetWindowText(hDlg, qp->title);\r
9300     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9301     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9302     return FALSE;\r
9303 \r
9304   case WM_COMMAND:\r
9305     switch (LOWORD(wParam)) {\r
9306     case IDOK:\r
9307       strcpy(reply, qp->replyPrefix);\r
9308       if (*reply) strcat(reply, " ");\r
9309       len = strlen(reply);\r
9310       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9311       strcat(reply, "\n");\r
9312       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9313       EndDialog(hDlg, TRUE);\r
9314       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9315       return TRUE;\r
9316     case IDCANCEL:\r
9317       EndDialog(hDlg, FALSE);\r
9318       return TRUE;\r
9319     default:\r
9320       break;\r
9321     }\r
9322     break;\r
9323   }\r
9324   return FALSE;\r
9325 }\r
9326 \r
9327 VOID\r
9328 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9329 {\r
9330     QuestionParams qp;\r
9331     FARPROC lpProc;\r
9332     \r
9333     qp.title = title;\r
9334     qp.question = question;\r
9335     qp.replyPrefix = replyPrefix;\r
9336     qp.pr = pr;\r
9337     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9338     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9339       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9340     FreeProcInstance(lpProc);\r
9341 }\r
9342 \r
9343 /* [AS] Pick FRC position */\r
9344 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9345 {\r
9346     static int * lpIndexFRC;\r
9347     BOOL index_is_ok;\r
9348     char buf[16];\r
9349 \r
9350     switch( message )\r
9351     {\r
9352     case WM_INITDIALOG:\r
9353         lpIndexFRC = (int *) lParam;\r
9354 \r
9355         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9356 \r
9357         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9358         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9359         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9360         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9361 \r
9362         break;\r
9363 \r
9364     case WM_COMMAND:\r
9365         switch( LOWORD(wParam) ) {\r
9366         case IDOK:\r
9367             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9368             EndDialog( hDlg, 0 );\r
9369             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9370             return TRUE;\r
9371         case IDCANCEL:\r
9372             EndDialog( hDlg, 1 );   \r
9373             return TRUE;\r
9374         case IDC_NFG_Edit:\r
9375             if( HIWORD(wParam) == EN_CHANGE ) {\r
9376                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9377 \r
9378                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9379             }\r
9380             return TRUE;\r
9381         case IDC_NFG_Random:\r
9382             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9383             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9384             return TRUE;\r
9385         }\r
9386 \r
9387         break;\r
9388     }\r
9389 \r
9390     return FALSE;\r
9391 }\r
9392 \r
9393 int NewGameFRC()\r
9394 {\r
9395     int result;\r
9396     int index = appData.defaultFrcPosition;\r
9397     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9398 \r
9399     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9400 \r
9401     if( result == 0 ) {\r
9402         appData.defaultFrcPosition = index;\r
9403     }\r
9404 \r
9405     return result;\r
9406 }\r
9407 \r
9408 /* [AS] Game list options */\r
9409 typedef struct {\r
9410     char id;\r
9411     char * name;\r
9412 } GLT_Item;\r
9413 \r
9414 static GLT_Item GLT_ItemInfo[] = {\r
9415     { GLT_EVENT,      "Event" },\r
9416     { GLT_SITE,       "Site" },\r
9417     { GLT_DATE,       "Date" },\r
9418     { GLT_ROUND,      "Round" },\r
9419     { GLT_PLAYERS,    "Players" },\r
9420     { GLT_RESULT,     "Result" },\r
9421     { GLT_WHITE_ELO,  "White Rating" },\r
9422     { GLT_BLACK_ELO,  "Black Rating" },\r
9423     { GLT_TIME_CONTROL,"Time Control" },\r
9424     { GLT_VARIANT,    "Variant" },\r
9425     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9426     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9427     { 0, 0 }\r
9428 };\r
9429 \r
9430 const char * GLT_FindItem( char id )\r
9431 {\r
9432     const char * result = 0;\r
9433 \r
9434     GLT_Item * list = GLT_ItemInfo;\r
9435 \r
9436     while( list->id != 0 ) {\r
9437         if( list->id == id ) {\r
9438             result = list->name;\r
9439             break;\r
9440         }\r
9441 \r
9442         list++;\r
9443     }\r
9444 \r
9445     return result;\r
9446 }\r
9447 \r
9448 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9449 {\r
9450     const char * name = GLT_FindItem( id );\r
9451 \r
9452     if( name != 0 ) {\r
9453         if( index >= 0 ) {\r
9454             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9455         }\r
9456         else {\r
9457             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9458         }\r
9459     }\r
9460 }\r
9461 \r
9462 void GLT_TagsToList( HWND hDlg, char * tags )\r
9463 {\r
9464     char * pc = tags;\r
9465 \r
9466     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9467 \r
9468     while( *pc ) {\r
9469         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9470         pc++;\r
9471     }\r
9472 \r
9473     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9474 \r
9475     pc = GLT_ALL_TAGS;\r
9476 \r
9477     while( *pc ) {\r
9478         if( strchr( tags, *pc ) == 0 ) {\r
9479             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9480         }\r
9481         pc++;\r
9482     }\r
9483 \r
9484     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9485 }\r
9486 \r
9487 char GLT_ListItemToTag( HWND hDlg, int index )\r
9488 {\r
9489     char result = '\0';\r
9490     char name[128];\r
9491 \r
9492     GLT_Item * list = GLT_ItemInfo;\r
9493 \r
9494     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9495         while( list->id != 0 ) {\r
9496             if( strcmp( list->name, name ) == 0 ) {\r
9497                 result = list->id;\r
9498                 break;\r
9499             }\r
9500 \r
9501             list++;\r
9502         }\r
9503     }\r
9504 \r
9505     return result;\r
9506 }\r
9507 \r
9508 void GLT_MoveSelection( HWND hDlg, int delta )\r
9509 {\r
9510     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9511     int idx2 = idx1 + delta;\r
9512     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9513 \r
9514     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9515         char buf[128];\r
9516 \r
9517         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9518         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9519         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9520         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9521     }\r
9522 }\r
9523 \r
9524 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9525 {\r
9526     static char glt[64];\r
9527     static char * lpUserGLT;\r
9528 \r
9529     switch( message )\r
9530     {\r
9531     case WM_INITDIALOG:\r
9532         lpUserGLT = (char *) lParam;\r
9533         \r
9534         strcpy( glt, lpUserGLT );\r
9535 \r
9536         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9537 \r
9538         /* Initialize list */\r
9539         GLT_TagsToList( hDlg, glt );\r
9540 \r
9541         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9542 \r
9543         break;\r
9544 \r
9545     case WM_COMMAND:\r
9546         switch( LOWORD(wParam) ) {\r
9547         case IDOK:\r
9548             {\r
9549                 char * pc = lpUserGLT;\r
9550                 int idx = 0;\r
9551 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9552                 char id;\r
9553 \r
9554                 do {\r
9555                     id = GLT_ListItemToTag( hDlg, idx );\r
9556 \r
9557                     *pc++ = id;\r
9558                     idx++;\r
9559                 } while( id != '\0' );\r
9560             }\r
9561             EndDialog( hDlg, 0 );\r
9562             return TRUE;\r
9563         case IDCANCEL:\r
9564             EndDialog( hDlg, 1 );\r
9565             return TRUE;\r
9566 \r
9567         case IDC_GLT_Default:\r
9568             strcpy( glt, GLT_DEFAULT_TAGS );\r
9569             GLT_TagsToList( hDlg, glt );\r
9570             return TRUE;\r
9571 \r
9572         case IDC_GLT_Restore:\r
9573             strcpy( glt, lpUserGLT );\r
9574             GLT_TagsToList( hDlg, glt );\r
9575             return TRUE;\r
9576 \r
9577         case IDC_GLT_Up:\r
9578             GLT_MoveSelection( hDlg, -1 );\r
9579             return TRUE;\r
9580 \r
9581         case IDC_GLT_Down:\r
9582             GLT_MoveSelection( hDlg, +1 );\r
9583             return TRUE;\r
9584         }\r
9585 \r
9586         break;\r
9587     }\r
9588 \r
9589     return FALSE;\r
9590 }\r
9591 \r
9592 int GameListOptions()\r
9593 {\r
9594     char glt[64];\r
9595     int result;\r
9596     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9597 \r
9598     strcpy( glt, appData.gameListTags );\r
9599 \r
9600     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9601 \r
9602     if( result == 0 ) {\r
9603         /* [AS] Memory leak here! */\r
9604         appData.gameListTags = strdup( glt ); \r
9605     }\r
9606 \r
9607     return result;\r
9608 }\r
9609 \r
9610 \r
9611 VOID\r
9612 DisplayIcsInteractionTitle(char *str)\r
9613 {\r
9614   char consoleTitle[MSG_SIZ];\r
9615 \r
9616   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9617   SetWindowText(hwndConsole, consoleTitle);\r
9618 }\r
9619 \r
9620 void\r
9621 DrawPosition(int fullRedraw, Board board)\r
9622 {\r
9623   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9624 }\r
9625 \r
9626 \r
9627 VOID\r
9628 ResetFrontEnd()\r
9629 {\r
9630   fromX = fromY = -1;\r
9631   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9632     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9633     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9634     dragInfo.lastpos = dragInfo.pos;\r
9635     dragInfo.start.x = dragInfo.start.y = -1;\r
9636     dragInfo.from = dragInfo.start;\r
9637     ReleaseCapture();\r
9638     DrawPosition(TRUE, NULL);\r
9639   }\r
9640 }\r
9641 \r
9642 \r
9643 VOID\r
9644 CommentPopUp(char *title, char *str)\r
9645 {\r
9646   HWND hwnd = GetActiveWindow();\r
9647   EitherCommentPopUp(0, title, str, FALSE);\r
9648   SetActiveWindow(hwnd);\r
9649 }\r
9650 \r
9651 VOID\r
9652 CommentPopDown(void)\r
9653 {\r
9654   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9655   if (commentDialog) {\r
9656     ShowWindow(commentDialog, SW_HIDE);\r
9657   }\r
9658   commentDialogUp = FALSE;\r
9659 }\r
9660 \r
9661 VOID\r
9662 EditCommentPopUp(int index, char *title, char *str)\r
9663 {\r
9664   EitherCommentPopUp(index, title, str, TRUE);\r
9665 }\r
9666 \r
9667 \r
9668 VOID\r
9669 RingBell()\r
9670 {\r
9671   MyPlaySound(&sounds[(int)SoundMove]);\r
9672 }\r
9673 \r
9674 VOID PlayIcsWinSound()\r
9675 {\r
9676   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9677 }\r
9678 \r
9679 VOID PlayIcsLossSound()\r
9680 {\r
9681   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9682 }\r
9683 \r
9684 VOID PlayIcsDrawSound()\r
9685 {\r
9686   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9687 }\r
9688 \r
9689 VOID PlayIcsUnfinishedSound()\r
9690 {\r
9691   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9692 }\r
9693 \r
9694 VOID\r
9695 PlayAlarmSound()\r
9696 {\r
9697   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9698 }\r
9699 \r
9700 \r
9701 VOID\r
9702 EchoOn()\r
9703 {\r
9704   HWND hInput;\r
9705   consoleEcho = TRUE;\r
9706   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9707   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9708   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9709 }\r
9710 \r
9711 \r
9712 VOID\r
9713 EchoOff()\r
9714 {\r
9715   CHARFORMAT cf;\r
9716   HWND hInput;\r
9717   consoleEcho = FALSE;\r
9718   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9719   /* This works OK: set text and background both to the same color */\r
9720   cf = consoleCF;\r
9721   cf.crTextColor = COLOR_ECHOOFF;\r
9722   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9723   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9724 }\r
9725 \r
9726 /* No Raw()...? */\r
9727 \r
9728 void Colorize(ColorClass cc, int continuation)\r
9729 {\r
9730   currentColorClass = cc;\r
9731   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9732   consoleCF.crTextColor = textAttribs[cc].color;\r
9733   consoleCF.dwEffects = textAttribs[cc].effects;\r
9734   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9735 }\r
9736 \r
9737 char *\r
9738 UserName()\r
9739 {\r
9740   static char buf[MSG_SIZ];\r
9741   DWORD bufsiz = MSG_SIZ;\r
9742 \r
9743   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9744         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9745   }\r
9746   if (!GetUserName(buf, &bufsiz)) {\r
9747     /*DisplayError("Error getting user name", GetLastError());*/\r
9748     strcpy(buf, "User");\r
9749   }\r
9750   return buf;\r
9751 }\r
9752 \r
9753 char *\r
9754 HostName()\r
9755 {\r
9756   static char buf[MSG_SIZ];\r
9757   DWORD bufsiz = MSG_SIZ;\r
9758 \r
9759   if (!GetComputerName(buf, &bufsiz)) {\r
9760     /*DisplayError("Error getting host name", GetLastError());*/\r
9761     strcpy(buf, "Unknown");\r
9762   }\r
9763   return buf;\r
9764 }\r
9765 \r
9766 \r
9767 int\r
9768 ClockTimerRunning()\r
9769 {\r
9770   return clockTimerEvent != 0;\r
9771 }\r
9772 \r
9773 int\r
9774 StopClockTimer()\r
9775 {\r
9776   if (clockTimerEvent == 0) return FALSE;\r
9777   KillTimer(hwndMain, clockTimerEvent);\r
9778   clockTimerEvent = 0;\r
9779   return TRUE;\r
9780 }\r
9781 \r
9782 void\r
9783 StartClockTimer(long millisec)\r
9784 {\r
9785   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9786                              (UINT) millisec, NULL);\r
9787 }\r
9788 \r
9789 void\r
9790 DisplayWhiteClock(long timeRemaining, int highlight)\r
9791 {\r
9792   HDC hdc;\r
9793   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9794 \r
9795   if(appData.noGUI) return;\r
9796   hdc = GetDC(hwndMain);\r
9797   if (!IsIconic(hwndMain)) {\r
9798     DisplayAClock(hdc, timeRemaining, highlight, \r
9799                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9800   }\r
9801   if (highlight && iconCurrent == iconBlack) {\r
9802     iconCurrent = iconWhite;\r
9803     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9804     if (IsIconic(hwndMain)) {\r
9805       DrawIcon(hdc, 2, 2, iconCurrent);\r
9806     }\r
9807   }\r
9808   (void) ReleaseDC(hwndMain, hdc);\r
9809   if (hwndConsole)\r
9810     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9811 }\r
9812 \r
9813 void\r
9814 DisplayBlackClock(long timeRemaining, int highlight)\r
9815 {\r
9816   HDC hdc;\r
9817   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9818 \r
9819   if(appData.noGUI) return;\r
9820   hdc = GetDC(hwndMain);\r
9821   if (!IsIconic(hwndMain)) {\r
9822     DisplayAClock(hdc, timeRemaining, highlight, \r
9823                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9824   }\r
9825   if (highlight && iconCurrent == iconWhite) {\r
9826     iconCurrent = iconBlack;\r
9827     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9828     if (IsIconic(hwndMain)) {\r
9829       DrawIcon(hdc, 2, 2, iconCurrent);\r
9830     }\r
9831   }\r
9832   (void) ReleaseDC(hwndMain, hdc);\r
9833   if (hwndConsole)\r
9834     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9835 }\r
9836 \r
9837 \r
9838 int\r
9839 LoadGameTimerRunning()\r
9840 {\r
9841   return loadGameTimerEvent != 0;\r
9842 }\r
9843 \r
9844 int\r
9845 StopLoadGameTimer()\r
9846 {\r
9847   if (loadGameTimerEvent == 0) return FALSE;\r
9848   KillTimer(hwndMain, loadGameTimerEvent);\r
9849   loadGameTimerEvent = 0;\r
9850   return TRUE;\r
9851 }\r
9852 \r
9853 void\r
9854 StartLoadGameTimer(long millisec)\r
9855 {\r
9856   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9857                                 (UINT) millisec, NULL);\r
9858 }\r
9859 \r
9860 void\r
9861 AutoSaveGame()\r
9862 {\r
9863   char *defName;\r
9864   FILE *f;\r
9865   char fileTitle[MSG_SIZ];\r
9866 \r
9867   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9868   f = OpenFileDialog(hwndMain, "a", defName,\r
9869                      appData.oldSaveStyle ? "gam" : "pgn",\r
9870                      GAME_FILT, \r
9871                      "Save Game to File", NULL, fileTitle, NULL);\r
9872   if (f != NULL) {\r
9873     SaveGame(f, 0, "");\r
9874     fclose(f);\r
9875   }\r
9876 }\r
9877 \r
9878 \r
9879 void\r
9880 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9881 {\r
9882   if (delayedTimerEvent != 0) {\r
9883     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9884       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9885     }\r
9886     KillTimer(hwndMain, delayedTimerEvent);\r
9887     delayedTimerEvent = 0;\r
9888     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9889     delayedTimerCallback();\r
9890   }\r
9891   delayedTimerCallback = cb;\r
9892   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9893                                 (UINT) millisec, NULL);\r
9894 }\r
9895 \r
9896 DelayedEventCallback\r
9897 GetDelayedEvent()\r
9898 {\r
9899   if (delayedTimerEvent) {\r
9900     return delayedTimerCallback;\r
9901   } else {\r
9902     return NULL;\r
9903   }\r
9904 }\r
9905 \r
9906 void\r
9907 CancelDelayedEvent()\r
9908 {\r
9909   if (delayedTimerEvent) {\r
9910     KillTimer(hwndMain, delayedTimerEvent);\r
9911     delayedTimerEvent = 0;\r
9912   }\r
9913 }\r
9914 \r
9915 DWORD GetWin32Priority(int nice)\r
9916 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9917 /*\r
9918 REALTIME_PRIORITY_CLASS     0x00000100\r
9919 HIGH_PRIORITY_CLASS         0x00000080\r
9920 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9921 NORMAL_PRIORITY_CLASS       0x00000020\r
9922 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9923 IDLE_PRIORITY_CLASS         0x00000040\r
9924 */\r
9925         if (nice < -15) return 0x00000080;\r
9926         if (nice < 0)   return 0x00008000;\r
9927         if (nice == 0)  return 0x00000020;\r
9928         if (nice < 15)  return 0x00004000;\r
9929         return 0x00000040;\r
9930 }\r
9931 \r
9932 /* Start a child process running the given program.\r
9933    The process's standard output can be read from "from", and its\r
9934    standard input can be written to "to".\r
9935    Exit with fatal error if anything goes wrong.\r
9936    Returns an opaque pointer that can be used to destroy the process\r
9937    later.\r
9938 */\r
9939 int\r
9940 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9941 {\r
9942 #define BUFSIZE 4096\r
9943 \r
9944   HANDLE hChildStdinRd, hChildStdinWr,\r
9945     hChildStdoutRd, hChildStdoutWr;\r
9946   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9947   SECURITY_ATTRIBUTES saAttr;\r
9948   BOOL fSuccess;\r
9949   PROCESS_INFORMATION piProcInfo;\r
9950   STARTUPINFO siStartInfo;\r
9951   ChildProc *cp;\r
9952   char buf[MSG_SIZ];\r
9953   DWORD err;\r
9954 \r
9955   if (appData.debugMode) {\r
9956     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9957   }\r
9958 \r
9959   *pr = NoProc;\r
9960 \r
9961   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9962   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9963   saAttr.bInheritHandle = TRUE;\r
9964   saAttr.lpSecurityDescriptor = NULL;\r
9965 \r
9966   /*\r
9967    * The steps for redirecting child's STDOUT:\r
9968    *     1. Create anonymous pipe to be STDOUT for child.\r
9969    *     2. Create a noninheritable duplicate of read handle,\r
9970    *         and close the inheritable read handle.\r
9971    */\r
9972 \r
9973   /* Create a pipe for the child's STDOUT. */\r
9974   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9975     return GetLastError();\r
9976   }\r
9977 \r
9978   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9979   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9980                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9981                              FALSE,     /* not inherited */\r
9982                              DUPLICATE_SAME_ACCESS);\r
9983   if (! fSuccess) {\r
9984     return GetLastError();\r
9985   }\r
9986   CloseHandle(hChildStdoutRd);\r
9987 \r
9988   /*\r
9989    * The steps for redirecting child's STDIN:\r
9990    *     1. Create anonymous pipe to be STDIN for child.\r
9991    *     2. Create a noninheritable duplicate of write handle,\r
9992    *         and close the inheritable write handle.\r
9993    */\r
9994 \r
9995   /* Create a pipe for the child's STDIN. */\r
9996   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9997     return GetLastError();\r
9998   }\r
9999 \r
10000   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
10001   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
10002                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
10003                              FALSE,     /* not inherited */\r
10004                              DUPLICATE_SAME_ACCESS);\r
10005   if (! fSuccess) {\r
10006     return GetLastError();\r
10007   }\r
10008   CloseHandle(hChildStdinWr);\r
10009 \r
10010   /* Arrange to (1) look in dir for the child .exe file, and\r
10011    * (2) have dir be the child's working directory.  Interpret\r
10012    * dir relative to the directory WinBoard loaded from. */\r
10013   GetCurrentDirectory(MSG_SIZ, buf);\r
10014   SetCurrentDirectory(installDir);\r
10015   SetCurrentDirectory(dir);\r
10016 \r
10017   /* Now create the child process. */\r
10018 \r
10019   siStartInfo.cb = sizeof(STARTUPINFO);\r
10020   siStartInfo.lpReserved = NULL;\r
10021   siStartInfo.lpDesktop = NULL;\r
10022   siStartInfo.lpTitle = NULL;\r
10023   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
10024   siStartInfo.cbReserved2 = 0;\r
10025   siStartInfo.lpReserved2 = NULL;\r
10026   siStartInfo.hStdInput = hChildStdinRd;\r
10027   siStartInfo.hStdOutput = hChildStdoutWr;\r
10028   siStartInfo.hStdError = hChildStdoutWr;\r
10029 \r
10030   fSuccess = CreateProcess(NULL,\r
10031                            cmdLine,        /* command line */\r
10032                            NULL,           /* process security attributes */\r
10033                            NULL,           /* primary thread security attrs */\r
10034                            TRUE,           /* handles are inherited */\r
10035                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10036                            NULL,           /* use parent's environment */\r
10037                            NULL,\r
10038                            &siStartInfo, /* STARTUPINFO pointer */\r
10039                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10040 \r
10041   err = GetLastError();\r
10042   SetCurrentDirectory(buf); /* return to prev directory */\r
10043   if (! fSuccess) {\r
10044     return err;\r
10045   }\r
10046 \r
10047   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10048     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10049     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10050   }\r
10051 \r
10052   /* Close the handles we don't need in the parent */\r
10053   CloseHandle(piProcInfo.hThread);\r
10054   CloseHandle(hChildStdinRd);\r
10055   CloseHandle(hChildStdoutWr);\r
10056 \r
10057   /* Prepare return value */\r
10058   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10059   cp->kind = CPReal;\r
10060   cp->hProcess = piProcInfo.hProcess;\r
10061   cp->pid = piProcInfo.dwProcessId;\r
10062   cp->hFrom = hChildStdoutRdDup;\r
10063   cp->hTo = hChildStdinWrDup;\r
10064 \r
10065   *pr = (void *) cp;\r
10066 \r
10067   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10068      2000 where engines sometimes don't see the initial command(s)\r
10069      from WinBoard and hang.  I don't understand how that can happen,\r
10070      but the Sleep is harmless, so I've put it in.  Others have also\r
10071      reported what may be the same problem, so hopefully this will fix\r
10072      it for them too.  */\r
10073   Sleep(500);\r
10074 \r
10075   return NO_ERROR;\r
10076 }\r
10077 \r
10078 \r
10079 void\r
10080 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10081 {\r
10082   ChildProc *cp; int result;\r
10083 \r
10084   cp = (ChildProc *) pr;\r
10085   if (cp == NULL) return;\r
10086 \r
10087   switch (cp->kind) {\r
10088   case CPReal:\r
10089     /* TerminateProcess is considered harmful, so... */\r
10090     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10091     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10092     /* The following doesn't work because the chess program\r
10093        doesn't "have the same console" as WinBoard.  Maybe\r
10094        we could arrange for this even though neither WinBoard\r
10095        nor the chess program uses a console for stdio? */\r
10096     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10097 \r
10098     /* [AS] Special termination modes for misbehaving programs... */\r
10099     if( signal == 9 ) { \r
10100         result = TerminateProcess( cp->hProcess, 0 );\r
10101 \r
10102         if ( appData.debugMode) {\r
10103             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10104         }\r
10105     }\r
10106     else if( signal == 10 ) {\r
10107         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10108 \r
10109         if( dw != WAIT_OBJECT_0 ) {\r
10110             result = TerminateProcess( cp->hProcess, 0 );\r
10111 \r
10112             if ( appData.debugMode) {\r
10113                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10114             }\r
10115 \r
10116         }\r
10117     }\r
10118 \r
10119     CloseHandle(cp->hProcess);\r
10120     break;\r
10121 \r
10122   case CPComm:\r
10123     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10124     break;\r
10125 \r
10126   case CPSock:\r
10127     closesocket(cp->sock);\r
10128     WSACleanup();\r
10129     break;\r
10130 \r
10131   case CPRcmd:\r
10132     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10133     closesocket(cp->sock);\r
10134     closesocket(cp->sock2);\r
10135     WSACleanup();\r
10136     break;\r
10137   }\r
10138   free(cp);\r
10139 }\r
10140 \r
10141 void\r
10142 InterruptChildProcess(ProcRef pr)\r
10143 {\r
10144   ChildProc *cp;\r
10145 \r
10146   cp = (ChildProc *) pr;\r
10147   if (cp == NULL) return;\r
10148   switch (cp->kind) {\r
10149   case CPReal:\r
10150     /* The following doesn't work because the chess program\r
10151        doesn't "have the same console" as WinBoard.  Maybe\r
10152        we could arrange for this even though neither WinBoard\r
10153        nor the chess program uses a console for stdio */\r
10154     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10155     break;\r
10156 \r
10157   case CPComm:\r
10158   case CPSock:\r
10159     /* Can't interrupt */\r
10160     break;\r
10161 \r
10162   case CPRcmd:\r
10163     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10164     break;\r
10165   }\r
10166 }\r
10167 \r
10168 \r
10169 int\r
10170 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10171 {\r
10172   char cmdLine[MSG_SIZ];\r
10173 \r
10174   if (port[0] == NULLCHAR) {\r
10175     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10176   } else {\r
10177     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10178   }\r
10179   return StartChildProcess(cmdLine, "", pr);\r
10180 }\r
10181 \r
10182 \r
10183 /* Code to open TCP sockets */\r
10184 \r
10185 int\r
10186 OpenTCP(char *host, char *port, ProcRef *pr)\r
10187 {\r
10188   ChildProc *cp;\r
10189   int err;\r
10190   SOCKET s;\r
10191   struct sockaddr_in sa, mysa;\r
10192   struct hostent FAR *hp;\r
10193   unsigned short uport;\r
10194   WORD wVersionRequested;\r
10195   WSADATA wsaData;\r
10196 \r
10197   /* Initialize socket DLL */\r
10198   wVersionRequested = MAKEWORD(1, 1);\r
10199   err = WSAStartup(wVersionRequested, &wsaData);\r
10200   if (err != 0) return err;\r
10201 \r
10202   /* Make socket */\r
10203   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10204     err = WSAGetLastError();\r
10205     WSACleanup();\r
10206     return err;\r
10207   }\r
10208 \r
10209   /* Bind local address using (mostly) don't-care values.\r
10210    */\r
10211   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10212   mysa.sin_family = AF_INET;\r
10213   mysa.sin_addr.s_addr = INADDR_ANY;\r
10214   uport = (unsigned short) 0;\r
10215   mysa.sin_port = htons(uport);\r
10216   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10217       == SOCKET_ERROR) {\r
10218     err = WSAGetLastError();\r
10219     WSACleanup();\r
10220     return err;\r
10221   }\r
10222 \r
10223   /* Resolve remote host name */\r
10224   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10225   if (!(hp = gethostbyname(host))) {\r
10226     unsigned int b0, b1, b2, b3;\r
10227 \r
10228     err = WSAGetLastError();\r
10229 \r
10230     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10231       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10232       hp->h_addrtype = AF_INET;\r
10233       hp->h_length = 4;\r
10234       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10235       hp->h_addr_list[0] = (char *) malloc(4);\r
10236       hp->h_addr_list[0][0] = (char) b0;\r
10237       hp->h_addr_list[0][1] = (char) b1;\r
10238       hp->h_addr_list[0][2] = (char) b2;\r
10239       hp->h_addr_list[0][3] = (char) b3;\r
10240     } else {\r
10241       WSACleanup();\r
10242       return err;\r
10243     }\r
10244   }\r
10245   sa.sin_family = hp->h_addrtype;\r
10246   uport = (unsigned short) atoi(port);\r
10247   sa.sin_port = htons(uport);\r
10248   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10249 \r
10250   /* Make connection */\r
10251   if (connect(s, (struct sockaddr *) &sa,\r
10252               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10253     err = WSAGetLastError();\r
10254     WSACleanup();\r
10255     return err;\r
10256   }\r
10257 \r
10258   /* Prepare return value */\r
10259   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10260   cp->kind = CPSock;\r
10261   cp->sock = s;\r
10262   *pr = (ProcRef *) cp;\r
10263 \r
10264   return NO_ERROR;\r
10265 }\r
10266 \r
10267 int\r
10268 OpenCommPort(char *name, ProcRef *pr)\r
10269 {\r
10270   HANDLE h;\r
10271   COMMTIMEOUTS ct;\r
10272   ChildProc *cp;\r
10273   char fullname[MSG_SIZ];\r
10274 \r
10275   if (*name != '\\')\r
10276     sprintf(fullname, "\\\\.\\%s", name);\r
10277   else\r
10278     strcpy(fullname, name);\r
10279 \r
10280   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10281                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10282   if (h == (HANDLE) -1) {\r
10283     return GetLastError();\r
10284   }\r
10285   hCommPort = h;\r
10286 \r
10287   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10288 \r
10289   /* Accumulate characters until a 100ms pause, then parse */\r
10290   ct.ReadIntervalTimeout = 100;\r
10291   ct.ReadTotalTimeoutMultiplier = 0;\r
10292   ct.ReadTotalTimeoutConstant = 0;\r
10293   ct.WriteTotalTimeoutMultiplier = 0;\r
10294   ct.WriteTotalTimeoutConstant = 0;\r
10295   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10296 \r
10297   /* Prepare return value */\r
10298   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10299   cp->kind = CPComm;\r
10300   cp->hFrom = h;\r
10301   cp->hTo = h;\r
10302   *pr = (ProcRef *) cp;\r
10303 \r
10304   return NO_ERROR;\r
10305 }\r
10306 \r
10307 int\r
10308 OpenLoopback(ProcRef *pr)\r
10309 {\r
10310   DisplayFatalError("Not implemented", 0, 1);\r
10311   return NO_ERROR;\r
10312 }\r
10313 \r
10314 \r
10315 int\r
10316 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10317 {\r
10318   ChildProc *cp;\r
10319   int err;\r
10320   SOCKET s, s2, s3;\r
10321   struct sockaddr_in sa, mysa;\r
10322   struct hostent FAR *hp;\r
10323   unsigned short uport;\r
10324   WORD wVersionRequested;\r
10325   WSADATA wsaData;\r
10326   int fromPort;\r
10327   char stderrPortStr[MSG_SIZ];\r
10328 \r
10329   /* Initialize socket DLL */\r
10330   wVersionRequested = MAKEWORD(1, 1);\r
10331   err = WSAStartup(wVersionRequested, &wsaData);\r
10332   if (err != 0) return err;\r
10333 \r
10334   /* Resolve remote host name */\r
10335   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10336   if (!(hp = gethostbyname(host))) {\r
10337     unsigned int b0, b1, b2, b3;\r
10338 \r
10339     err = WSAGetLastError();\r
10340 \r
10341     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10342       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10343       hp->h_addrtype = AF_INET;\r
10344       hp->h_length = 4;\r
10345       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10346       hp->h_addr_list[0] = (char *) malloc(4);\r
10347       hp->h_addr_list[0][0] = (char) b0;\r
10348       hp->h_addr_list[0][1] = (char) b1;\r
10349       hp->h_addr_list[0][2] = (char) b2;\r
10350       hp->h_addr_list[0][3] = (char) b3;\r
10351     } else {\r
10352       WSACleanup();\r
10353       return err;\r
10354     }\r
10355   }\r
10356   sa.sin_family = hp->h_addrtype;\r
10357   uport = (unsigned short) 514;\r
10358   sa.sin_port = htons(uport);\r
10359   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10360 \r
10361   /* Bind local socket to unused "privileged" port address\r
10362    */\r
10363   s = INVALID_SOCKET;\r
10364   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10365   mysa.sin_family = AF_INET;\r
10366   mysa.sin_addr.s_addr = INADDR_ANY;\r
10367   for (fromPort = 1023;; fromPort--) {\r
10368     if (fromPort < 0) {\r
10369       WSACleanup();\r
10370       return WSAEADDRINUSE;\r
10371     }\r
10372     if (s == INVALID_SOCKET) {\r
10373       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10374         err = WSAGetLastError();\r
10375         WSACleanup();\r
10376         return err;\r
10377       }\r
10378     }\r
10379     uport = (unsigned short) fromPort;\r
10380     mysa.sin_port = htons(uport);\r
10381     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10382         == SOCKET_ERROR) {\r
10383       err = WSAGetLastError();\r
10384       if (err == WSAEADDRINUSE) continue;\r
10385       WSACleanup();\r
10386       return err;\r
10387     }\r
10388     if (connect(s, (struct sockaddr *) &sa,\r
10389       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10390       err = WSAGetLastError();\r
10391       if (err == WSAEADDRINUSE) {\r
10392         closesocket(s);\r
10393         s = -1;\r
10394         continue;\r
10395       }\r
10396       WSACleanup();\r
10397       return err;\r
10398     }\r
10399     break;\r
10400   }\r
10401 \r
10402   /* Bind stderr local socket to unused "privileged" port address\r
10403    */\r
10404   s2 = INVALID_SOCKET;\r
10405   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10406   mysa.sin_family = AF_INET;\r
10407   mysa.sin_addr.s_addr = INADDR_ANY;\r
10408   for (fromPort = 1023;; fromPort--) {\r
10409     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10410     if (fromPort < 0) {\r
10411       (void) closesocket(s);\r
10412       WSACleanup();\r
10413       return WSAEADDRINUSE;\r
10414     }\r
10415     if (s2 == INVALID_SOCKET) {\r
10416       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10417         err = WSAGetLastError();\r
10418         closesocket(s);\r
10419         WSACleanup();\r
10420         return err;\r
10421       }\r
10422     }\r
10423     uport = (unsigned short) fromPort;\r
10424     mysa.sin_port = htons(uport);\r
10425     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10426         == SOCKET_ERROR) {\r
10427       err = WSAGetLastError();\r
10428       if (err == WSAEADDRINUSE) continue;\r
10429       (void) closesocket(s);\r
10430       WSACleanup();\r
10431       return err;\r
10432     }\r
10433     if (listen(s2, 1) == SOCKET_ERROR) {\r
10434       err = WSAGetLastError();\r
10435       if (err == WSAEADDRINUSE) {\r
10436         closesocket(s2);\r
10437         s2 = INVALID_SOCKET;\r
10438         continue;\r
10439       }\r
10440       (void) closesocket(s);\r
10441       (void) closesocket(s2);\r
10442       WSACleanup();\r
10443       return err;\r
10444     }\r
10445     break;\r
10446   }\r
10447   prevStderrPort = fromPort; // remember port used\r
10448   sprintf(stderrPortStr, "%d", fromPort);\r
10449 \r
10450   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10451     err = WSAGetLastError();\r
10452     (void) closesocket(s);\r
10453     (void) closesocket(s2);\r
10454     WSACleanup();\r
10455     return err;\r
10456   }\r
10457 \r
10458   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10459     err = WSAGetLastError();\r
10460     (void) closesocket(s);\r
10461     (void) closesocket(s2);\r
10462     WSACleanup();\r
10463     return err;\r
10464   }\r
10465   if (*user == NULLCHAR) user = UserName();\r
10466   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10467     err = WSAGetLastError();\r
10468     (void) closesocket(s);\r
10469     (void) closesocket(s2);\r
10470     WSACleanup();\r
10471     return err;\r
10472   }\r
10473   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10474     err = WSAGetLastError();\r
10475     (void) closesocket(s);\r
10476     (void) closesocket(s2);\r
10477     WSACleanup();\r
10478     return err;\r
10479   }\r
10480 \r
10481   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10482     err = WSAGetLastError();\r
10483     (void) closesocket(s);\r
10484     (void) closesocket(s2);\r
10485     WSACleanup();\r
10486     return err;\r
10487   }\r
10488   (void) closesocket(s2);  /* Stop listening */\r
10489 \r
10490   /* Prepare return value */\r
10491   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10492   cp->kind = CPRcmd;\r
10493   cp->sock = s;\r
10494   cp->sock2 = s3;\r
10495   *pr = (ProcRef *) cp;\r
10496 \r
10497   return NO_ERROR;\r
10498 }\r
10499 \r
10500 \r
10501 InputSourceRef\r
10502 AddInputSource(ProcRef pr, int lineByLine,\r
10503                InputCallback func, VOIDSTAR closure)\r
10504 {\r
10505   InputSource *is, *is2 = NULL;\r
10506   ChildProc *cp = (ChildProc *) pr;\r
10507 \r
10508   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10509   is->lineByLine = lineByLine;\r
10510   is->func = func;\r
10511   is->closure = closure;\r
10512   is->second = NULL;\r
10513   is->next = is->buf;\r
10514   if (pr == NoProc) {\r
10515     is->kind = CPReal;\r
10516     consoleInputSource = is;\r
10517   } else {\r
10518     is->kind = cp->kind;\r
10519     /* \r
10520         [AS] Try to avoid a race condition if the thread is given control too early:\r
10521         we create all threads suspended so that the is->hThread variable can be\r
10522         safely assigned, then let the threads start with ResumeThread.\r
10523     */\r
10524     switch (cp->kind) {\r
10525     case CPReal:\r
10526       is->hFile = cp->hFrom;\r
10527       cp->hFrom = NULL; /* now owned by InputThread */\r
10528       is->hThread =\r
10529         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10530                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10531       break;\r
10532 \r
10533     case CPComm:\r
10534       is->hFile = cp->hFrom;\r
10535       cp->hFrom = NULL; /* now owned by InputThread */\r
10536       is->hThread =\r
10537         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10538                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10539       break;\r
10540 \r
10541     case CPSock:\r
10542       is->sock = cp->sock;\r
10543       is->hThread =\r
10544         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10545                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10546       break;\r
10547 \r
10548     case CPRcmd:\r
10549       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10550       *is2 = *is;\r
10551       is->sock = cp->sock;\r
10552       is->second = is2;\r
10553       is2->sock = cp->sock2;\r
10554       is2->second = is2;\r
10555       is->hThread =\r
10556         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10557                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10558       is2->hThread =\r
10559         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10560                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10561       break;\r
10562     }\r
10563 \r
10564     if( is->hThread != NULL ) {\r
10565         ResumeThread( is->hThread );\r
10566     }\r
10567 \r
10568     if( is2 != NULL && is2->hThread != NULL ) {\r
10569         ResumeThread( is2->hThread );\r
10570     }\r
10571   }\r
10572 \r
10573   return (InputSourceRef) is;\r
10574 }\r
10575 \r
10576 void\r
10577 RemoveInputSource(InputSourceRef isr)\r
10578 {\r
10579   InputSource *is;\r
10580 \r
10581   is = (InputSource *) isr;\r
10582   is->hThread = NULL;  /* tell thread to stop */\r
10583   CloseHandle(is->hThread);\r
10584   if (is->second != NULL) {\r
10585     is->second->hThread = NULL;\r
10586     CloseHandle(is->second->hThread);\r
10587   }\r
10588 }\r
10589 \r
10590 \r
10591 int\r
10592 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10593 {\r
10594   DWORD dOutCount;\r
10595   int outCount = SOCKET_ERROR;\r
10596   ChildProc *cp = (ChildProc *) pr;\r
10597   static OVERLAPPED ovl;\r
10598 \r
10599   if (pr == NoProc) {\r
10600     ConsoleOutput(message, count, FALSE);\r
10601     return count;\r
10602   } \r
10603 \r
10604   if (ovl.hEvent == NULL) {\r
10605     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10606   }\r
10607   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10608 \r
10609   switch (cp->kind) {\r
10610   case CPSock:\r
10611   case CPRcmd:\r
10612     outCount = send(cp->sock, message, count, 0);\r
10613     if (outCount == SOCKET_ERROR) {\r
10614       *outError = WSAGetLastError();\r
10615     } else {\r
10616       *outError = NO_ERROR;\r
10617     }\r
10618     break;\r
10619 \r
10620   case CPReal:\r
10621     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10622                   &dOutCount, NULL)) {\r
10623       *outError = NO_ERROR;\r
10624       outCount = (int) dOutCount;\r
10625     } else {\r
10626       *outError = GetLastError();\r
10627     }\r
10628     break;\r
10629 \r
10630   case CPComm:\r
10631     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10632                             &dOutCount, &ovl);\r
10633     if (*outError == NO_ERROR) {\r
10634       outCount = (int) dOutCount;\r
10635     }\r
10636     break;\r
10637   }\r
10638   return outCount;\r
10639 }\r
10640 \r
10641 int\r
10642 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10643                        long msdelay)\r
10644 {\r
10645   /* Ignore delay, not implemented for WinBoard */\r
10646   return OutputToProcess(pr, message, count, outError);\r
10647 }\r
10648 \r
10649 \r
10650 void\r
10651 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10652                         char *buf, int count, int error)\r
10653 {\r
10654   DisplayFatalError("Not implemented", 0, 1);\r
10655 }\r
10656 \r
10657 /* see wgamelist.c for Game List functions */\r
10658 /* see wedittags.c for Edit Tags functions */\r
10659 \r
10660 \r
10661 VOID\r
10662 ICSInitScript()\r
10663 {\r
10664   FILE *f;\r
10665   char buf[MSG_SIZ];\r
10666   char *dummy;\r
10667 \r
10668   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10669     f = fopen(buf, "r");\r
10670     if (f != NULL) {\r
10671       ProcessICSInitScript(f);\r
10672       fclose(f);\r
10673     }\r
10674   }\r
10675 }\r
10676 \r
10677 \r
10678 VOID\r
10679 StartAnalysisClock()\r
10680 {\r
10681   if (analysisTimerEvent) return;\r
10682   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10683                                         (UINT) 2000, NULL);\r
10684 }\r
10685 \r
10686 LRESULT CALLBACK\r
10687 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10688 {\r
10689   static HANDLE hwndText;\r
10690   RECT rect;\r
10691   static int sizeX, sizeY;\r
10692   int newSizeX, newSizeY, flags;\r
10693   MINMAXINFO *mmi;\r
10694 \r
10695   switch (message) {\r
10696   case WM_INITDIALOG: /* message: initialize dialog box */\r
10697     /* Initialize the dialog items */\r
10698     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10699     SetWindowText(hDlg, analysisTitle);\r
10700     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10701     /* Size and position the dialog */\r
10702     if (!analysisDialog) {\r
10703       analysisDialog = hDlg;\r
10704       flags = SWP_NOZORDER;\r
10705       GetClientRect(hDlg, &rect);\r
10706       sizeX = rect.right;\r
10707       sizeY = rect.bottom;\r
10708       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10709           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10710         WINDOWPLACEMENT wp;\r
10711         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10712         wp.length = sizeof(WINDOWPLACEMENT);\r
10713         wp.flags = 0;\r
10714         wp.showCmd = SW_SHOW;\r
10715         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10716         wp.rcNormalPosition.left = analysisX;\r
10717         wp.rcNormalPosition.right = analysisX + analysisW;\r
10718         wp.rcNormalPosition.top = analysisY;\r
10719         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10720         SetWindowPlacement(hDlg, &wp);\r
10721 \r
10722         GetClientRect(hDlg, &rect);\r
10723         newSizeX = rect.right;\r
10724         newSizeY = rect.bottom;\r
10725         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10726                               newSizeX, newSizeY);\r
10727         sizeX = newSizeX;\r
10728         sizeY = newSizeY;\r
10729       }\r
10730     }\r
10731     return FALSE;\r
10732 \r
10733   case WM_COMMAND: /* message: received a command */\r
10734     switch (LOWORD(wParam)) {\r
10735     case IDCANCEL:\r
10736       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10737           ExitAnalyzeMode();\r
10738           ModeHighlight();\r
10739           return TRUE;\r
10740       }\r
10741       EditGameEvent();\r
10742       return TRUE;\r
10743     default:\r
10744       break;\r
10745     }\r
10746     break;\r
10747 \r
10748   case WM_SIZE:\r
10749     newSizeX = LOWORD(lParam);\r
10750     newSizeY = HIWORD(lParam);\r
10751     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10752     sizeX = newSizeX;\r
10753     sizeY = newSizeY;\r
10754     break;\r
10755 \r
10756   case WM_GETMINMAXINFO:\r
10757     /* Prevent resizing window too small */\r
10758     mmi = (MINMAXINFO *) lParam;\r
10759     mmi->ptMinTrackSize.x = 100;\r
10760     mmi->ptMinTrackSize.y = 100;\r
10761     break;\r
10762   }\r
10763   return FALSE;\r
10764 }\r
10765 \r
10766 VOID\r
10767 AnalysisPopUp(char* title, char* str)\r
10768 {\r
10769   FARPROC lpProc;\r
10770   char *p, *q;\r
10771 \r
10772   /* [AS] */\r
10773   EngineOutputPopUp();\r
10774   return;\r
10775 \r
10776   if (str == NULL) str = "";\r
10777   p = (char *) malloc(2 * strlen(str) + 2);\r
10778   q = p;\r
10779   while (*str) {\r
10780     if (*str == '\n') *q++ = '\r';\r
10781     *q++ = *str++;\r
10782   }\r
10783   *q = NULLCHAR;\r
10784   if (analysisText != NULL) free(analysisText);\r
10785   analysisText = p;\r
10786 \r
10787   if (analysisDialog) {\r
10788     SetWindowText(analysisDialog, title);\r
10789     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10790     ShowWindow(analysisDialog, SW_SHOW);\r
10791   } else {\r
10792     analysisTitle = title;\r
10793     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10794     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10795                  hwndMain, (DLGPROC)lpProc);\r
10796     FreeProcInstance(lpProc);\r
10797   }\r
10798   analysisDialogUp = TRUE;  \r
10799 }\r
10800 \r
10801 VOID\r
10802 AnalysisPopDown()\r
10803 {\r
10804   if (analysisDialog) {\r
10805     ShowWindow(analysisDialog, SW_HIDE);\r
10806   }\r
10807   analysisDialogUp = FALSE;  \r
10808 }\r
10809 \r
10810 \r
10811 VOID\r
10812 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10813 {\r
10814   highlightInfo.sq[0].x = fromX;\r
10815   highlightInfo.sq[0].y = fromY;\r
10816   highlightInfo.sq[1].x = toX;\r
10817   highlightInfo.sq[1].y = toY;\r
10818 }\r
10819 \r
10820 VOID\r
10821 ClearHighlights()\r
10822 {\r
10823   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10824     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10825 }\r
10826 \r
10827 VOID\r
10828 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10829 {\r
10830   premoveHighlightInfo.sq[0].x = fromX;\r
10831   premoveHighlightInfo.sq[0].y = fromY;\r
10832   premoveHighlightInfo.sq[1].x = toX;\r
10833   premoveHighlightInfo.sq[1].y = toY;\r
10834 }\r
10835 \r
10836 VOID\r
10837 ClearPremoveHighlights()\r
10838 {\r
10839   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10840     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10841 }\r
10842 \r
10843 VOID\r
10844 ShutDownFrontEnd()\r
10845 {\r
10846   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10847   DeleteClipboardTempFiles();\r
10848 }\r
10849 \r
10850 void\r
10851 BoardToTop()\r
10852 {\r
10853     if (IsIconic(hwndMain))\r
10854       ShowWindow(hwndMain, SW_RESTORE);\r
10855 \r
10856     SetActiveWindow(hwndMain);\r
10857 }\r
10858 \r
10859 /*\r
10860  * Prototypes for animation support routines\r
10861  */\r
10862 static void ScreenSquare(int column, int row, POINT * pt);\r
10863 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10864      POINT frames[], int * nFrames);\r
10865 \r
10866 \r
10867 void\r
10868 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10869 {       // [HGM] atomic: animate blast wave\r
10870         int i;\r
10871 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10872         explodeInfo.fromX = fromX;\r
10873         explodeInfo.fromY = fromY;\r
10874         explodeInfo.toX = toX;\r
10875         explodeInfo.toY = toY;\r
10876         for(i=1; i<nFrames; i++) {\r
10877             explodeInfo.radius = (i*180)/(nFrames-1);\r
10878             DrawPosition(FALSE, NULL);\r
10879             Sleep(appData.animSpeed);\r
10880         }\r
10881         explodeInfo.radius = 0;\r
10882         DrawPosition(TRUE, NULL);\r
10883 }\r
10884 \r
10885 #define kFactor 4\r
10886 \r
10887 void\r
10888 AnimateMove(board, fromX, fromY, toX, toY)\r
10889      Board board;\r
10890      int fromX;\r
10891      int fromY;\r
10892      int toX;\r
10893      int toY;\r
10894 {\r
10895   ChessSquare piece;\r
10896   POINT start, finish, mid;\r
10897   POINT frames[kFactor * 2 + 1];\r
10898   int nFrames, n;\r
10899 \r
10900   if (!appData.animate) return;\r
10901   if (doingSizing) return;\r
10902   if (fromY < 0 || fromX < 0) return;\r
10903   piece = board[fromY][fromX];\r
10904   if (piece >= EmptySquare) return;\r
10905 \r
10906   ScreenSquare(fromX, fromY, &start);\r
10907   ScreenSquare(toX, toY, &finish);\r
10908 \r
10909   /* All pieces except knights move in straight line */\r
10910   if (piece != WhiteKnight && piece != BlackKnight) {\r
10911     mid.x = start.x + (finish.x - start.x) / 2;\r
10912     mid.y = start.y + (finish.y - start.y) / 2;\r
10913   } else {\r
10914     /* Knight: make diagonal movement then straight */\r
10915     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10916        mid.x = start.x + (finish.x - start.x) / 2;\r
10917        mid.y = finish.y;\r
10918      } else {\r
10919        mid.x = finish.x;\r
10920        mid.y = start.y + (finish.y - start.y) / 2;\r
10921      }\r
10922   }\r
10923   \r
10924   /* Don't use as many frames for very short moves */\r
10925   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10926     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10927   else\r
10928     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10929 \r
10930   animInfo.from.x = fromX;\r
10931   animInfo.from.y = fromY;\r
10932   animInfo.to.x = toX;\r
10933   animInfo.to.y = toY;\r
10934   animInfo.lastpos = start;\r
10935   animInfo.piece = piece;\r
10936   for (n = 0; n < nFrames; n++) {\r
10937     animInfo.pos = frames[n];\r
10938     DrawPosition(FALSE, NULL);\r
10939     animInfo.lastpos = animInfo.pos;\r
10940     Sleep(appData.animSpeed);\r
10941   }\r
10942   animInfo.pos = finish;\r
10943   DrawPosition(FALSE, NULL);\r
10944   animInfo.piece = EmptySquare;\r
10945   if(gameInfo.variant == VariantAtomic && \r
10946      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10947         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10948 }\r
10949 \r
10950 /*      Convert board position to corner of screen rect and color       */\r
10951 \r
10952 static void\r
10953 ScreenSquare(column, row, pt)\r
10954      int column; int row; POINT * pt;\r
10955 {\r
10956   if (flipView) {\r
10957     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10958     pt->y = lineGap + row * (squareSize + lineGap);\r
10959   } else {\r
10960     pt->x = lineGap + column * (squareSize + lineGap);\r
10961     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10962   }\r
10963 }\r
10964 \r
10965 /*      Generate a series of frame coords from start->mid->finish.\r
10966         The movement rate doubles until the half way point is\r
10967         reached, then halves back down to the final destination,\r
10968         which gives a nice slow in/out effect. The algorithmn\r
10969         may seem to generate too many intermediates for short\r
10970         moves, but remember that the purpose is to attract the\r
10971         viewers attention to the piece about to be moved and\r
10972         then to where it ends up. Too few frames would be less\r
10973         noticeable.                                             */\r
10974 \r
10975 static void\r
10976 Tween(start, mid, finish, factor, frames, nFrames)\r
10977      POINT * start; POINT * mid;\r
10978      POINT * finish; int factor;\r
10979      POINT frames[]; int * nFrames;\r
10980 {\r
10981   int n, fraction = 1, count = 0;\r
10982 \r
10983   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10984   for (n = 0; n < factor; n++)\r
10985     fraction *= 2;\r
10986   for (n = 0; n < factor; n++) {\r
10987     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10988     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10989     count ++;\r
10990     fraction = fraction / 2;\r
10991   }\r
10992   \r
10993   /* Midpoint */\r
10994   frames[count] = *mid;\r
10995   count ++;\r
10996   \r
10997   /* Slow out, stepping 1/2, then 1/4, ... */\r
10998   fraction = 2;\r
10999   for (n = 0; n < factor; n++) {\r
11000     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
11001     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
11002     count ++;\r
11003     fraction = fraction * 2;\r
11004   }\r
11005   *nFrames = count;\r
11006 }\r
11007 \r
11008 void\r
11009 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
11010 {\r
11011 #if 0\r
11012     char buf[256];\r
11013 \r
11014     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
11015         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
11016 \r
11017     OutputDebugString( buf );\r
11018 #endif\r
11019 \r
11020     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
11021 \r
11022     EvalGraphSet( first, last, current, pvInfoList );\r
11023 }\r
11024 \r
11025 void SetProgramStats( FrontEndProgramStats * stats )\r
11026 {\r
11027 #if 0\r
11028     char buf[1024];\r
11029 \r
11030     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
11031         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
11032 \r
11033     OutputDebugString( buf );\r
11034 #endif\r
11035 \r
11036     EngineOutputUpdate( stats );\r
11037 }\r