4ab4f93e06102cac56aaf1f4d5b118a5bdec454c
[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   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1109   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1110   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1111   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1112   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1113   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1114   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1115   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1116   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1117   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1118   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1119   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1120   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1121   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1122   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1123   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1124   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1125   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1126   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1127   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1128   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1129   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1130   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1131   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1132   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1133   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1134   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1135   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1136   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1137   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1138   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1139   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1140   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1141   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1142   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1143   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1144   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1145   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1146   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1147   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1148   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1149   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1150   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1151   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1152   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1153   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1154   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1155   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1156   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1157   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1158   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1159   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1160   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1161   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1162   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1163   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1164   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1165   { "highlightLastMove", ArgBoolean,\r
1166     (LPVOID) &appData.highlightLastMove, TRUE },\r
1167   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1168   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1169   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1170   { "highlightDragging", ArgBoolean,\r
1171     (LPVOID) &appData.highlightDragging, TRUE },\r
1172   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1173   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1174   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1175   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1176   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1177   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1178   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1179   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1180   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1181   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1182   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1183   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1184   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1185   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1186   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1187   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1188   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1189   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1190   { "soundShout", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1192   { "soundSShout", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1194   { "soundChannel1", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1196   { "soundChannel", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1198   { "soundKibitz", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1200   { "soundTell", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1202   { "soundChallenge", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1204   { "soundRequest", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1206   { "soundSeek", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1208   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1209   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1210   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1211   { "soundIcsLoss", ArgFilename, \r
1212     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1213   { "soundIcsDraw", ArgFilename, \r
1214     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1215   { "soundIcsUnfinished", ArgFilename, \r
1216     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1217   { "soundIcsAlarm", ArgFilename, \r
1218     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1219   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1220   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1221   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1222   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1223   { "reuseChessPrograms", ArgBoolean,\r
1224     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1225   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1226   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1227   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1228   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1229   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1230   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1231   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1232   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1233   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1234   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1235   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1236   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1237   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1238   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1239   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1240     TRUE },\r
1241   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1242     TRUE },\r
1243   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1244   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1245   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1246   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1247   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1248   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1249   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1250   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1251   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1252   /* [AS] New features */\r
1253   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1254   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1255   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1256   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1257   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1258   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1259   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1260   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1261   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1262   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1263   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1264   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1265   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1266   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1267   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1268   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1269   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1270   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1271   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1272   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1273   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1274   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1275   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1276   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1277   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1278   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1279   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1280   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1281   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1282   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1283   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1284   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1285   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1286   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1287   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1288   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1289   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1290   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1291   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1292   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1293   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1294   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1295   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1296   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1297   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1298   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1299   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1300   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1301   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1302   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1303 \r
1304   /* [HGM] board-size, adjudication and misc. options */\r
1305   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1306   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1307   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1308   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1309   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1310   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1311   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1312   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1313   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1314   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1315   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1316   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1317   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1318   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1319   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1320   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1321   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1322   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1323   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1324   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1325   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1326   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1327   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1328   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1329   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1330   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1331   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1332   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1333   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1334   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1335   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1336   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1337   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1338   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1339 \r
1340 #ifdef ZIPPY\r
1341   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1342   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1343   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1344   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1345   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1346   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1347   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1348   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1349   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1350   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1351   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1352   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1353   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1354     FALSE },\r
1355   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1356   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1357   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1358   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1359   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1360   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1361   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1362     FALSE },\r
1363   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1364   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1365   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1366   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1367   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1368   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1369   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1370   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1371   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1372   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1373   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1374   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1375   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1376   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1377   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1378   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1379   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1380   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1381   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1382 #endif\r
1383   /* [HGM] options for broadcasting and time odds */\r
1384   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1385   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1386   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1387   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1388   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1389   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1390   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1391   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1392   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1393   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1394   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1395   { "keepLineBreaksICS", ArgBoolean, (LPVOID) &appData.noJoin, TRUE },\r
1396 \r
1397   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1398   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1399   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1400   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1401   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1402   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1403   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1404   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1405   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1406   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1407   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1408   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1409   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1410   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1411   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1412   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1413   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1414   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1415   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1416   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1417   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1418   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1419   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1420   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1421   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1422   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1423   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1424   /* [AS] Layout stuff */\r
1425   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1426   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1427   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1428   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1429   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1430 \r
1431   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1432   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1433   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1434   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1435   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1436 \r
1437   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1438   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1439   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1440   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1441   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1442 \r
1443   { NULL, ArgNone, NULL, FALSE }\r
1444 };\r
1445 \r
1446 \r
1447 /* Kludge for indirection files on command line */\r
1448 char* lastIndirectionFilename;\r
1449 ArgDescriptor argDescriptorIndirection =\r
1450 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1451 \r
1452 \r
1453 VOID\r
1454 ExitArgError(char *msg, char *badArg)\r
1455 {\r
1456   char buf[MSG_SIZ];\r
1457 \r
1458   sprintf(buf, "%s %s", msg, badArg);\r
1459   DisplayFatalError(buf, 0, 2);\r
1460   exit(2);\r
1461 }\r
1462 \r
1463 /* Command line font name parser.  NULL name means do nothing.\r
1464    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1465    For backward compatibility, syntax without the colon is also\r
1466    accepted, but font names with digits in them won't work in that case.\r
1467 */\r
1468 VOID\r
1469 ParseFontName(char *name, MyFontParams *mfp)\r
1470 {\r
1471   char *p, *q;\r
1472   if (name == NULL) return;\r
1473   p = name;\r
1474   q = strchr(p, ':');\r
1475   if (q) {\r
1476     if (q - p >= sizeof(mfp->faceName))\r
1477       ExitArgError("Font name too long:", name);\r
1478     memcpy(mfp->faceName, p, q - p);\r
1479     mfp->faceName[q - p] = NULLCHAR;\r
1480     p = q + 1;\r
1481   } else {\r
1482     q = mfp->faceName;\r
1483     while (*p && !isdigit(*p)) {\r
1484       *q++ = *p++;\r
1485       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1486         ExitArgError("Font name too long:", name);\r
1487     }\r
1488     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1489     *q = NULLCHAR;\r
1490   }\r
1491   if (!*p) ExitArgError("Font point size missing:", name);\r
1492   mfp->pointSize = (float) atof(p);\r
1493   mfp->bold = (strchr(p, 'b') != NULL);\r
1494   mfp->italic = (strchr(p, 'i') != NULL);\r
1495   mfp->underline = (strchr(p, 'u') != NULL);\r
1496   mfp->strikeout = (strchr(p, 's') != NULL);\r
1497 }\r
1498 \r
1499 /* Color name parser.\r
1500    X version accepts X color names, but this one\r
1501    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1502 COLORREF\r
1503 ParseColorName(char *name)\r
1504 {\r
1505   int red, green, blue, count;\r
1506   char buf[MSG_SIZ];\r
1507 \r
1508   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1509   if (count != 3) {\r
1510     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1511       &red, &green, &blue);\r
1512   }\r
1513   if (count != 3) {\r
1514     sprintf(buf, "Can't parse color name %s", name);\r
1515     DisplayError(buf, 0);\r
1516     return RGB(0, 0, 0);\r
1517   }\r
1518   return PALETTERGB(red, green, blue);\r
1519 }\r
1520 \r
1521 \r
1522 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1523 {\r
1524   char *e = argValue;\r
1525   int eff = 0;\r
1526 \r
1527   while (*e) {\r
1528     if (*e == 'b')      eff |= CFE_BOLD;\r
1529     else if (*e == 'i') eff |= CFE_ITALIC;\r
1530     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1531     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1532     else if (*e == '#' || isdigit(*e)) break;\r
1533     e++;\r
1534   }\r
1535   *effects = eff;\r
1536   *color   = ParseColorName(e);\r
1537 }\r
1538 \r
1539 \r
1540 BoardSize\r
1541 ParseBoardSize(char *name)\r
1542 {\r
1543   BoardSize bs = SizeTiny;\r
1544   while (sizeInfo[bs].name != NULL) {\r
1545     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1546     bs++;\r
1547   }\r
1548   ExitArgError("Unrecognized board size value", name);\r
1549   return bs; /* not reached */\r
1550 }\r
1551 \r
1552 \r
1553 char\r
1554 StringGet(void *getClosure)\r
1555 {\r
1556   char **p = (char **) getClosure;\r
1557   return *((*p)++);\r
1558 }\r
1559 \r
1560 char\r
1561 FileGet(void *getClosure)\r
1562 {\r
1563   int c;\r
1564   FILE* f = (FILE*) getClosure;\r
1565 \r
1566   c = getc(f);\r
1567   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1568   if (c == EOF)\r
1569     return NULLCHAR;\r
1570   else\r
1571     return (char) c;\r
1572 }\r
1573 \r
1574 /* Parse settings file named "name". If file found, return the\r
1575    full name in fullname and return TRUE; else return FALSE */\r
1576 BOOLEAN\r
1577 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1578 {\r
1579   char *dummy;\r
1580   FILE *f;\r
1581   int ok; char buf[MSG_SIZ];\r
1582 \r
1583   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1584   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1585     sprintf(buf, "%s.ini", name);\r
1586     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1587   }\r
1588   if (ok) {\r
1589     f = fopen(fullname, "r");\r
1590     if (f != NULL) {\r
1591       ParseArgs(FileGet, f);\r
1592       fclose(f);\r
1593       return TRUE;\r
1594     }\r
1595   }\r
1596   return FALSE;\r
1597 }\r
1598 \r
1599 VOID\r
1600 ParseArgs(GetFunc get, void *cl)\r
1601 {\r
1602   char argName[ARG_MAX];\r
1603   char argValue[ARG_MAX];\r
1604   ArgDescriptor *ad;\r
1605   char start;\r
1606   char *q;\r
1607   int i, octval;\r
1608   char ch;\r
1609   int posarg = 0;\r
1610 \r
1611   ch = get(cl);\r
1612   for (;;) {\r
1613     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1614     if (ch == NULLCHAR) break;\r
1615     if (ch == ';') {\r
1616       /* Comment to end of line */\r
1617       ch = get(cl);\r
1618       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1619       continue;\r
1620     } else if (ch == '/' || ch == '-') {\r
1621       /* Switch */\r
1622       q = argName;\r
1623       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1624              ch != '\n' && ch != '\t') {\r
1625         *q++ = ch;\r
1626         ch = get(cl);\r
1627       }\r
1628       *q = NULLCHAR;\r
1629 \r
1630       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1631         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1632 \r
1633       if (ad->argName == NULL)\r
1634         ExitArgError("Unrecognized argument", argName);\r
1635 \r
1636     } else if (ch == '@') {\r
1637       /* Indirection file */\r
1638       ad = &argDescriptorIndirection;\r
1639       ch = get(cl);\r
1640     } else {\r
1641       /* Positional argument */\r
1642       ad = &argDescriptors[posarg++];\r
1643       strcpy(argName, ad->argName);\r
1644     }\r
1645 \r
1646     if (ad->argType == ArgTrue) {\r
1647       *(Boolean *) ad->argLoc = TRUE;\r
1648       continue;\r
1649     }\r
1650     if (ad->argType == ArgFalse) {\r
1651       *(Boolean *) ad->argLoc = FALSE;\r
1652       continue;\r
1653     }\r
1654 \r
1655     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1656     if (ch == NULLCHAR || ch == '\n') {\r
1657       ExitArgError("No value provided for argument", argName);\r
1658     }\r
1659     q = argValue;\r
1660     if (ch == '{') {\r
1661       // Quoting with { }.  No characters have to (or can) be escaped.\r
1662       // Thus the string cannot contain a '}' character.\r
1663       start = ch;\r
1664       ch = get(cl);\r
1665       while (start) {\r
1666         switch (ch) {\r
1667         case NULLCHAR:\r
1668           start = NULLCHAR;\r
1669           break;\r
1670           \r
1671         case '}':\r
1672           ch = get(cl);\r
1673           start = NULLCHAR;\r
1674           break;\r
1675 \r
1676         default:\r
1677           *q++ = ch;\r
1678           ch = get(cl);\r
1679           break;\r
1680         }\r
1681       }   \r
1682     } else if (ch == '\'' || ch == '"') {\r
1683       // Quoting with ' ' or " ", with \ as escape character.\r
1684       // Inconvenient for long strings that may contain Windows filenames.\r
1685       start = ch;\r
1686       ch = get(cl);\r
1687       while (start) {\r
1688         switch (ch) {\r
1689         case NULLCHAR:\r
1690           start = NULLCHAR;\r
1691           break;\r
1692 \r
1693         default:\r
1694         not_special:\r
1695           *q++ = ch;\r
1696           ch = get(cl);\r
1697           break;\r
1698 \r
1699         case '\'':\r
1700         case '\"':\r
1701           if (ch == start) {\r
1702             ch = get(cl);\r
1703             start = NULLCHAR;\r
1704             break;\r
1705           } else {\r
1706             goto not_special;\r
1707           }\r
1708 \r
1709         case '\\':\r
1710           if (ad->argType == ArgFilename\r
1711               || ad->argType == ArgSettingsFilename) {\r
1712               goto not_special;\r
1713           }\r
1714           ch = get(cl);\r
1715           switch (ch) {\r
1716           case NULLCHAR:\r
1717             ExitArgError("Incomplete \\ escape in value for", argName);\r
1718             break;\r
1719           case 'n':\r
1720             *q++ = '\n';\r
1721             ch = get(cl);\r
1722             break;\r
1723           case 'r':\r
1724             *q++ = '\r';\r
1725             ch = get(cl);\r
1726             break;\r
1727           case 't':\r
1728             *q++ = '\t';\r
1729             ch = get(cl);\r
1730             break;\r
1731           case 'b':\r
1732             *q++ = '\b';\r
1733             ch = get(cl);\r
1734             break;\r
1735           case 'f':\r
1736             *q++ = '\f';\r
1737             ch = get(cl);\r
1738             break;\r
1739           default:\r
1740             octval = 0;\r
1741             for (i = 0; i < 3; i++) {\r
1742               if (ch >= '0' && ch <= '7') {\r
1743                 octval = octval*8 + (ch - '0');\r
1744                 ch = get(cl);\r
1745               } else {\r
1746                 break;\r
1747               }\r
1748             }\r
1749             if (i > 0) {\r
1750               *q++ = (char) octval;\r
1751             } else {\r
1752               *q++ = ch;\r
1753               ch = get(cl);\r
1754             }\r
1755             break;\r
1756           }\r
1757           break;\r
1758         }\r
1759       }\r
1760     } else {\r
1761       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1762         *q++ = ch;\r
1763         ch = get(cl);\r
1764       }\r
1765     }\r
1766     *q = NULLCHAR;\r
1767 \r
1768     switch (ad->argType) {\r
1769     case ArgInt:\r
1770       *(int *) ad->argLoc = atoi(argValue);\r
1771       break;\r
1772 \r
1773     case ArgX:\r
1774       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1775       break;\r
1776 \r
1777     case ArgY:\r
1778       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1779       break;\r
1780 \r
1781     case ArgZ:\r
1782       *(int *) ad->argLoc = atoi(argValue);\r
1783       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1784       break;\r
1785 \r
1786     case ArgFloat:\r
1787       *(float *) ad->argLoc = (float) atof(argValue);\r
1788       break;\r
1789 \r
1790     case ArgString:\r
1791     case ArgFilename:\r
1792       *(char **) ad->argLoc = strdup(argValue);\r
1793       break;\r
1794 \r
1795     case ArgSettingsFilename:\r
1796       {\r
1797         char fullname[MSG_SIZ];\r
1798         if (ParseSettingsFile(argValue, fullname)) {\r
1799           if (ad->argLoc != NULL) {\r
1800             *(char **) ad->argLoc = strdup(fullname);\r
1801           }\r
1802         } else {\r
1803           if (ad->argLoc != NULL) {\r
1804           } else {\r
1805             ExitArgError("Failed to open indirection file", argValue);\r
1806           }\r
1807         }\r
1808       }\r
1809       break;\r
1810 \r
1811     case ArgBoolean:\r
1812       switch (argValue[0]) {\r
1813       case 't':\r
1814       case 'T':\r
1815         *(Boolean *) ad->argLoc = TRUE;\r
1816         break;\r
1817       case 'f':\r
1818       case 'F':\r
1819         *(Boolean *) ad->argLoc = FALSE;\r
1820         break;\r
1821       default:\r
1822         ExitArgError("Unrecognized boolean argument value", argValue);\r
1823         break;\r
1824       }\r
1825       break;\r
1826 \r
1827     case ArgColor:\r
1828       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1829       break;\r
1830 \r
1831     case ArgAttribs: {\r
1832       ColorClass cc = (ColorClass)ad->argLoc;\r
1833       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1834       }\r
1835       break;\r
1836       \r
1837     case ArgBoardSize:\r
1838       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1839       break;\r
1840 \r
1841     case ArgFont:\r
1842       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1843       break;\r
1844 \r
1845     case ArgCommSettings:\r
1846       ParseCommSettings(argValue, &dcb);\r
1847       break;\r
1848 \r
1849     case ArgNone:\r
1850       ExitArgError("Unrecognized argument", argValue);\r
1851       break;\r
1852     case ArgTrue:\r
1853     case ArgFalse: ;\r
1854     }\r
1855   }\r
1856 }\r
1857 \r
1858 VOID\r
1859 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1860 {\r
1861   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1862   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1863   DeleteDC(hdc);\r
1864   lf->lfWidth = 0;\r
1865   lf->lfEscapement = 0;\r
1866   lf->lfOrientation = 0;\r
1867   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1868   lf->lfItalic = mfp->italic;\r
1869   lf->lfUnderline = mfp->underline;\r
1870   lf->lfStrikeOut = mfp->strikeout;\r
1871   lf->lfCharSet = DEFAULT_CHARSET;\r
1872   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1873   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1874   lf->lfQuality = DEFAULT_QUALITY;\r
1875   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1876   strcpy(lf->lfFaceName, mfp->faceName);\r
1877 }\r
1878 \r
1879 VOID\r
1880 CreateFontInMF(MyFont *mf)\r
1881 {\r
1882   LFfromMFP(&mf->lf, &mf->mfp);\r
1883   if (mf->hf) DeleteObject(mf->hf);\r
1884   mf->hf = CreateFontIndirect(&mf->lf);\r
1885 }\r
1886 \r
1887 VOID\r
1888 SetDefaultTextAttribs()\r
1889 {\r
1890   ColorClass cc;\r
1891   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1892     ParseAttribs(&textAttribs[cc].color, \r
1893                  &textAttribs[cc].effects, \r
1894                  defaultTextAttribs[cc]);\r
1895   }\r
1896 }\r
1897 \r
1898 VOID\r
1899 SetDefaultSounds()\r
1900 {\r
1901   ColorClass cc;\r
1902   SoundClass sc;\r
1903   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1904     textAttribs[cc].sound.name = strdup("");\r
1905     textAttribs[cc].sound.data = NULL;\r
1906   }\r
1907   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1908     sounds[sc].name = strdup("");\r
1909     sounds[sc].data = NULL;\r
1910   }\r
1911   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1912 }\r
1913 \r
1914 VOID\r
1915 LoadAllSounds()\r
1916 {\r
1917   ColorClass cc;\r
1918   SoundClass sc;\r
1919   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1920     MyLoadSound(&textAttribs[cc].sound);\r
1921   }\r
1922   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1923     MyLoadSound(&sounds[sc]);\r
1924   }\r
1925 }\r
1926 \r
1927 VOID\r
1928 InitAppData(LPSTR lpCmdLine)\r
1929 {\r
1930   int i, j;\r
1931   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1932   char *dummy, *p;\r
1933 \r
1934   programName = szAppName;\r
1935 \r
1936   /* Initialize to defaults */\r
1937   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1938   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1939   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1940   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1941   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1942   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1943   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1944   SetDefaultTextAttribs();\r
1945   SetDefaultSounds();\r
1946   appData.movesPerSession = MOVES_PER_SESSION;\r
1947   appData.initString = INIT_STRING;\r
1948   appData.secondInitString = INIT_STRING;\r
1949   appData.firstComputerString = COMPUTER_STRING;\r
1950   appData.secondComputerString = COMPUTER_STRING;\r
1951   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1952   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1953   appData.firstPlaysBlack = FALSE;\r
1954   appData.noChessProgram = FALSE;\r
1955   chessProgram = FALSE;\r
1956   appData.firstHost = FIRST_HOST;\r
1957   appData.secondHost = SECOND_HOST;\r
1958   appData.firstDirectory = FIRST_DIRECTORY;\r
1959   appData.secondDirectory = SECOND_DIRECTORY;\r
1960   appData.bitmapDirectory = "";\r
1961   appData.remoteShell = REMOTE_SHELL;\r
1962   appData.remoteUser = "";\r
1963   appData.timeDelay = TIME_DELAY;\r
1964   appData.timeControl = TIME_CONTROL;\r
1965   appData.timeIncrement = TIME_INCREMENT;\r
1966   appData.icsActive = FALSE;\r
1967   appData.icsHost = "";\r
1968   appData.icsPort = ICS_PORT;\r
1969   appData.icsCommPort = ICS_COMM_PORT;\r
1970   appData.icsLogon = ICS_LOGON;\r
1971   appData.icsHelper = "";\r
1972   appData.useTelnet = FALSE;\r
1973   appData.telnetProgram = TELNET_PROGRAM;\r
1974   appData.gateway = "";\r
1975   appData.loadGameFile = "";\r
1976   appData.loadGameIndex = 0;\r
1977   appData.saveGameFile = "";\r
1978   appData.autoSaveGames = FALSE;\r
1979   appData.loadPositionFile = "";\r
1980   appData.loadPositionIndex = 1;\r
1981   appData.savePositionFile = "";\r
1982   appData.matchMode = FALSE;\r
1983   appData.matchGames = 0;\r
1984   appData.monoMode = FALSE;\r
1985   appData.debugMode = FALSE;\r
1986   appData.clockMode = TRUE;\r
1987   boardSize = (BoardSize) -1; /* determine by screen size */\r
1988   appData.Iconic = FALSE; /*unused*/\r
1989   appData.searchTime = "";\r
1990   appData.searchDepth = 0;\r
1991   appData.showCoords = FALSE;\r
1992   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1993   appData.autoCallFlag = FALSE;\r
1994   appData.flipView = FALSE;\r
1995   appData.autoFlipView = TRUE;\r
1996   appData.cmailGameName = "";\r
1997   appData.alwaysPromoteToQueen = FALSE;\r
1998   appData.oldSaveStyle = FALSE;\r
1999   appData.quietPlay = FALSE;\r
2000   appData.showThinking = FALSE;\r
2001   appData.ponderNextMove = TRUE;\r
2002   appData.periodicUpdates = TRUE;\r
2003   appData.popupExitMessage = TRUE;\r
2004   appData.popupMoveErrors = FALSE;\r
2005   appData.autoObserve = FALSE;\r
2006   appData.autoComment = FALSE;\r
2007   appData.animate = TRUE;\r
2008   appData.animSpeed = 10;\r
2009   appData.animateDragging = TRUE;\r
2010   appData.highlightLastMove = TRUE;\r
2011   appData.getMoveList = TRUE;\r
2012   appData.testLegality = TRUE;\r
2013   appData.premove = TRUE;\r
2014   appData.premoveWhite = FALSE;\r
2015   appData.premoveWhiteText = "";\r
2016   appData.premoveBlack = FALSE;\r
2017   appData.premoveBlackText = "";\r
2018   appData.icsAlarm = TRUE;\r
2019   appData.icsAlarmTime = 5000;\r
2020   appData.autoRaiseBoard = TRUE;\r
2021   appData.localLineEditing = TRUE;\r
2022   appData.colorize = TRUE;\r
2023   appData.reuseFirst = TRUE;\r
2024   appData.reuseSecond = TRUE;\r
2025   appData.blindfold = FALSE;\r
2026   appData.icsEngineAnalyze = FALSE;\r
2027   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2028   dcb.DCBlength = sizeof(DCB);\r
2029   dcb.BaudRate = 9600;\r
2030   dcb.fBinary = TRUE;\r
2031   dcb.fParity = FALSE;\r
2032   dcb.fOutxCtsFlow = FALSE;\r
2033   dcb.fOutxDsrFlow = FALSE;\r
2034   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2035   dcb.fDsrSensitivity = FALSE;\r
2036   dcb.fTXContinueOnXoff = TRUE;\r
2037   dcb.fOutX = FALSE;\r
2038   dcb.fInX = FALSE;\r
2039   dcb.fNull = FALSE;\r
2040   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2041   dcb.fAbortOnError = FALSE;\r
2042   dcb.ByteSize = 7;\r
2043   dcb.Parity = SPACEPARITY;\r
2044   dcb.StopBits = ONESTOPBIT;\r
2045   settingsFileName = SETTINGS_FILE;\r
2046   saveSettingsOnExit = TRUE;\r
2047   boardX = CW_USEDEFAULT;\r
2048   boardY = CW_USEDEFAULT;\r
2049   analysisX = CW_USEDEFAULT; \r
2050   analysisY = CW_USEDEFAULT; \r
2051   analysisW = CW_USEDEFAULT;\r
2052   analysisH = CW_USEDEFAULT;\r
2053   commentX = CW_USEDEFAULT; \r
2054   commentY = CW_USEDEFAULT; \r
2055   commentW = CW_USEDEFAULT;\r
2056   commentH = CW_USEDEFAULT;\r
2057   editTagsX = CW_USEDEFAULT; \r
2058   editTagsY = CW_USEDEFAULT; \r
2059   editTagsW = CW_USEDEFAULT;\r
2060   editTagsH = CW_USEDEFAULT;\r
2061   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2062   icsNames = ICS_NAMES;\r
2063   firstChessProgramNames = FCP_NAMES;\r
2064   secondChessProgramNames = SCP_NAMES;\r
2065   appData.initialMode = "";\r
2066   appData.variant = "normal";\r
2067   appData.firstProtocolVersion = PROTOVER;\r
2068   appData.secondProtocolVersion = PROTOVER;\r
2069   appData.showButtonBar = TRUE;\r
2070 \r
2071    /* [AS] New properties (see comments in header file) */\r
2072   appData.firstScoreIsAbsolute = FALSE;\r
2073   appData.secondScoreIsAbsolute = FALSE;\r
2074   appData.saveExtendedInfoInPGN = FALSE;\r
2075   appData.hideThinkingFromHuman = FALSE;\r
2076   appData.liteBackTextureFile = "";\r
2077   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2078   appData.darkBackTextureFile = "";\r
2079   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2080   appData.renderPiecesWithFont = "";\r
2081   appData.fontToPieceTable = "";\r
2082   appData.fontBackColorWhite = 0;\r
2083   appData.fontForeColorWhite = 0;\r
2084   appData.fontBackColorBlack = 0;\r
2085   appData.fontForeColorBlack = 0;\r
2086   appData.fontPieceSize = 80;\r
2087   appData.overrideLineGap = 1;\r
2088   appData.adjudicateLossThreshold = 0;\r
2089   appData.delayBeforeQuit = 0;\r
2090   appData.delayAfterQuit = 0;\r
2091   appData.nameOfDebugFile = "winboard.debug";\r
2092   appData.pgnEventHeader = "Computer Chess Game";\r
2093   appData.defaultFrcPosition = -1;\r
2094   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2095   appData.saveOutOfBookInfo = TRUE;\r
2096   appData.showEvalInMoveHistory = TRUE;\r
2097   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2098   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2099   appData.highlightMoveWithArrow = FALSE;\r
2100   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2101   appData.useStickyWindows = TRUE;\r
2102   appData.adjudicateDrawMoves = 0;\r
2103   appData.autoDisplayComment = TRUE;\r
2104   appData.autoDisplayTags = TRUE;\r
2105   appData.firstIsUCI = FALSE;\r
2106   appData.secondIsUCI = FALSE;\r
2107   appData.firstHasOwnBookUCI = TRUE;\r
2108   appData.secondHasOwnBookUCI = TRUE;\r
2109   appData.polyglotDir = "";\r
2110   appData.usePolyglotBook = FALSE;\r
2111   appData.polyglotBook = "";\r
2112   appData.defaultHashSize = 64;\r
2113   appData.defaultCacheSizeEGTB = 4;\r
2114   appData.defaultPathEGTB = "c:\\egtb";\r
2115   appData.firstOptions = "";\r
2116   appData.secondOptions = "";\r
2117 \r
2118   InitWindowPlacement( &wpGameList );\r
2119   InitWindowPlacement( &wpMoveHistory );\r
2120   InitWindowPlacement( &wpEvalGraph );\r
2121   InitWindowPlacement( &wpEngineOutput );\r
2122   InitWindowPlacement( &wpConsole );\r
2123 \r
2124   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2125   appData.NrFiles      = -1;\r
2126   appData.NrRanks      = -1;\r
2127   appData.holdingsSize = -1;\r
2128   appData.testClaims   = FALSE;\r
2129   appData.checkMates   = FALSE;\r
2130   appData.materialDraws= FALSE;\r
2131   appData.trivialDraws = FALSE;\r
2132   appData.ruleMoves    = 51;\r
2133   appData.drawRepeats  = 6;\r
2134   appData.matchPause   = 10000;\r
2135   appData.alphaRank    = FALSE;\r
2136   appData.allWhite     = FALSE;\r
2137   appData.upsideDown   = FALSE;\r
2138   appData.serverPause  = 15;\r
2139   appData.serverMovesName   = NULL;\r
2140   appData.suppressLoadMoves = FALSE;\r
2141   appData.firstTimeOdds  = 1;\r
2142   appData.secondTimeOdds = 1;\r
2143   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2144   appData.secondAccumulateTC = 1;\r
2145   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2146   appData.secondNPS = -1;\r
2147   appData.engineComments = 1;\r
2148   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2149   appData.egtFormats = "";\r
2150 \r
2151 #ifdef ZIPPY\r
2152   appData.zippyTalk = ZIPPY_TALK;\r
2153   appData.zippyPlay = ZIPPY_PLAY;\r
2154   appData.zippyLines = ZIPPY_LINES;\r
2155   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2156   appData.zippyPassword = ZIPPY_PASSWORD;\r
2157   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2158   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2159   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2160   appData.zippyUseI = ZIPPY_USE_I;\r
2161   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2162   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2163   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2164   appData.zippyGameStart = ZIPPY_GAME_START;\r
2165   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2166   appData.zippyAbort = ZIPPY_ABORT;\r
2167   appData.zippyVariants = ZIPPY_VARIANTS;\r
2168   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2169   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2170 #endif\r
2171 \r
2172   /* Point font array elements to structures and\r
2173      parse default font names */\r
2174   for (i=0; i<NUM_FONTS; i++) {\r
2175     for (j=0; j<NUM_SIZES; j++) {\r
2176       font[j][i] = &fontRec[j][i];\r
2177       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2178     }\r
2179   }\r
2180   \r
2181   /* Parse default settings file if any */\r
2182   if (ParseSettingsFile(settingsFileName, buf)) {\r
2183     settingsFileName = strdup(buf);\r
2184   }\r
2185 \r
2186   /* Parse command line */\r
2187   ParseArgs(StringGet, &lpCmdLine);\r
2188 \r
2189   /* [HGM] make sure board size is acceptable */\r
2190   if(appData.NrFiles > BOARD_SIZE ||\r
2191      appData.NrRanks > BOARD_SIZE   )\r
2192       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2193 \r
2194   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2195    * with options from the command line, we now make an even higher priority\r
2196    * overrule by WB options attached to the engine command line. This so that\r
2197    * tournament managers can use WB options (such as /timeOdds) that follow\r
2198    * the engines.\r
2199    */\r
2200   if(appData.firstChessProgram != NULL) {\r
2201       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2202       static char *f = "first";\r
2203       char buf[MSG_SIZ], *q = buf;\r
2204       if(p != NULL) { // engine command line contains WinBoard options\r
2205           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2206           ParseArgs(StringGet, &q);\r
2207           p[-1] = 0; // cut them offengine command line\r
2208       }\r
2209   }\r
2210   // now do same for second chess program\r
2211   if(appData.secondChessProgram != NULL) {\r
2212       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2213       static char *s = "second";\r
2214       char buf[MSG_SIZ], *q = buf;\r
2215       if(p != NULL) { // engine command line contains WinBoard options\r
2216           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2217           ParseArgs(StringGet, &q);\r
2218           p[-1] = 0; // cut them offengine command line\r
2219       }\r
2220   }\r
2221 \r
2222 \r
2223   /* Propagate options that affect others */\r
2224   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2225   if (appData.icsActive || appData.noChessProgram) {\r
2226      chessProgram = FALSE;  /* not local chess program mode */\r
2227   }\r
2228 \r
2229   /* Open startup dialog if needed */\r
2230   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2231       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2232       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2233                         *appData.secondChessProgram == NULLCHAR))) {\r
2234     FARPROC lpProc;\r
2235     \r
2236     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2237     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2238     FreeProcInstance(lpProc);\r
2239   }\r
2240 \r
2241   /* Make sure save files land in the right (?) directory */\r
2242   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2243     appData.saveGameFile = strdup(buf);\r
2244   }\r
2245   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2246     appData.savePositionFile = strdup(buf);\r
2247   }\r
2248 \r
2249   /* Finish initialization for fonts and sounds */\r
2250   for (i=0; i<NUM_FONTS; i++) {\r
2251     for (j=0; j<NUM_SIZES; j++) {\r
2252       CreateFontInMF(font[j][i]);\r
2253     }\r
2254   }\r
2255   /* xboard, and older WinBoards, controlled the move sound with the\r
2256      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2257      always turn the option on (so that the backend will call us),\r
2258      then let the user turn the sound off by setting it to silence if\r
2259      desired.  To accommodate old winboard.ini files saved by old\r
2260      versions of WinBoard, we also turn off the sound if the option\r
2261      was initially set to false. */\r
2262   if (!appData.ringBellAfterMoves) {\r
2263     sounds[(int)SoundMove].name = strdup("");\r
2264     appData.ringBellAfterMoves = TRUE;\r
2265   }\r
2266   GetCurrentDirectory(MSG_SIZ, currDir);\r
2267   SetCurrentDirectory(installDir);\r
2268   LoadAllSounds();\r
2269   SetCurrentDirectory(currDir);\r
2270 \r
2271   p = icsTextMenuString;\r
2272   if (p[0] == '@') {\r
2273     FILE* f = fopen(p + 1, "r");\r
2274     if (f == NULL) {\r
2275       DisplayFatalError(p + 1, errno, 2);\r
2276       return;\r
2277     }\r
2278     i = fread(buf, 1, sizeof(buf)-1, f);\r
2279     fclose(f);\r
2280     buf[i] = NULLCHAR;\r
2281     p = buf;\r
2282   }\r
2283   ParseIcsTextMenu(strdup(p));\r
2284 }\r
2285 \r
2286 \r
2287 VOID\r
2288 InitMenuChecks()\r
2289 {\r
2290   HMENU hmenu = GetMenu(hwndMain);\r
2291 \r
2292   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2293                         MF_BYCOMMAND|((appData.icsActive &&\r
2294                                        *appData.icsCommPort != NULLCHAR) ?\r
2295                                       MF_ENABLED : MF_GRAYED));\r
2296   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2297                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2298                                      MF_CHECKED : MF_UNCHECKED));\r
2299 }\r
2300 \r
2301 \r
2302 VOID\r
2303 SaveSettings(char* name)\r
2304 {\r
2305   FILE *f;\r
2306   ArgDescriptor *ad;\r
2307   WINDOWPLACEMENT wp;\r
2308   char dir[MSG_SIZ];\r
2309 \r
2310   if (!hwndMain) return;\r
2311 \r
2312   GetCurrentDirectory(MSG_SIZ, dir);\r
2313   SetCurrentDirectory(installDir);\r
2314   f = fopen(name, "w");\r
2315   SetCurrentDirectory(dir);\r
2316   if (f == NULL) {\r
2317     DisplayError(name, errno);\r
2318     return;\r
2319   }\r
2320   fprintf(f, ";\n");\r
2321   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2322   fprintf(f, ";\n");\r
2323   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2324   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2325   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2326   fprintf(f, ";\n");\r
2327 \r
2328   wp.length = sizeof(WINDOWPLACEMENT);\r
2329   GetWindowPlacement(hwndMain, &wp);\r
2330   boardX = wp.rcNormalPosition.left;\r
2331   boardY = wp.rcNormalPosition.top;\r
2332 \r
2333   if (hwndConsole) {\r
2334     GetWindowPlacement(hwndConsole, &wp);\r
2335     wpConsole.x = wp.rcNormalPosition.left;\r
2336     wpConsole.y = wp.rcNormalPosition.top;\r
2337     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2338     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2339   }\r
2340 \r
2341   if (analysisDialog) {\r
2342     GetWindowPlacement(analysisDialog, &wp);\r
2343     analysisX = wp.rcNormalPosition.left;\r
2344     analysisY = wp.rcNormalPosition.top;\r
2345     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2346     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2347   }\r
2348 \r
2349   if (commentDialog) {\r
2350     GetWindowPlacement(commentDialog, &wp);\r
2351     commentX = wp.rcNormalPosition.left;\r
2352     commentY = wp.rcNormalPosition.top;\r
2353     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2354     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2355   }\r
2356 \r
2357   if (editTagsDialog) {\r
2358     GetWindowPlacement(editTagsDialog, &wp);\r
2359     editTagsX = wp.rcNormalPosition.left;\r
2360     editTagsY = wp.rcNormalPosition.top;\r
2361     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2362     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2363   }\r
2364 \r
2365   if (gameListDialog) {\r
2366     GetWindowPlacement(gameListDialog, &wp);\r
2367     wpGameList.x = wp.rcNormalPosition.left;\r
2368     wpGameList.y = wp.rcNormalPosition.top;\r
2369     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2370     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2371   }\r
2372 \r
2373   /* [AS] Move history */\r
2374   wpMoveHistory.visible = MoveHistoryIsUp();\r
2375   \r
2376   if( moveHistoryDialog ) {\r
2377     GetWindowPlacement(moveHistoryDialog, &wp);\r
2378     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2379     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2380     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2381     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2382   }\r
2383 \r
2384   /* [AS] Eval graph */\r
2385   wpEvalGraph.visible = EvalGraphIsUp();\r
2386 \r
2387   if( evalGraphDialog ) {\r
2388     GetWindowPlacement(evalGraphDialog, &wp);\r
2389     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2390     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2391     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2392     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2393   }\r
2394 \r
2395   /* [AS] Engine output */\r
2396   wpEngineOutput.visible = EngineOutputIsUp();\r
2397 \r
2398   if( engineOutputDialog ) {\r
2399     GetWindowPlacement(engineOutputDialog, &wp);\r
2400     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2401     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2402     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2403     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2404   }\r
2405 \r
2406   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2407     if (!ad->save) continue;\r
2408     switch (ad->argType) {\r
2409     case ArgString:\r
2410       {\r
2411         char *p = *(char **)ad->argLoc;\r
2412         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2413           /* Quote multiline values or \-containing values\r
2414              with { } if possible */\r
2415           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2416         } else {\r
2417           /* Else quote with " " */\r
2418           fprintf(f, "/%s=\"", ad->argName);\r
2419           while (*p) {\r
2420             if (*p == '\n') fprintf(f, "\n");\r
2421             else if (*p == '\r') fprintf(f, "\\r");\r
2422             else if (*p == '\t') fprintf(f, "\\t");\r
2423             else if (*p == '\b') fprintf(f, "\\b");\r
2424             else if (*p == '\f') fprintf(f, "\\f");\r
2425             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2426             else if (*p == '\"') fprintf(f, "\\\"");\r
2427             else if (*p == '\\') fprintf(f, "\\\\");\r
2428             else putc(*p, f);\r
2429             p++;\r
2430           }\r
2431           fprintf(f, "\"\n");\r
2432         }\r
2433       }\r
2434       break;\r
2435     case ArgInt:\r
2436     case ArgZ:\r
2437       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2438       break;\r
2439     case ArgX:\r
2440       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2441       break;\r
2442     case ArgY:\r
2443       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2444       break;\r
2445     case ArgFloat:\r
2446       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2447       break;\r
2448     case ArgBoolean:\r
2449       fprintf(f, "/%s=%s\n", ad->argName, \r
2450         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2451       break;\r
2452     case ArgTrue:\r
2453       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2454       break;\r
2455     case ArgFalse:\r
2456       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2457       break;\r
2458     case ArgColor:\r
2459       {\r
2460         COLORREF color = *(COLORREF *)ad->argLoc;\r
2461         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2462           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2463       }\r
2464       break;\r
2465     case ArgAttribs:\r
2466       {\r
2467         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2468         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2469           (ta->effects & CFE_BOLD) ? "b" : "",\r
2470           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2471           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2472           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2473           (ta->effects) ? " " : "",\r
2474           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2475       }\r
2476       break;\r
2477     case ArgFilename:\r
2478       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2479         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2480       } else {\r
2481         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2482       }\r
2483       break;\r
2484     case ArgBoardSize:\r
2485       fprintf(f, "/%s=%s\n", ad->argName,\r
2486               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2487       break;\r
2488     case ArgFont:\r
2489       {\r
2490         int bs;\r
2491         for (bs=0; bs<NUM_SIZES; bs++) {\r
2492           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2493           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2494           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2495             ad->argName, mfp->faceName, mfp->pointSize,\r
2496             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2497             mfp->bold ? "b" : "",\r
2498             mfp->italic ? "i" : "",\r
2499             mfp->underline ? "u" : "",\r
2500             mfp->strikeout ? "s" : "");\r
2501         }\r
2502       }\r
2503       break;\r
2504     case ArgCommSettings:\r
2505       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2506     case ArgNone:\r
2507     case ArgSettingsFilename: ;\r
2508     }\r
2509   }\r
2510   fclose(f);\r
2511 }\r
2512 \r
2513 \r
2514 \r
2515 /*---------------------------------------------------------------------------*\\r
2516  *\r
2517  * GDI board drawing routines\r
2518  *\r
2519 \*---------------------------------------------------------------------------*/\r
2520 \r
2521 /* [AS] Draw square using background texture */\r
2522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2523 {\r
2524     XFORM   x;\r
2525 \r
2526     if( mode == 0 ) {\r
2527         return; /* Should never happen! */\r
2528     }\r
2529 \r
2530     SetGraphicsMode( dst, GM_ADVANCED );\r
2531 \r
2532     switch( mode ) {\r
2533     case 1:\r
2534         /* Identity */\r
2535         break;\r
2536     case 2:\r
2537         /* X reflection */\r
2538         x.eM11 = -1.0;\r
2539         x.eM12 = 0;\r
2540         x.eM21 = 0;\r
2541         x.eM22 = 1.0;\r
2542         x.eDx = (FLOAT) dw + dx - 1;\r
2543         x.eDy = 0;\r
2544         dx = 0;\r
2545         SetWorldTransform( dst, &x );\r
2546         break;\r
2547     case 3:\r
2548         /* Y reflection */\r
2549         x.eM11 = 1.0;\r
2550         x.eM12 = 0;\r
2551         x.eM21 = 0;\r
2552         x.eM22 = -1.0;\r
2553         x.eDx = 0;\r
2554         x.eDy = (FLOAT) dh + dy - 1;\r
2555         dy = 0;\r
2556         SetWorldTransform( dst, &x );\r
2557         break;\r
2558     case 4:\r
2559         /* X/Y flip */\r
2560         x.eM11 = 0;\r
2561         x.eM12 = 1.0;\r
2562         x.eM21 = 1.0;\r
2563         x.eM22 = 0;\r
2564         x.eDx = (FLOAT) dx;\r
2565         x.eDy = (FLOAT) dy;\r
2566         dx = 0;\r
2567         dy = 0;\r
2568         SetWorldTransform( dst, &x );\r
2569         break;\r
2570     }\r
2571 \r
2572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2573 \r
2574     x.eM11 = 1.0;\r
2575     x.eM12 = 0;\r
2576     x.eM21 = 0;\r
2577     x.eM22 = 1.0;\r
2578     x.eDx = 0;\r
2579     x.eDy = 0;\r
2580     SetWorldTransform( dst, &x );\r
2581 \r
2582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2583 }\r
2584 \r
2585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2586 enum {\r
2587     PM_WP = (int) WhitePawn, \r
2588     PM_WN = (int) WhiteKnight, \r
2589     PM_WB = (int) WhiteBishop, \r
2590     PM_WR = (int) WhiteRook, \r
2591     PM_WQ = (int) WhiteQueen, \r
2592     PM_WF = (int) WhiteFerz, \r
2593     PM_WW = (int) WhiteWazir, \r
2594     PM_WE = (int) WhiteAlfil, \r
2595     PM_WM = (int) WhiteMan, \r
2596     PM_WO = (int) WhiteCannon, \r
2597     PM_WU = (int) WhiteUnicorn, \r
2598     PM_WH = (int) WhiteNightrider, \r
2599     PM_WA = (int) WhiteAngel, \r
2600     PM_WC = (int) WhiteMarshall, \r
2601     PM_WAB = (int) WhiteCardinal, \r
2602     PM_WD = (int) WhiteDragon, \r
2603     PM_WL = (int) WhiteLance, \r
2604     PM_WS = (int) WhiteCobra, \r
2605     PM_WV = (int) WhiteFalcon, \r
2606     PM_WSG = (int) WhiteSilver, \r
2607     PM_WG = (int) WhiteGrasshopper, \r
2608     PM_WK = (int) WhiteKing,\r
2609     PM_BP = (int) BlackPawn, \r
2610     PM_BN = (int) BlackKnight, \r
2611     PM_BB = (int) BlackBishop, \r
2612     PM_BR = (int) BlackRook, \r
2613     PM_BQ = (int) BlackQueen, \r
2614     PM_BF = (int) BlackFerz, \r
2615     PM_BW = (int) BlackWazir, \r
2616     PM_BE = (int) BlackAlfil, \r
2617     PM_BM = (int) BlackMan,\r
2618     PM_BO = (int) BlackCannon, \r
2619     PM_BU = (int) BlackUnicorn, \r
2620     PM_BH = (int) BlackNightrider, \r
2621     PM_BA = (int) BlackAngel, \r
2622     PM_BC = (int) BlackMarshall, \r
2623     PM_BG = (int) BlackGrasshopper, \r
2624     PM_BAB = (int) BlackCardinal,\r
2625     PM_BD = (int) BlackDragon,\r
2626     PM_BL = (int) BlackLance,\r
2627     PM_BS = (int) BlackCobra,\r
2628     PM_BV = (int) BlackFalcon,\r
2629     PM_BSG = (int) BlackSilver,\r
2630     PM_BK = (int) BlackKing\r
2631 };\r
2632 \r
2633 static HFONT hPieceFont = NULL;\r
2634 static HBITMAP hPieceMask[(int) EmptySquare];\r
2635 static HBITMAP hPieceFace[(int) EmptySquare];\r
2636 static int fontBitmapSquareSize = 0;\r
2637 static char pieceToFontChar[(int) EmptySquare] =\r
2638                               { 'p', 'n', 'b', 'r', 'q', \r
2639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2640                       'k', 'o', 'm', 'v', 't', 'w', \r
2641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2642                                                               'l' };\r
2643 \r
2644 extern BOOL SetCharTable( char *table, const char * map );\r
2645 /* [HGM] moved to backend.c */\r
2646 \r
2647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2648 {\r
2649     HBRUSH hbrush;\r
2650     BYTE r1 = GetRValue( color );\r
2651     BYTE g1 = GetGValue( color );\r
2652     BYTE b1 = GetBValue( color );\r
2653     BYTE r2 = r1 / 2;\r
2654     BYTE g2 = g1 / 2;\r
2655     BYTE b2 = b1 / 2;\r
2656     RECT rc;\r
2657 \r
2658     /* Create a uniform background first */\r
2659     hbrush = CreateSolidBrush( color );\r
2660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2661     FillRect( hdc, &rc, hbrush );\r
2662     DeleteObject( hbrush );\r
2663     \r
2664     if( mode == 1 ) {\r
2665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2666         int steps = squareSize / 2;\r
2667         int i;\r
2668 \r
2669         for( i=0; i<steps; i++ ) {\r
2670             BYTE r = r1 - (r1-r2) * i / steps;\r
2671             BYTE g = g1 - (g1-g2) * i / steps;\r
2672             BYTE b = b1 - (b1-b2) * i / steps;\r
2673 \r
2674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2676             FillRect( hdc, &rc, hbrush );\r
2677             DeleteObject(hbrush);\r
2678         }\r
2679     }\r
2680     else if( mode == 2 ) {\r
2681         /* Diagonal gradient, good more or less for every piece */\r
2682         POINT triangle[3];\r
2683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2684         HBRUSH hbrush_old;\r
2685         int steps = squareSize;\r
2686         int i;\r
2687 \r
2688         triangle[0].x = squareSize - steps;\r
2689         triangle[0].y = squareSize;\r
2690         triangle[1].x = squareSize;\r
2691         triangle[1].y = squareSize;\r
2692         triangle[2].x = squareSize;\r
2693         triangle[2].y = squareSize - steps;\r
2694 \r
2695         for( i=0; i<steps; i++ ) {\r
2696             BYTE r = r1 - (r1-r2) * i / steps;\r
2697             BYTE g = g1 - (g1-g2) * i / steps;\r
2698             BYTE b = b1 - (b1-b2) * i / steps;\r
2699 \r
2700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2701             hbrush_old = SelectObject( hdc, hbrush );\r
2702             Polygon( hdc, triangle, 3 );\r
2703             SelectObject( hdc, hbrush_old );\r
2704             DeleteObject(hbrush);\r
2705             triangle[0].x++;\r
2706             triangle[2].y++;\r
2707         }\r
2708 \r
2709         SelectObject( hdc, hpen );\r
2710     }\r
2711 }\r
2712 \r
2713 /*\r
2714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2715     seems to work ok. The main problem here is to find the "inside" of a chess\r
2716     piece: follow the steps as explained below.\r
2717 */\r
2718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2719 {\r
2720     HBITMAP hbm;\r
2721     HBITMAP hbm_old;\r
2722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2723     RECT rc;\r
2724     SIZE sz;\r
2725     POINT pt;\r
2726     int backColor = whitePieceColor; \r
2727     int foreColor = blackPieceColor;\r
2728     \r
2729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2730         backColor = appData.fontBackColorWhite;\r
2731         foreColor = appData.fontForeColorWhite;\r
2732     }\r
2733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2734         backColor = appData.fontBackColorBlack;\r
2735         foreColor = appData.fontForeColorBlack;\r
2736     }\r
2737 \r
2738     /* Mask */\r
2739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2740 \r
2741     hbm_old = SelectObject( hdc, hbm );\r
2742 \r
2743     rc.left = 0;\r
2744     rc.top = 0;\r
2745     rc.right = squareSize;\r
2746     rc.bottom = squareSize;\r
2747 \r
2748     /* Step 1: background is now black */\r
2749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2750 \r
2751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2752 \r
2753     pt.x = (squareSize - sz.cx) / 2;\r
2754     pt.y = (squareSize - sz.cy) / 2;\r
2755 \r
2756     SetBkMode( hdc, TRANSPARENT );\r
2757     SetTextColor( hdc, chroma );\r
2758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2760 \r
2761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2762     /* Step 3: the area outside the piece is filled with white */\r
2763 //    FloodFill( hdc, 0, 0, chroma );\r
2764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2769     /* \r
2770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2771         but if the start point is not inside the piece we're lost!\r
2772         There should be a better way to do this... if we could create a region or path\r
2773         from the fill operation we would be fine for example.\r
2774     */\r
2775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2777 \r
2778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2781 \r
2782         SelectObject( dc2, bm2 );\r
2783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2788 \r
2789         DeleteDC( dc2 );\r
2790         DeleteObject( bm2 );\r
2791     }\r
2792 \r
2793     SetTextColor( hdc, 0 );\r
2794     /* \r
2795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2796         draw the piece again in black for safety.\r
2797     */\r
2798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2799 \r
2800     SelectObject( hdc, hbm_old );\r
2801 \r
2802     if( hPieceMask[index] != NULL ) {\r
2803         DeleteObject( hPieceMask[index] );\r
2804     }\r
2805 \r
2806     hPieceMask[index] = hbm;\r
2807 \r
2808     /* Face */\r
2809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2810 \r
2811     SelectObject( hdc, hbm );\r
2812 \r
2813     {\r
2814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2817 \r
2818         SelectObject( dc1, hPieceMask[index] );\r
2819         SelectObject( dc2, bm2 );\r
2820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2822         \r
2823         /* \r
2824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2825             the piece background and deletes (makes transparent) the rest.\r
2826             Thanks to that mask, we are free to paint the background with the greates\r
2827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2828             We use this, to make gradients and give the pieces a "roundish" look.\r
2829         */\r
2830         SetPieceBackground( hdc, backColor, 2 );\r
2831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2832 \r
2833         DeleteDC( dc2 );\r
2834         DeleteDC( dc1 );\r
2835         DeleteObject( bm2 );\r
2836     }\r
2837 \r
2838     SetTextColor( hdc, foreColor );\r
2839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2840 \r
2841     SelectObject( hdc, hbm_old );\r
2842 \r
2843     if( hPieceFace[index] != NULL ) {\r
2844         DeleteObject( hPieceFace[index] );\r
2845     }\r
2846 \r
2847     hPieceFace[index] = hbm;\r
2848 }\r
2849 \r
2850 static int TranslatePieceToFontPiece( int piece )\r
2851 {\r
2852     switch( piece ) {\r
2853     case BlackPawn:\r
2854         return PM_BP;\r
2855     case BlackKnight:\r
2856         return PM_BN;\r
2857     case BlackBishop:\r
2858         return PM_BB;\r
2859     case BlackRook:\r
2860         return PM_BR;\r
2861     case BlackQueen:\r
2862         return PM_BQ;\r
2863     case BlackKing:\r
2864         return PM_BK;\r
2865     case WhitePawn:\r
2866         return PM_WP;\r
2867     case WhiteKnight:\r
2868         return PM_WN;\r
2869     case WhiteBishop:\r
2870         return PM_WB;\r
2871     case WhiteRook:\r
2872         return PM_WR;\r
2873     case WhiteQueen:\r
2874         return PM_WQ;\r
2875     case WhiteKing:\r
2876         return PM_WK;\r
2877 \r
2878     case BlackAngel:\r
2879         return PM_BA;\r
2880     case BlackMarshall:\r
2881         return PM_BC;\r
2882     case BlackFerz:\r
2883         return PM_BF;\r
2884     case BlackNightrider:\r
2885         return PM_BH;\r
2886     case BlackAlfil:\r
2887         return PM_BE;\r
2888     case BlackWazir:\r
2889         return PM_BW;\r
2890     case BlackUnicorn:\r
2891         return PM_BU;\r
2892     case BlackCannon:\r
2893         return PM_BO;\r
2894     case BlackGrasshopper:\r
2895         return PM_BG;\r
2896     case BlackMan:\r
2897         return PM_BM;\r
2898     case BlackSilver:\r
2899         return PM_BSG;\r
2900     case BlackLance:\r
2901         return PM_BL;\r
2902     case BlackFalcon:\r
2903         return PM_BV;\r
2904     case BlackCobra:\r
2905         return PM_BS;\r
2906     case BlackCardinal:\r
2907         return PM_BAB;\r
2908     case BlackDragon:\r
2909         return PM_BD;\r
2910 \r
2911     case WhiteAngel:\r
2912         return PM_WA;\r
2913     case WhiteMarshall:\r
2914         return PM_WC;\r
2915     case WhiteFerz:\r
2916         return PM_WF;\r
2917     case WhiteNightrider:\r
2918         return PM_WH;\r
2919     case WhiteAlfil:\r
2920         return PM_WE;\r
2921     case WhiteWazir:\r
2922         return PM_WW;\r
2923     case WhiteUnicorn:\r
2924         return PM_WU;\r
2925     case WhiteCannon:\r
2926         return PM_WO;\r
2927     case WhiteGrasshopper:\r
2928         return PM_WG;\r
2929     case WhiteMan:\r
2930         return PM_WM;\r
2931     case WhiteSilver:\r
2932         return PM_WSG;\r
2933     case WhiteLance:\r
2934         return PM_WL;\r
2935     case WhiteFalcon:\r
2936         return PM_WV;\r
2937     case WhiteCobra:\r
2938         return PM_WS;\r
2939     case WhiteCardinal:\r
2940         return PM_WAB;\r
2941     case WhiteDragon:\r
2942         return PM_WD;\r
2943     }\r
2944 \r
2945     return 0;\r
2946 }\r
2947 \r
2948 void CreatePiecesFromFont()\r
2949 {\r
2950     LOGFONT lf;\r
2951     HDC hdc_window = NULL;\r
2952     HDC hdc = NULL;\r
2953     HFONT hfont_old;\r
2954     int fontHeight;\r
2955     int i;\r
2956 \r
2957     if( fontBitmapSquareSize < 0 ) {\r
2958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2959         return;\r
2960     }\r
2961 \r
2962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2963         fontBitmapSquareSize = -1;\r
2964         return;\r
2965     }\r
2966 \r
2967     if( fontBitmapSquareSize != squareSize ) {\r
2968         hdc_window = GetDC( hwndMain );\r
2969         hdc = CreateCompatibleDC( hdc_window );\r
2970 \r
2971         if( hPieceFont != NULL ) {\r
2972             DeleteObject( hPieceFont );\r
2973         }\r
2974         else {\r
2975             for( i=0; i<=(int)BlackKing; i++ ) {\r
2976                 hPieceMask[i] = NULL;\r
2977                 hPieceFace[i] = NULL;\r
2978             }\r
2979         }\r
2980 \r
2981         fontHeight = 75;\r
2982 \r
2983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2984             fontHeight = appData.fontPieceSize;\r
2985         }\r
2986 \r
2987         fontHeight = (fontHeight * squareSize) / 100;\r
2988 \r
2989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2990         lf.lfWidth = 0;\r
2991         lf.lfEscapement = 0;\r
2992         lf.lfOrientation = 0;\r
2993         lf.lfWeight = FW_NORMAL;\r
2994         lf.lfItalic = 0;\r
2995         lf.lfUnderline = 0;\r
2996         lf.lfStrikeOut = 0;\r
2997         lf.lfCharSet = DEFAULT_CHARSET;\r
2998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
3000         lf.lfQuality = PROOF_QUALITY;\r
3001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3004 \r
3005         hPieceFont = CreateFontIndirect( &lf );\r
3006 \r
3007         if( hPieceFont == NULL ) {\r
3008             fontBitmapSquareSize = -2;\r
3009         }\r
3010         else {\r
3011             /* Setup font-to-piece character table */\r
3012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3013                 /* No (or wrong) global settings, try to detect the font */\r
3014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3015                     /* Alpha */\r
3016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3017                 }\r
3018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3019                     /* DiagramTT* family */\r
3020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3021                 }\r
3022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3023                     /* Fairy symbols */\r
3024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3025                 }\r
3026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3027                     /* Good Companion (Some characters get warped as literal :-( */\r
3028                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3030                     SetCharTable(pieceToFontChar, s);\r
3031                 }\r
3032                 else {\r
3033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3035                 }\r
3036             }\r
3037 \r
3038             /* Create bitmaps */\r
3039             hfont_old = SelectObject( hdc, hPieceFont );\r
3040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3043 \r
3044             SelectObject( hdc, hfont_old );\r
3045 \r
3046             fontBitmapSquareSize = squareSize;\r
3047         }\r
3048     }\r
3049 \r
3050     if( hdc != NULL ) {\r
3051         DeleteDC( hdc );\r
3052     }\r
3053 \r
3054     if( hdc_window != NULL ) {\r
3055         ReleaseDC( hwndMain, hdc_window );\r
3056     }\r
3057 }\r
3058 \r
3059 HBITMAP\r
3060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3061 {\r
3062   char name[128];\r
3063 \r
3064   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3065   if (gameInfo.event &&\r
3066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3067       strcmp(name, "k80s") == 0) {\r
3068     strcpy(name, "tim");\r
3069   }\r
3070   return LoadBitmap(hinst, name);\r
3071 }\r
3072 \r
3073 \r
3074 /* Insert a color into the program's logical palette\r
3075    structure.  This code assumes the given color is\r
3076    the result of the RGB or PALETTERGB macro, and it\r
3077    knows how those macros work (which is documented).\r
3078 */\r
3079 VOID\r
3080 InsertInPalette(COLORREF color)\r
3081 {\r
3082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3083 \r
3084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3085     DisplayFatalError("Too many colors", 0, 1);\r
3086     pLogPal->palNumEntries--;\r
3087     return;\r
3088   }\r
3089 \r
3090   pe->peFlags = (char) 0;\r
3091   pe->peRed = (char) (0xFF & color);\r
3092   pe->peGreen = (char) (0xFF & (color >> 8));\r
3093   pe->peBlue = (char) (0xFF & (color >> 16));\r
3094   return;\r
3095 }\r
3096 \r
3097 \r
3098 VOID\r
3099 InitDrawingColors()\r
3100 {\r
3101   if (pLogPal == NULL) {\r
3102     /* Allocate enough memory for a logical palette with\r
3103      * PALETTESIZE entries and set the size and version fields\r
3104      * of the logical palette structure.\r
3105      */\r
3106     pLogPal = (NPLOGPALETTE)\r
3107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3109     pLogPal->palVersion    = 0x300;\r
3110   }\r
3111   pLogPal->palNumEntries = 0;\r
3112 \r
3113   InsertInPalette(lightSquareColor);\r
3114   InsertInPalette(darkSquareColor);\r
3115   InsertInPalette(whitePieceColor);\r
3116   InsertInPalette(blackPieceColor);\r
3117   InsertInPalette(highlightSquareColor);\r
3118   InsertInPalette(premoveHighlightColor);\r
3119 \r
3120   /*  create a logical color palette according the information\r
3121    *  in the LOGPALETTE structure.\r
3122    */\r
3123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3124 \r
3125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3132   /* [AS] Force rendering of the font-based pieces */\r
3133   if( fontBitmapSquareSize > 0 ) {\r
3134     fontBitmapSquareSize = 0;\r
3135   }\r
3136 }\r
3137 \r
3138 \r
3139 int\r
3140 BoardWidth(int boardSize, int n)\r
3141 { /* [HGM] argument n added to allow different width and height */\r
3142   int lineGap = sizeInfo[boardSize].lineGap;\r
3143 \r
3144   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3145       lineGap = appData.overrideLineGap;\r
3146   }\r
3147 \r
3148   return (n + 1) * lineGap +\r
3149           n * sizeInfo[boardSize].squareSize;\r
3150 }\r
3151 \r
3152 /* Respond to board resize by dragging edge */\r
3153 VOID\r
3154 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3155 {\r
3156   BoardSize newSize = NUM_SIZES - 1;\r
3157   static int recurse = 0;\r
3158   if (IsIconic(hwndMain)) return;\r
3159   if (recurse > 0) return;\r
3160   recurse++;\r
3161   while (newSize > 0) {\r
3162         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3163         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3164            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3165     newSize--;\r
3166   } \r
3167   boardSize = newSize;\r
3168   InitDrawingSizes(boardSize, flags);\r
3169   recurse--;\r
3170 }\r
3171 \r
3172 \r
3173 \r
3174 VOID\r
3175 InitDrawingSizes(BoardSize boardSize, int flags)\r
3176 {\r
3177   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3178   ChessSquare piece;\r
3179   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3180   HDC hdc;\r
3181   SIZE clockSize, messageSize;\r
3182   HFONT oldFont;\r
3183   char buf[MSG_SIZ];\r
3184   char *str;\r
3185   HMENU hmenu = GetMenu(hwndMain);\r
3186   RECT crect, wrect, oldRect;\r
3187   int offby;\r
3188   LOGBRUSH logbrush;\r
3189 \r
3190   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3191   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3192 \r
3193   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3194   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3195 \r
3196   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3197   oldRect.top = boardY;\r
3198   oldRect.right = boardX + winWidth;\r
3199   oldRect.bottom = boardY + winHeight;\r
3200 \r
3201   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3202   smallLayout = sizeInfo[boardSize].smallLayout;\r
3203   squareSize = sizeInfo[boardSize].squareSize;\r
3204   lineGap = sizeInfo[boardSize].lineGap;\r
3205   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3206 \r
3207   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3208       lineGap = appData.overrideLineGap;\r
3209   }\r
3210 \r
3211   if (tinyLayout != oldTinyLayout) {\r
3212     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3213     if (tinyLayout) {\r
3214       style &= ~WS_SYSMENU;\r
3215       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3216                  "&Minimize\tCtrl+F4");\r
3217     } else {\r
3218       style |= WS_SYSMENU;\r
3219       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3220     }\r
3221     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3222 \r
3223     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3224       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3225         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3226     }\r
3227     DrawMenuBar(hwndMain);\r
3228   }\r
3229 \r
3230   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3231   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3232 \r
3233   /* Get text area sizes */\r
3234   hdc = GetDC(hwndMain);\r
3235   if (appData.clockMode) {\r
3236     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3237   } else {\r
3238     sprintf(buf, "White");\r
3239   }\r
3240   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3241   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3242   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3243   str = "We only care about the height here";\r
3244   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3245   SelectObject(hdc, oldFont);\r
3246   ReleaseDC(hwndMain, hdc);\r
3247 \r
3248   /* Compute where everything goes */\r
3249   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3250         /* [HGM] logo: if either logo is on, reserve space for it */\r
3251         logoHeight =  2*clockSize.cy;\r
3252         leftLogoRect.left   = OUTER_MARGIN;\r
3253         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3254         leftLogoRect.top    = OUTER_MARGIN;\r
3255         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3256 \r
3257         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3258         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3259         rightLogoRect.top    = OUTER_MARGIN;\r
3260         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3261 \r
3262 \r
3263     whiteRect.left = leftLogoRect.right;\r
3264     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3265     whiteRect.top = OUTER_MARGIN;\r
3266     whiteRect.bottom = whiteRect.top + logoHeight;\r
3267 \r
3268     blackRect.right = rightLogoRect.left;\r
3269     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3270     blackRect.top = whiteRect.top;\r
3271     blackRect.bottom = whiteRect.bottom;\r
3272   } else {\r
3273     whiteRect.left = OUTER_MARGIN;\r
3274     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3275     whiteRect.top = OUTER_MARGIN;\r
3276     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3277 \r
3278     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3279     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3280     blackRect.top = whiteRect.top;\r
3281     blackRect.bottom = whiteRect.bottom;\r
3282   }\r
3283 \r
3284   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3285   if (appData.showButtonBar) {\r
3286     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3287       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3288   } else {\r
3289     messageRect.right = OUTER_MARGIN + boardWidth;\r
3290   }\r
3291   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3292   messageRect.bottom = messageRect.top + messageSize.cy;\r
3293 \r
3294   boardRect.left = OUTER_MARGIN;\r
3295   boardRect.right = boardRect.left + boardWidth;\r
3296   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3297   boardRect.bottom = boardRect.top + boardHeight;\r
3298 \r
3299   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3300   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3301   oldBoardSize = boardSize;\r
3302   oldTinyLayout = tinyLayout;\r
3303   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3304   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3305     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3306   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3307   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3308   winHeight = winH; //       without disturbing window attachments\r
3309   GetWindowRect(hwndMain, &wrect);\r
3310   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3311                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3312 \r
3313   // [HGM] placement: let attached windows follow size change.\r
3314   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3315   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3316   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3317   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3318   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3319 \r
3320   /* compensate if menu bar wrapped */\r
3321   GetClientRect(hwndMain, &crect);\r
3322   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3323   winHeight += offby;\r
3324   switch (flags) {\r
3325   case WMSZ_TOPLEFT:\r
3326     SetWindowPos(hwndMain, NULL, \r
3327                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3328                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3329     break;\r
3330 \r
3331   case WMSZ_TOPRIGHT:\r
3332   case WMSZ_TOP:\r
3333     SetWindowPos(hwndMain, NULL, \r
3334                  wrect.left, wrect.bottom - winHeight, \r
3335                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3336     break;\r
3337 \r
3338   case WMSZ_BOTTOMLEFT:\r
3339   case WMSZ_LEFT:\r
3340     SetWindowPos(hwndMain, NULL, \r
3341                  wrect.right - winWidth, wrect.top, \r
3342                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3343     break;\r
3344 \r
3345   case WMSZ_BOTTOMRIGHT:\r
3346   case WMSZ_BOTTOM:\r
3347   case WMSZ_RIGHT:\r
3348   default:\r
3349     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3350                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3351     break;\r
3352   }\r
3353 \r
3354   hwndPause = NULL;\r
3355   for (i = 0; i < N_BUTTONS; i++) {\r
3356     if (buttonDesc[i].hwnd != NULL) {\r
3357       DestroyWindow(buttonDesc[i].hwnd);\r
3358       buttonDesc[i].hwnd = NULL;\r
3359     }\r
3360     if (appData.showButtonBar) {\r
3361       buttonDesc[i].hwnd =\r
3362         CreateWindow("BUTTON", buttonDesc[i].label,\r
3363                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3364                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3365                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3366                      (HMENU) buttonDesc[i].id,\r
3367                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3368       if (tinyLayout) {\r
3369         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3370                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3371                     MAKELPARAM(FALSE, 0));\r
3372       }\r
3373       if (buttonDesc[i].id == IDM_Pause)\r
3374         hwndPause = buttonDesc[i].hwnd;\r
3375       buttonDesc[i].wndproc = (WNDPROC)\r
3376         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3377     }\r
3378   }\r
3379   if (gridPen != NULL) DeleteObject(gridPen);\r
3380   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3381   if (premovePen != NULL) DeleteObject(premovePen);\r
3382   if (lineGap != 0) {\r
3383     logbrush.lbStyle = BS_SOLID;\r
3384     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3385     gridPen =\r
3386       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3387                    lineGap, &logbrush, 0, NULL);\r
3388     logbrush.lbColor = highlightSquareColor;\r
3389     highlightPen =\r
3390       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3391                    lineGap, &logbrush, 0, NULL);\r
3392 \r
3393     logbrush.lbColor = premoveHighlightColor; \r
3394     premovePen =\r
3395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3396                    lineGap, &logbrush, 0, NULL);\r
3397 \r
3398     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3399     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3400       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3401       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3402         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3403       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3404         BOARD_WIDTH * (squareSize + lineGap);\r
3405       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3406     }\r
3407     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3408       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3409       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3410         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3411         lineGap / 2 + (i * (squareSize + lineGap));\r
3412       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3413         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3414       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3415     }\r
3416   }\r
3417 \r
3418   /* [HGM] Licensing requirement */\r
3419 #ifdef GOTHIC\r
3420   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3421 #endif\r
3422 #ifdef FALCON\r
3423   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3424 #endif\r
3425   GothicPopUp( "", VariantNormal);\r
3426 \r
3427 \r
3428 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3429 \r
3430   /* Load piece bitmaps for this board size */\r
3431   for (i=0; i<=2; i++) {\r
3432     for (piece = WhitePawn;\r
3433          (int) piece < (int) BlackPawn;\r
3434          piece = (ChessSquare) ((int) piece + 1)) {\r
3435       if (pieceBitmap[i][piece] != NULL)\r
3436         DeleteObject(pieceBitmap[i][piece]);\r
3437     }\r
3438   }\r
3439 \r
3440   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3441   // Orthodox Chess pieces\r
3442   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3443   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3444   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3445   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3446   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3447   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3448   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3449   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3450   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3451   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3452   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3453   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3454   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3455   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3456   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3457   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3458     // in Shogi, Hijack the unused Queen for Lance\r
3459     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3460     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3461     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3462   } else {\r
3463     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3464     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3465     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3466   }\r
3467 \r
3468   if(squareSize <= 72 && squareSize >= 33) { \r
3469     /* A & C are available in most sizes now */\r
3470     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3471       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3472       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3473       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3474       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3475       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3476       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3477       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3478       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3479       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3480       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3481       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3482       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3483     } else { // Smirf-like\r
3484       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3485       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3486       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3487     }\r
3488     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3489       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3490       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3491       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3492     } else { // WinBoard standard\r
3493       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3494       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3495       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3496     }\r
3497   }\r
3498 \r
3499 \r
3500   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3501     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3502     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3503     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3504     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3505     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3506     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3507     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3508     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3509     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3510     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3511     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3512     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3513     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3514     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3515     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3516     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3517     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3518     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3519     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3520     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3521     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3522     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3523     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3524     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3525     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3526     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3527     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3528     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3529     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3530     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3531 \r
3532     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3533       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3534       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3535       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3536       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3537       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3538       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3539       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3540       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3541       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3542       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3543       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3544       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3545     } else {\r
3546       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3547       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3548       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3549       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3550       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3551       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3552       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3553       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3554       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3555       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3556       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3557       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3558     }\r
3559 \r
3560   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3561     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3562     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3563     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3564     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3565     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3566     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3567     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3568     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3569     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3570     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3571     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3572     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3573     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3574     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3575   }\r
3576 \r
3577 \r
3578   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3579   /* special Shogi support in this size */\r
3580   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3581       for (piece = WhitePawn;\r
3582            (int) piece < (int) BlackPawn;\r
3583            piece = (ChessSquare) ((int) piece + 1)) {\r
3584         if (pieceBitmap[i][piece] != NULL)\r
3585           DeleteObject(pieceBitmap[i][piece]);\r
3586       }\r
3587     }\r
3588   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3589   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3590   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3591   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3592   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3593   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3594   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3595   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3596   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3597   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3598   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3599   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3600   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3601   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3602   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3603   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3604   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3605   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3606   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3607   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3608   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3609   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3610   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3611   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3612   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3613   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3614   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3615   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3616   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3617   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3618   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3619   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3620   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3621   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3622   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3623   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3624   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3625   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3626   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3627   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3628   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3629   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3630   minorSize = 0;\r
3631   }\r
3632 }\r
3633 \r
3634 HBITMAP\r
3635 PieceBitmap(ChessSquare p, int kind)\r
3636 {\r
3637   if ((int) p >= (int) BlackPawn)\r
3638     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3639 \r
3640   return pieceBitmap[kind][(int) p];\r
3641 }\r
3642 \r
3643 /***************************************************************/\r
3644 \r
3645 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3646 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3647 /*\r
3648 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3649 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3650 */\r
3651 \r
3652 VOID\r
3653 SquareToPos(int row, int column, int * x, int * y)\r
3654 {\r
3655   if (flipView) {\r
3656     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3657     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3658   } else {\r
3659     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3660     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3661   }\r
3662 }\r
3663 \r
3664 VOID\r
3665 DrawCoordsOnDC(HDC hdc)\r
3666 {\r
3667   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
3668   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
3669   char str[2] = { NULLCHAR, NULLCHAR };\r
3670   int oldMode, oldAlign, x, y, start, i;\r
3671   HFONT oldFont;\r
3672   HBRUSH oldBrush;\r
3673 \r
3674   if (!appData.showCoords)\r
3675     return;\r
3676 \r
3677   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3678 \r
3679   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3680   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3681   oldAlign = GetTextAlign(hdc);\r
3682   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3683 \r
3684   y = boardRect.top + lineGap;\r
3685   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3686 \r
3687   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3688   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3689     str[0] = files[start + i];\r
3690     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3691     y += squareSize + lineGap;\r
3692   }\r
3693 \r
3694   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3695 \r
3696   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3697   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3698     str[0] = ranks[start + i];\r
3699     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3700     x += squareSize + lineGap;\r
3701   }    \r
3702 \r
3703   SelectObject(hdc, oldBrush);\r
3704   SetBkMode(hdc, oldMode);\r
3705   SetTextAlign(hdc, oldAlign);\r
3706   SelectObject(hdc, oldFont);\r
3707 }\r
3708 \r
3709 VOID\r
3710 DrawGridOnDC(HDC hdc)\r
3711 {\r
3712   HPEN oldPen;\r
3713  \r
3714   if (lineGap != 0) {\r
3715     oldPen = SelectObject(hdc, gridPen);\r
3716     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3717     SelectObject(hdc, oldPen);\r
3718   }\r
3719 }\r
3720 \r
3721 #define HIGHLIGHT_PEN 0\r
3722 #define PREMOVE_PEN   1\r
3723 \r
3724 VOID\r
3725 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3726 {\r
3727   int x1, y1;\r
3728   HPEN oldPen, hPen;\r
3729   if (lineGap == 0) return;\r
3730   if (flipView) {\r
3731     x1 = boardRect.left +\r
3732       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3733     y1 = boardRect.top +\r
3734       lineGap/2 + y * (squareSize + lineGap);\r
3735   } else {\r
3736     x1 = boardRect.left +\r
3737       lineGap/2 + x * (squareSize + lineGap);\r
3738     y1 = boardRect.top +\r
3739       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3740   }\r
3741   hPen = pen ? premovePen : highlightPen;\r
3742   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3743   MoveToEx(hdc, x1, y1, NULL);\r
3744   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3745   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3746   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3747   LineTo(hdc, x1, y1);\r
3748   SelectObject(hdc, oldPen);\r
3749 }\r
3750 \r
3751 VOID\r
3752 DrawHighlightsOnDC(HDC hdc)\r
3753 {\r
3754   int i;\r
3755   for (i=0; i<2; i++) {\r
3756     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3757       DrawHighlightOnDC(hdc, TRUE,\r
3758                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3759                         HIGHLIGHT_PEN);\r
3760   }\r
3761   for (i=0; i<2; i++) {\r
3762     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3763         premoveHighlightInfo.sq[i].y >= 0) {\r
3764         DrawHighlightOnDC(hdc, TRUE,\r
3765                           premoveHighlightInfo.sq[i].x, \r
3766                           premoveHighlightInfo.sq[i].y,\r
3767                           PREMOVE_PEN);\r
3768     }\r
3769   }\r
3770 }\r
3771 \r
3772 /* Note: sqcolor is used only in monoMode */\r
3773 /* Note that this code is largely duplicated in woptions.c,\r
3774    function DrawSampleSquare, so that needs to be updated too */\r
3775 VOID\r
3776 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3777 {\r
3778   HBITMAP oldBitmap;\r
3779   HBRUSH oldBrush;\r
3780   int tmpSize;\r
3781 \r
3782   if (appData.blindfold) return;\r
3783 \r
3784   /* [AS] Use font-based pieces if needed */\r
3785   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3786     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3787     CreatePiecesFromFont();\r
3788 \r
3789     if( fontBitmapSquareSize == squareSize ) {\r
3790         int index = TranslatePieceToFontPiece(piece);\r
3791 \r
3792         SelectObject( tmphdc, hPieceMask[ index ] );\r
3793 \r
3794         BitBlt( hdc,\r
3795             x, y,\r
3796             squareSize, squareSize,\r
3797             tmphdc,\r
3798             0, 0,\r
3799             SRCAND );\r
3800 \r
3801         SelectObject( tmphdc, hPieceFace[ index ] );\r
3802 \r
3803         BitBlt( hdc,\r
3804             x, y,\r
3805             squareSize, squareSize,\r
3806             tmphdc,\r
3807             0, 0,\r
3808             SRCPAINT );\r
3809 \r
3810         return;\r
3811     }\r
3812   }\r
3813 \r
3814   if (appData.monoMode) {\r
3815     SelectObject(tmphdc, PieceBitmap(piece, \r
3816       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3817     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3818            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3819   } else {\r
3820     tmpSize = squareSize;\r
3821     if(minorSize &&\r
3822         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3823          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3824       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3825       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3826       x += (squareSize - minorSize)>>1;\r
3827       y += squareSize - minorSize - 2;\r
3828       tmpSize = minorSize;\r
3829     }\r
3830     if (color || appData.allWhite ) {\r
3831       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3832       if( color )\r
3833               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3834       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3835       if(appData.upsideDown && color==flipView)\r
3836         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3837       else\r
3838         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3839       /* Use black for outline of white pieces */\r
3840       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3841       if(appData.upsideDown && color==flipView)\r
3842         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3843       else\r
3844         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3845     } else {\r
3846       /* Use square color for details of black pieces */\r
3847       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3848       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3849       if(appData.upsideDown && !flipView)\r
3850         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3851       else\r
3852         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3853     }\r
3854     SelectObject(hdc, oldBrush);\r
3855     SelectObject(tmphdc, oldBitmap);\r
3856   }\r
3857 }\r
3858 \r
3859 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3860 int GetBackTextureMode( int algo )\r
3861 {\r
3862     int result = BACK_TEXTURE_MODE_DISABLED;\r
3863 \r
3864     switch( algo ) \r
3865     {\r
3866         case BACK_TEXTURE_MODE_PLAIN:\r
3867             result = 1; /* Always use identity map */\r
3868             break;\r
3869         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3870             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3871             break;\r
3872     }\r
3873 \r
3874     return result;\r
3875 }\r
3876 \r
3877 /* \r
3878     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3879     to handle redraws cleanly (as random numbers would always be different).\r
3880 */\r
3881 VOID RebuildTextureSquareInfo()\r
3882 {\r
3883     BITMAP bi;\r
3884     int lite_w = 0;\r
3885     int lite_h = 0;\r
3886     int dark_w = 0;\r
3887     int dark_h = 0;\r
3888     int row;\r
3889     int col;\r
3890 \r
3891     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3892 \r
3893     if( liteBackTexture != NULL ) {\r
3894         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3895             lite_w = bi.bmWidth;\r
3896             lite_h = bi.bmHeight;\r
3897         }\r
3898     }\r
3899 \r
3900     if( darkBackTexture != NULL ) {\r
3901         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3902             dark_w = bi.bmWidth;\r
3903             dark_h = bi.bmHeight;\r
3904         }\r
3905     }\r
3906 \r
3907     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3908         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3909             if( (col + row) & 1 ) {\r
3910                 /* Lite square */\r
3911                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3912                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3913                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3914                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3915                 }\r
3916             }\r
3917             else {\r
3918                 /* Dark square */\r
3919                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3920                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3921                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3922                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3923                 }\r
3924             }\r
3925         }\r
3926     }\r
3927 }\r
3928 \r
3929 /* [AS] Arrow highlighting support */\r
3930 \r
3931 static int A_WIDTH = 5; /* Width of arrow body */\r
3932 \r
3933 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3934 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3935 \r
3936 static double Sqr( double x )\r
3937 {\r
3938     return x*x;\r
3939 }\r
3940 \r
3941 static int Round( double x )\r
3942 {\r
3943     return (int) (x + 0.5);\r
3944 }\r
3945 \r
3946 /* Draw an arrow between two points using current settings */\r
3947 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3948 {\r
3949     POINT arrow[7];\r
3950     double dx, dy, j, k, x, y;\r
3951 \r
3952     if( d_x == s_x ) {\r
3953         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3954 \r
3955         arrow[0].x = s_x + A_WIDTH;\r
3956         arrow[0].y = s_y;\r
3957 \r
3958         arrow[1].x = s_x + A_WIDTH;\r
3959         arrow[1].y = d_y - h;\r
3960 \r
3961         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3962         arrow[2].y = d_y - h;\r
3963 \r
3964         arrow[3].x = d_x;\r
3965         arrow[3].y = d_y;\r
3966 \r
3967         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3968         arrow[4].y = d_y - h;\r
3969 \r
3970         arrow[5].x = s_x - A_WIDTH;\r
3971         arrow[5].y = d_y - h;\r
3972 \r
3973         arrow[6].x = s_x - A_WIDTH;\r
3974         arrow[6].y = s_y;\r
3975     }\r
3976     else if( d_y == s_y ) {\r
3977         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3978 \r
3979         arrow[0].x = s_x;\r
3980         arrow[0].y = s_y + A_WIDTH;\r
3981 \r
3982         arrow[1].x = d_x - w;\r
3983         arrow[1].y = s_y + A_WIDTH;\r
3984 \r
3985         arrow[2].x = d_x - w;\r
3986         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3987 \r
3988         arrow[3].x = d_x;\r
3989         arrow[3].y = d_y;\r
3990 \r
3991         arrow[4].x = d_x - w;\r
3992         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3993 \r
3994         arrow[5].x = d_x - w;\r
3995         arrow[5].y = s_y - A_WIDTH;\r
3996 \r
3997         arrow[6].x = s_x;\r
3998         arrow[6].y = s_y - A_WIDTH;\r
3999     }\r
4000     else {\r
4001         /* [AS] Needed a lot of paper for this! :-) */\r
4002         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4003         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4004   \r
4005         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4006 \r
4007         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4008 \r
4009         x = s_x;\r
4010         y = s_y;\r
4011 \r
4012         arrow[0].x = Round(x - j);\r
4013         arrow[0].y = Round(y + j*dx);\r
4014 \r
4015         arrow[1].x = Round(x + j);\r
4016         arrow[1].y = Round(y - j*dx);\r
4017 \r
4018         if( d_x > s_x ) {\r
4019             x = (double) d_x - k;\r
4020             y = (double) d_y - k*dy;\r
4021         }\r
4022         else {\r
4023             x = (double) d_x + k;\r
4024             y = (double) d_y + k*dy;\r
4025         }\r
4026 \r
4027         arrow[2].x = Round(x + j);\r
4028         arrow[2].y = Round(y - j*dx);\r
4029 \r
4030         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4031         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4032 \r
4033         arrow[4].x = d_x;\r
4034         arrow[4].y = d_y;\r
4035 \r
4036         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4037         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4038 \r
4039         arrow[6].x = Round(x - j);\r
4040         arrow[6].y = Round(y + j*dx);\r
4041     }\r
4042 \r
4043     Polygon( hdc, arrow, 7 );\r
4044 }\r
4045 \r
4046 /* [AS] Draw an arrow between two squares */\r
4047 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4048 {\r
4049     int s_x, s_y, d_x, d_y;\r
4050     HPEN hpen;\r
4051     HPEN holdpen;\r
4052     HBRUSH hbrush;\r
4053     HBRUSH holdbrush;\r
4054     LOGBRUSH stLB;\r
4055 \r
4056     if( s_col == d_col && s_row == d_row ) {\r
4057         return;\r
4058     }\r
4059 \r
4060     /* Get source and destination points */\r
4061     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4062     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4063 \r
4064     if( d_y > s_y ) {\r
4065         d_y += squareSize / 4;\r
4066     }\r
4067     else if( d_y < s_y ) {\r
4068         d_y += 3 * squareSize / 4;\r
4069     }\r
4070     else {\r
4071         d_y += squareSize / 2;\r
4072     }\r
4073 \r
4074     if( d_x > s_x ) {\r
4075         d_x += squareSize / 4;\r
4076     }\r
4077     else if( d_x < s_x ) {\r
4078         d_x += 3 * squareSize / 4;\r
4079     }\r
4080     else {\r
4081         d_x += squareSize / 2;\r
4082     }\r
4083 \r
4084     s_x += squareSize / 2;\r
4085     s_y += squareSize / 2;\r
4086 \r
4087     /* Adjust width */\r
4088     A_WIDTH = squareSize / 14;\r
4089 \r
4090     /* Draw */\r
4091     stLB.lbStyle = BS_SOLID;\r
4092     stLB.lbColor = appData.highlightArrowColor;\r
4093     stLB.lbHatch = 0;\r
4094 \r
4095     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4096     holdpen = SelectObject( hdc, hpen );\r
4097     hbrush = CreateBrushIndirect( &stLB );\r
4098     holdbrush = SelectObject( hdc, hbrush );\r
4099 \r
4100     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4101 \r
4102     SelectObject( hdc, holdpen );\r
4103     SelectObject( hdc, holdbrush );\r
4104     DeleteObject( hpen );\r
4105     DeleteObject( hbrush );\r
4106 }\r
4107 \r
4108 BOOL HasHighlightInfo()\r
4109 {\r
4110     BOOL result = FALSE;\r
4111 \r
4112     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4113         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4114     {\r
4115         result = TRUE;\r
4116     }\r
4117 \r
4118     return result;\r
4119 }\r
4120 \r
4121 BOOL IsDrawArrowEnabled()\r
4122 {\r
4123     BOOL result = FALSE;\r
4124 \r
4125     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4126         result = TRUE;\r
4127     }\r
4128 \r
4129     return result;\r
4130 }\r
4131 \r
4132 VOID DrawArrowHighlight( HDC hdc )\r
4133 {\r
4134     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4135         DrawArrowBetweenSquares( hdc,\r
4136             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4137             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4138     }\r
4139 }\r
4140 \r
4141 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4142 {\r
4143     HRGN result = NULL;\r
4144 \r
4145     if( HasHighlightInfo() ) {\r
4146         int x1, y1, x2, y2;\r
4147         int sx, sy, dx, dy;\r
4148 \r
4149         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4150         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4151 \r
4152         sx = MIN( x1, x2 );\r
4153         sy = MIN( y1, y2 );\r
4154         dx = MAX( x1, x2 ) + squareSize;\r
4155         dy = MAX( y1, y2 ) + squareSize;\r
4156 \r
4157         result = CreateRectRgn( sx, sy, dx, dy );\r
4158     }\r
4159 \r
4160     return result;\r
4161 }\r
4162 \r
4163 /*\r
4164     Warning: this function modifies the behavior of several other functions. \r
4165     \r
4166     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4167     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4168     repaint is scattered all over the place, which is not good for features such as\r
4169     "arrow highlighting" that require a full repaint of the board.\r
4170 \r
4171     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4172     user interaction, when speed is not so important) but especially to avoid errors\r
4173     in the displayed graphics.\r
4174 \r
4175     In such patched places, I always try refer to this function so there is a single\r
4176     place to maintain knowledge.\r
4177     \r
4178     To restore the original behavior, just return FALSE unconditionally.\r
4179 */\r
4180 BOOL IsFullRepaintPreferrable()\r
4181 {\r
4182     BOOL result = FALSE;\r
4183 \r
4184     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4185         /* Arrow may appear on the board */\r
4186         result = TRUE;\r
4187     }\r
4188 \r
4189     return result;\r
4190 }\r
4191 \r
4192 /* \r
4193     This function is called by DrawPosition to know whether a full repaint must\r
4194     be forced or not.\r
4195 \r
4196     Only DrawPosition may directly call this function, which makes use of \r
4197     some state information. Other function should call DrawPosition specifying \r
4198     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4199 */\r
4200 BOOL DrawPositionNeedsFullRepaint()\r
4201 {\r
4202     BOOL result = FALSE;\r
4203 \r
4204     /* \r
4205         Probably a slightly better policy would be to trigger a full repaint\r
4206         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4207         but animation is fast enough that it's difficult to notice.\r
4208     */\r
4209     if( animInfo.piece == EmptySquare ) {\r
4210         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4211             result = TRUE;\r
4212         }\r
4213     }\r
4214 \r
4215     return result;\r
4216 }\r
4217 \r
4218 VOID\r
4219 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4220 {\r
4221   int row, column, x, y, square_color, piece_color;\r
4222   ChessSquare piece;\r
4223   HBRUSH oldBrush;\r
4224   HDC texture_hdc = NULL;\r
4225 \r
4226   /* [AS] Initialize background textures if needed */\r
4227   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4228       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4229       if( backTextureSquareSize != squareSize \r
4230        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4231           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4232           backTextureSquareSize = squareSize;\r
4233           RebuildTextureSquareInfo();\r
4234       }\r
4235 \r
4236       texture_hdc = CreateCompatibleDC( hdc );\r
4237   }\r
4238 \r
4239   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4240     for (column = 0; column < BOARD_WIDTH; column++) {\r
4241   \r
4242       SquareToPos(row, column, &x, &y);\r
4243 \r
4244       piece = board[row][column];\r
4245 \r
4246       square_color = ((column + row) % 2) == 1;\r
4247       if( gameInfo.variant == VariantXiangqi ) {\r
4248           square_color = !InPalace(row, column);\r
4249           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4250           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4251       }\r
4252       piece_color = (int) piece < (int) BlackPawn;\r
4253 \r
4254 \r
4255       /* [HGM] holdings file: light square or black */\r
4256       if(column == BOARD_LEFT-2) {\r
4257             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4258                 square_color = 1;\r
4259             else {\r
4260                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4261                 continue;\r
4262             }\r
4263       } else\r
4264       if(column == BOARD_RGHT + 1 ) {\r
4265             if( row < gameInfo.holdingsSize )\r
4266                 square_color = 1;\r
4267             else {\r
4268                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4269                 continue;\r
4270             }\r
4271       }\r
4272       if(column == BOARD_LEFT-1 ) /* left align */\r
4273             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4274       else if( column == BOARD_RGHT) /* right align */\r
4275             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4276       else\r
4277       if (appData.monoMode) {\r
4278         if (piece == EmptySquare) {\r
4279           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4280                  square_color ? WHITENESS : BLACKNESS);\r
4281         } else {\r
4282           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4283         }\r
4284       } \r
4285       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4286           /* [AS] Draw the square using a texture bitmap */\r
4287           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4288           int r = row, c = column; // [HGM] do not flip board in flipView\r
4289           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4290 \r
4291           DrawTile( x, y, \r
4292               squareSize, squareSize, \r
4293               hdc, \r
4294               texture_hdc,\r
4295               backTextureSquareInfo[r][c].mode,\r
4296               backTextureSquareInfo[r][c].x,\r
4297               backTextureSquareInfo[r][c].y );\r
4298 \r
4299           SelectObject( texture_hdc, hbm );\r
4300 \r
4301           if (piece != EmptySquare) {\r
4302               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4303           }\r
4304       }\r
4305       else {\r
4306         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4307 \r
4308         oldBrush = SelectObject(hdc, brush );\r
4309         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4310         SelectObject(hdc, oldBrush);\r
4311         if (piece != EmptySquare)\r
4312           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4313       }\r
4314     }\r
4315   }\r
4316 \r
4317   if( texture_hdc != NULL ) {\r
4318     DeleteDC( texture_hdc );\r
4319   }\r
4320 }\r
4321 \r
4322 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4323 void fputDW(FILE *f, int x)\r
4324 {\r
4325         fputc(x     & 255, f);\r
4326         fputc(x>>8  & 255, f);\r
4327         fputc(x>>16 & 255, f);\r
4328         fputc(x>>24 & 255, f);\r
4329 }\r
4330 \r
4331 #define MAX_CLIPS 200   /* more than enough */\r
4332 \r
4333 VOID\r
4334 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4335 {\r
4336 //  HBITMAP bufferBitmap;\r
4337   BITMAP bi;\r
4338 //  RECT Rect;\r
4339   HDC tmphdc;\r
4340   HBITMAP hbm;\r
4341   int w = 100, h = 50;\r
4342 \r
4343   if(logo == NULL) return;\r
4344 //  GetClientRect(hwndMain, &Rect);\r
4345 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4346 //                                      Rect.bottom-Rect.top+1);\r
4347   tmphdc = CreateCompatibleDC(hdc);\r
4348   hbm = SelectObject(tmphdc, logo);\r
4349   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4350             w = bi.bmWidth;\r
4351             h = bi.bmHeight;\r
4352   }\r
4353   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4354                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4355   SelectObject(tmphdc, hbm);\r
4356   DeleteDC(tmphdc);\r
4357 }\r
4358 \r
4359 VOID\r
4360 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4361 {\r
4362   static Board lastReq, lastDrawn;\r
4363   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4364   static int lastDrawnFlipView = 0;\r
4365   static int lastReqValid = 0, lastDrawnValid = 0;\r
4366   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4367   HDC tmphdc;\r
4368   HDC hdcmem;\r
4369   HBITMAP bufferBitmap;\r
4370   HBITMAP oldBitmap;\r
4371   RECT Rect;\r
4372   HRGN clips[MAX_CLIPS];\r
4373   ChessSquare dragged_piece = EmptySquare;\r
4374 \r
4375   /* I'm undecided on this - this function figures out whether a full\r
4376    * repaint is necessary on its own, so there's no real reason to have the\r
4377    * caller tell it that.  I think this can safely be set to FALSE - but\r
4378    * if we trust the callers not to request full repaints unnessesarily, then\r
4379    * we could skip some clipping work.  In other words, only request a full\r
4380    * redraw when the majority of pieces have changed positions (ie. flip, \r
4381    * gamestart and similar)  --Hawk\r
4382    */\r
4383   Boolean fullrepaint = repaint;\r
4384 \r
4385   if( DrawPositionNeedsFullRepaint() ) {\r
4386       fullrepaint = TRUE;\r
4387   }\r
4388 \r
4389   if (board == NULL) {\r
4390     if (!lastReqValid) {\r
4391       return;\r
4392     }\r
4393     board = lastReq;\r
4394   } else {\r
4395     CopyBoard(lastReq, board);\r
4396     lastReqValid = 1;\r
4397   }\r
4398 \r
4399   if (doingSizing) {\r
4400     return;\r
4401   }\r
4402 \r
4403   if (IsIconic(hwndMain)) {\r
4404     return;\r
4405   }\r
4406 \r
4407   if (hdc == NULL) {\r
4408     hdc = GetDC(hwndMain);\r
4409     if (!appData.monoMode) {\r
4410       SelectPalette(hdc, hPal, FALSE);\r
4411       RealizePalette(hdc);\r
4412     }\r
4413     releaseDC = TRUE;\r
4414   } else {\r
4415     releaseDC = FALSE;\r
4416   }\r
4417 \r
4418   /* Create some work-DCs */\r
4419   hdcmem = CreateCompatibleDC(hdc);\r
4420   tmphdc = CreateCompatibleDC(hdc);\r
4421 \r
4422   /* If dragging is in progress, we temporarely remove the piece */\r
4423   /* [HGM] or temporarily decrease count if stacked              */\r
4424   /*       !! Moved to before board compare !!                   */\r
4425   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4426     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4427     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4428             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4429         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4430     } else \r
4431     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4432             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4433         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4434     } else \r
4435         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4436   }\r
4437 \r
4438   /* Figure out which squares need updating by comparing the \r
4439    * newest board with the last drawn board and checking if\r
4440    * flipping has changed.\r
4441    */\r
4442   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4443     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4444       for (column = 0; column < BOARD_WIDTH; column++) {\r
4445         if (lastDrawn[row][column] != board[row][column]) {\r
4446           SquareToPos(row, column, &x, &y);\r
4447           clips[num_clips++] =\r
4448             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4449         }\r
4450       }\r
4451     }\r
4452     for (i=0; i<2; i++) {\r
4453       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4454           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4455         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4456             lastDrawnHighlight.sq[i].y >= 0) {\r
4457           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4458                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4459           clips[num_clips++] =\r
4460             CreateRectRgn(x - lineGap, y - lineGap, \r
4461                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4462         }\r
4463         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4464           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4465           clips[num_clips++] =\r
4466             CreateRectRgn(x - lineGap, y - lineGap, \r
4467                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4468         }\r
4469       }\r
4470     }\r
4471     for (i=0; i<2; i++) {\r
4472       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4473           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4474         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4475             lastDrawnPremove.sq[i].y >= 0) {\r
4476           SquareToPos(lastDrawnPremove.sq[i].y,\r
4477                       lastDrawnPremove.sq[i].x, &x, &y);\r
4478           clips[num_clips++] =\r
4479             CreateRectRgn(x - lineGap, y - lineGap, \r
4480                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4481         }\r
4482         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4483             premoveHighlightInfo.sq[i].y >= 0) {\r
4484           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4485                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4486           clips[num_clips++] =\r
4487             CreateRectRgn(x - lineGap, y - lineGap, \r
4488                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4489         }\r
4490       }\r
4491     }\r
4492   } else {\r
4493     fullrepaint = TRUE;\r
4494   }\r
4495 \r
4496   /* Create a buffer bitmap - this is the actual bitmap\r
4497    * being written to.  When all the work is done, we can\r
4498    * copy it to the real DC (the screen).  This avoids\r
4499    * the problems with flickering.\r
4500    */\r
4501   GetClientRect(hwndMain, &Rect);\r
4502   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4503                                         Rect.bottom-Rect.top+1);\r
4504   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4505   if (!appData.monoMode) {\r
4506     SelectPalette(hdcmem, hPal, FALSE);\r
4507   }\r
4508 \r
4509   /* Create clips for dragging */\r
4510   if (!fullrepaint) {\r
4511     if (dragInfo.from.x >= 0) {\r
4512       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4513       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4514     }\r
4515     if (dragInfo.start.x >= 0) {\r
4516       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4517       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4518     }\r
4519     if (dragInfo.pos.x >= 0) {\r
4520       x = dragInfo.pos.x - squareSize / 2;\r
4521       y = dragInfo.pos.y - squareSize / 2;\r
4522       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4523     }\r
4524     if (dragInfo.lastpos.x >= 0) {\r
4525       x = dragInfo.lastpos.x - squareSize / 2;\r
4526       y = dragInfo.lastpos.y - squareSize / 2;\r
4527       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4528     }\r
4529   }\r
4530 \r
4531   /* Are we animating a move?  \r
4532    * If so, \r
4533    *   - remove the piece from the board (temporarely)\r
4534    *   - calculate the clipping region\r
4535    */\r
4536   if (!fullrepaint) {\r
4537     if (animInfo.piece != EmptySquare) {\r
4538       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4539       x = boardRect.left + animInfo.lastpos.x;\r
4540       y = boardRect.top + animInfo.lastpos.y;\r
4541       x2 = boardRect.left + animInfo.pos.x;\r
4542       y2 = boardRect.top + animInfo.pos.y;\r
4543       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4544       /* Slight kludge.  The real problem is that after AnimateMove is\r
4545          done, the position on the screen does not match lastDrawn.\r
4546          This currently causes trouble only on e.p. captures in\r
4547          atomic, where the piece moves to an empty square and then\r
4548          explodes.  The old and new positions both had an empty square\r
4549          at the destination, but animation has drawn a piece there and\r
4550          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4551       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4552     }\r
4553   }\r
4554 \r
4555   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4556   if (num_clips == 0)\r
4557     fullrepaint = TRUE;\r
4558 \r
4559   /* Set clipping on the memory DC */\r
4560   if (!fullrepaint) {\r
4561     SelectClipRgn(hdcmem, clips[0]);\r
4562     for (x = 1; x < num_clips; x++) {\r
4563       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4564         abort();  // this should never ever happen!\r
4565     }\r
4566   }\r
4567 \r
4568   /* Do all the drawing to the memory DC */\r
4569   if(explodeInfo.radius) { // [HGM] atomic\r
4570         HBRUSH oldBrush;\r
4571         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4572         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4573         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4574         x += squareSize/2;\r
4575         y += squareSize/2;\r
4576         if(!fullrepaint) {\r
4577           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4578           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4579         }\r
4580         DrawGridOnDC(hdcmem);\r
4581         DrawHighlightsOnDC(hdcmem);\r
4582         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4583         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4584         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4585         SelectObject(hdcmem, oldBrush);\r
4586   } else {\r
4587     DrawGridOnDC(hdcmem);\r
4588     DrawHighlightsOnDC(hdcmem);\r
4589     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4590   }\r
4591   if(logoHeight) {\r
4592         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4593         if(appData.autoLogo) {\r
4594           \r
4595           switch(gameMode) { // pick logos based on game mode\r
4596             case IcsObserving:\r
4597                 whiteLogo = second.programLogo; // ICS logo\r
4598                 blackLogo = second.programLogo;\r
4599             default:\r
4600                 break;\r
4601             case IcsPlayingWhite:\r
4602                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4603                 blackLogo = second.programLogo; // ICS logo\r
4604                 break;\r
4605             case IcsPlayingBlack:\r
4606                 whiteLogo = second.programLogo; // ICS logo\r
4607                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4608                 break;\r
4609             case TwoMachinesPlay:\r
4610                 if(first.twoMachinesColor[0] == 'b') {\r
4611                     whiteLogo = second.programLogo;\r
4612                     blackLogo = first.programLogo;\r
4613                 }\r
4614                 break;\r
4615             case MachinePlaysWhite:\r
4616                 blackLogo = userLogo;\r
4617                 break;\r
4618             case MachinePlaysBlack:\r
4619                 whiteLogo = userLogo;\r
4620                 blackLogo = first.programLogo;\r
4621           }\r
4622         }\r
4623         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4624         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4625   }\r
4626 \r
4627   if( appData.highlightMoveWithArrow ) {\r
4628     DrawArrowHighlight(hdcmem);\r
4629   }\r
4630 \r
4631   DrawCoordsOnDC(hdcmem);\r
4632 \r
4633   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4634                  /* to make sure lastDrawn contains what is actually drawn */\r
4635 \r
4636   /* Put the dragged piece back into place and draw it (out of place!) */\r
4637     if (dragged_piece != EmptySquare) {\r
4638     /* [HGM] or restack */\r
4639     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4640                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4641     else\r
4642     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4643                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4644     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4645     x = dragInfo.pos.x - squareSize / 2;\r
4646     y = dragInfo.pos.y - squareSize / 2;\r
4647     DrawPieceOnDC(hdcmem, dragged_piece,\r
4648                   ((int) dragged_piece < (int) BlackPawn), \r
4649                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4650   }   \r
4651   \r
4652   /* Put the animated piece back into place and draw it */\r
4653   if (animInfo.piece != EmptySquare) {\r
4654     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4655     x = boardRect.left + animInfo.pos.x;\r
4656     y = boardRect.top + animInfo.pos.y;\r
4657     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4658                   ((int) animInfo.piece < (int) BlackPawn),\r
4659                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4660   }\r
4661 \r
4662   /* Release the bufferBitmap by selecting in the old bitmap \r
4663    * and delete the memory DC\r
4664    */\r
4665   SelectObject(hdcmem, oldBitmap);\r
4666   DeleteDC(hdcmem);\r
4667 \r
4668   /* Set clipping on the target DC */\r
4669   if (!fullrepaint) {\r
4670     SelectClipRgn(hdc, clips[0]);\r
4671     for (x = 1; x < num_clips; x++) {\r
4672       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4673         abort();   // this should never ever happen!\r
4674     } \r
4675   }\r
4676 \r
4677   /* Copy the new bitmap onto the screen in one go.\r
4678    * This way we avoid any flickering\r
4679    */\r
4680   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4681   BitBlt(hdc, boardRect.left, boardRect.top,\r
4682          boardRect.right - boardRect.left,\r
4683          boardRect.bottom - boardRect.top,\r
4684          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4685   if(saveDiagFlag) { \r
4686     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4687     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4688 \r
4689     GetObject(bufferBitmap, sizeof(b), &b);\r
4690     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4691         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4692         bih.biWidth = b.bmWidth;\r
4693         bih.biHeight = b.bmHeight;\r
4694         bih.biPlanes = 1;\r
4695         bih.biBitCount = b.bmBitsPixel;\r
4696         bih.biCompression = 0;\r
4697         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4698         bih.biXPelsPerMeter = 0;\r
4699         bih.biYPelsPerMeter = 0;\r
4700         bih.biClrUsed = 0;\r
4701         bih.biClrImportant = 0;\r
4702 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4703 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4704         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4705 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4706 \r
4707         wb = b.bmWidthBytes;\r
4708         // count colors\r
4709         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4710                 int k = ((int*) pData)[i];\r
4711                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4712                 if(j >= 16) break;\r
4713                 color[j] = k;\r
4714                 if(j >= nrColors) nrColors = j+1;\r
4715         }\r
4716         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4717                 INT p = 0;\r
4718                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4719                     for(w=0; w<(wb>>2); w+=2) {\r
4720                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4721                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4722                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4723                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4724                         pData[p++] = m | j<<4;\r
4725                     }\r
4726                     while(p&3) pData[p++] = 0;\r
4727                 }\r
4728                 fac = 3;\r
4729                 wb = ((wb+31)>>5)<<2;\r
4730         }\r
4731         // write BITMAPFILEHEADER\r
4732         fprintf(diagFile, "BM");\r
4733         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4734         fputDW(diagFile, 0);\r
4735         fputDW(diagFile, 0x36 + (fac?64:0));\r
4736         // write BITMAPINFOHEADER\r
4737         fputDW(diagFile, 40);\r
4738         fputDW(diagFile, b.bmWidth);\r
4739         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4740         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4741         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4742         fputDW(diagFile, 0);\r
4743         fputDW(diagFile, 0);\r
4744         fputDW(diagFile, 0);\r
4745         fputDW(diagFile, 0);\r
4746         fputDW(diagFile, 0);\r
4747         fputDW(diagFile, 0);\r
4748         // write color table\r
4749         if(fac)\r
4750         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4751         // write bitmap data\r
4752         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4753                 fputc(pData[i], diagFile);\r
4754      }\r
4755   }\r
4756 \r
4757   SelectObject(tmphdc, oldBitmap);\r
4758 \r
4759   /* Massive cleanup */\r
4760   for (x = 0; x < num_clips; x++)\r
4761     DeleteObject(clips[x]);\r
4762 \r
4763   DeleteDC(tmphdc);\r
4764   DeleteObject(bufferBitmap);\r
4765 \r
4766   if (releaseDC) \r
4767     ReleaseDC(hwndMain, hdc);\r
4768   \r
4769   if (lastDrawnFlipView != flipView) {\r
4770     if (flipView)\r
4771       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4772     else\r
4773       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4774   }\r
4775 \r
4776 /*  CopyBoard(lastDrawn, board);*/\r
4777   lastDrawnHighlight = highlightInfo;\r
4778   lastDrawnPremove   = premoveHighlightInfo;\r
4779   lastDrawnFlipView = flipView;\r
4780   lastDrawnValid = 1;\r
4781 }\r
4782 \r
4783 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4784 int\r
4785 SaveDiagram(f)\r
4786      FILE *f;\r
4787 {\r
4788     saveDiagFlag = 1; diagFile = f;\r
4789     HDCDrawPosition(NULL, TRUE, NULL);\r
4790 \r
4791     saveDiagFlag = 0;\r
4792 \r
4793 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4794     \r
4795     fclose(f);\r
4796     return TRUE;\r
4797 }\r
4798 \r
4799 \r
4800 /*---------------------------------------------------------------------------*\\r
4801 | CLIENT PAINT PROCEDURE\r
4802 |   This is the main event-handler for the WM_PAINT message.\r
4803 |\r
4804 \*---------------------------------------------------------------------------*/\r
4805 VOID\r
4806 PaintProc(HWND hwnd)\r
4807 {\r
4808   HDC         hdc;\r
4809   PAINTSTRUCT ps;\r
4810   HFONT       oldFont;\r
4811 \r
4812   if((hdc = BeginPaint(hwnd, &ps))) {\r
4813     if (IsIconic(hwnd)) {\r
4814       DrawIcon(hdc, 2, 2, iconCurrent);\r
4815     } else {\r
4816       if (!appData.monoMode) {\r
4817         SelectPalette(hdc, hPal, FALSE);\r
4818         RealizePalette(hdc);\r
4819       }\r
4820       HDCDrawPosition(hdc, 1, NULL);\r
4821       oldFont =\r
4822         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4823       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4824                  ETO_CLIPPED|ETO_OPAQUE,\r
4825                  &messageRect, messageText, strlen(messageText), NULL);\r
4826       SelectObject(hdc, oldFont);\r
4827       DisplayBothClocks();\r
4828     }\r
4829     EndPaint(hwnd,&ps);\r
4830   }\r
4831 \r
4832   return;\r
4833 }\r
4834 \r
4835 \r
4836 /*\r
4837  * If the user selects on a border boundary, return -1; if off the board,\r
4838  *   return -2.  Otherwise map the event coordinate to the square.\r
4839  * The offset boardRect.left or boardRect.top must already have been\r
4840  *   subtracted from x.\r
4841  */\r
4842 int EventToSquare(x, limit)\r
4843      int x;\r
4844 {\r
4845   if (x <= 0)\r
4846     return -2;\r
4847   if (x < lineGap)\r
4848     return -1;\r
4849   x -= lineGap;\r
4850   if ((x % (squareSize + lineGap)) >= squareSize)\r
4851     return -1;\r
4852   x /= (squareSize + lineGap);\r
4853     if (x >= limit)\r
4854     return -2;\r
4855   return x;\r
4856 }\r
4857 \r
4858 typedef struct {\r
4859   char piece;\r
4860   int command;\r
4861   char* name;\r
4862 } DropEnable;\r
4863 \r
4864 DropEnable dropEnables[] = {\r
4865   { 'P', DP_Pawn, "Pawn" },\r
4866   { 'N', DP_Knight, "Knight" },\r
4867   { 'B', DP_Bishop, "Bishop" },\r
4868   { 'R', DP_Rook, "Rook" },\r
4869   { 'Q', DP_Queen, "Queen" },\r
4870 };\r
4871 \r
4872 VOID\r
4873 SetupDropMenu(HMENU hmenu)\r
4874 {\r
4875   int i, count, enable;\r
4876   char *p;\r
4877   extern char white_holding[], black_holding[];\r
4878   char item[MSG_SIZ];\r
4879 \r
4880   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4881     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4882                dropEnables[i].piece);\r
4883     count = 0;\r
4884     while (p && *p++ == dropEnables[i].piece) count++;\r
4885     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4886     enable = count > 0 || !appData.testLegality\r
4887       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4888                       && !appData.icsActive);\r
4889     ModifyMenu(hmenu, dropEnables[i].command,\r
4890                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4891                dropEnables[i].command, item);\r
4892   }\r
4893 }\r
4894 \r
4895 void DragPieceBegin(int x, int y)\r
4896 {\r
4897       dragInfo.lastpos.x = boardRect.left + x;\r
4898       dragInfo.lastpos.y = boardRect.top + y;\r
4899       dragInfo.from.x = fromX;\r
4900       dragInfo.from.y = fromY;\r
4901       dragInfo.start = dragInfo.from;\r
4902       SetCapture(hwndMain);\r
4903 }\r
4904 \r
4905 void DragPieceEnd(int x, int y)\r
4906 {\r
4907     ReleaseCapture();\r
4908     dragInfo.start.x = dragInfo.start.y = -1;\r
4909     dragInfo.from = dragInfo.start;\r
4910     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4911 }\r
4912 \r
4913 /* Event handler for mouse messages */\r
4914 VOID\r
4915 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4916 {\r
4917   int x, y;\r
4918   POINT pt;\r
4919   static int recursive = 0;\r
4920   HMENU hmenu;\r
4921   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4922 \r
4923   if (recursive) {\r
4924     if (message == WM_MBUTTONUP) {\r
4925       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4926          to the middle button: we simulate pressing the left button too!\r
4927          */\r
4928       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4929       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4930     }\r
4931     return;\r
4932   }\r
4933   recursive++;\r
4934   \r
4935   pt.x = LOWORD(lParam);\r
4936   pt.y = HIWORD(lParam);\r
4937   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4938   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4939   if (!flipView && y >= 0) {\r
4940     y = BOARD_HEIGHT - 1 - y;\r
4941   }\r
4942   if (flipView && x >= 0) {\r
4943     x = BOARD_WIDTH - 1 - x;\r
4944   }\r
4945 \r
4946   switch (message) {\r
4947   case WM_LBUTTONDOWN:\r
4948       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4949         if (gameMode == EditPosition) {\r
4950           SetWhiteToPlayEvent();\r
4951         } else if (gameMode == IcsPlayingBlack ||\r
4952                    gameMode == MachinePlaysWhite) {\r
4953           CallFlagEvent();\r
4954         } else if (gameMode == EditGame) {\r
4955           AdjustClock(flipClock, -1);\r
4956         }\r
4957       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4958         if (gameMode == EditPosition) {\r
4959           SetBlackToPlayEvent();\r
4960         } else if (gameMode == IcsPlayingWhite ||\r
4961                    gameMode == MachinePlaysBlack) {\r
4962           CallFlagEvent();\r
4963         } else if (gameMode == EditGame) {\r
4964           AdjustClock(!flipClock, -1);\r
4965         }\r
4966       }\r
4967       dragInfo.start.x = dragInfo.start.y = -1;\r
4968       dragInfo.from = dragInfo.start;\r
4969     if(fromX == -1 && frozen) { // not sure where this is for\r
4970                 fromX = fromY = -1; \r
4971       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4972       break;\r
4973     }\r
4974       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4975       DrawPosition(TRUE, NULL);\r
4976     break;\r
4977 \r
4978   case WM_LBUTTONUP:\r
4979       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4980       DrawPosition(TRUE, NULL);\r
4981     break;\r
4982 \r
4983   case WM_MOUSEMOVE:\r
4984     if ((appData.animateDragging || appData.highlightDragging)\r
4985         && (wParam & MK_LBUTTON)\r
4986         && dragInfo.from.x >= 0) \r
4987     {\r
4988       BOOL full_repaint = FALSE;\r
4989 \r
4990       if (appData.animateDragging) {\r
4991         dragInfo.pos = pt;\r
4992       }\r
4993       if (appData.highlightDragging) {\r
4994         SetHighlights(fromX, fromY, x, y);\r
4995         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4996             full_repaint = TRUE;\r
4997         }\r
4998       }\r
4999       \r
5000       DrawPosition( full_repaint, NULL);\r
5001       \r
5002       dragInfo.lastpos = dragInfo.pos;\r
5003     }\r
5004     break;\r
5005 \r
5006   case WM_MOUSEWHEEL: // [DM]\r
5007     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5008        /* Mouse Wheel is being rolled forward\r
5009         * Play moves forward\r
5010         */\r
5011        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5012                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5013        /* Mouse Wheel is being rolled backward\r
5014         * Play moves backward\r
5015         */\r
5016        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5017                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5018     }\r
5019     break;\r
5020 \r
5021   case WM_MBUTTONDOWN:\r
5022   case WM_RBUTTONDOWN:\r
5023     ErrorPopDown();\r
5024     ReleaseCapture();\r
5025     fromX = fromY = -1;\r
5026     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5027     dragInfo.start.x = dragInfo.start.y = -1;\r
5028     dragInfo.from = dragInfo.start;\r
5029     dragInfo.lastpos = dragInfo.pos;\r
5030     if (appData.highlightDragging) {\r
5031       ClearHighlights();\r
5032     }\r
5033     if(y == -2) {\r
5034       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5035       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5036           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5037       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5038           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5039       }\r
5040     }\r
5041     DrawPosition(TRUE, NULL);\r
5042 \r
5043     switch (gameMode) {\r
5044     case EditPosition:\r
5045     case IcsExamining:\r
5046       if (x < 0 || y < 0) break;\r
5047       fromX = x;\r
5048       fromY = y;\r
5049       if (message == WM_MBUTTONDOWN) {\r
5050         buttonCount = 3;  /* even if system didn't think so */\r
5051         if (wParam & MK_SHIFT) \r
5052           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5053         else\r
5054           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5055       } else { /* message == WM_RBUTTONDOWN */\r
5056         /* Just have one menu, on the right button.  Windows users don't\r
5057            think to try the middle one, and sometimes other software steals\r
5058            it, or it doesn't really exist. */\r
5059         if(gameInfo.variant != VariantShogi)\r
5060             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5061         else\r
5062             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5063       }\r
5064       break;\r
5065     case IcsPlayingWhite:\r
5066     case IcsPlayingBlack:\r
5067     case EditGame:\r
5068     case MachinePlaysWhite:\r
5069     case MachinePlaysBlack:\r
5070       if (appData.testLegality &&\r
5071           gameInfo.variant != VariantBughouse &&\r
5072           gameInfo.variant != VariantCrazyhouse) break;\r
5073       if (x < 0 || y < 0) break;\r
5074       fromX = x;\r
5075       fromY = y;\r
5076       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5077       SetupDropMenu(hmenu);\r
5078       MenuPopup(hwnd, pt, hmenu, -1);\r
5079       break;\r
5080     default:\r
5081       break;\r
5082     }\r
5083     break;\r
5084   }\r
5085 \r
5086   recursive--;\r
5087 }\r
5088 \r
5089 /* Preprocess messages for buttons in main window */\r
5090 LRESULT CALLBACK\r
5091 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5092 {\r
5093   int id = GetWindowLong(hwnd, GWL_ID);\r
5094   int i, dir;\r
5095 \r
5096   for (i=0; i<N_BUTTONS; i++) {\r
5097     if (buttonDesc[i].id == id) break;\r
5098   }\r
5099   if (i == N_BUTTONS) return 0;\r
5100   switch (message) {\r
5101   case WM_KEYDOWN:\r
5102     switch (wParam) {\r
5103     case VK_LEFT:\r
5104     case VK_RIGHT:\r
5105       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5106       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5107       return TRUE;\r
5108     }\r
5109     break;\r
5110   case WM_CHAR:\r
5111     switch (wParam) {\r
5112     case '\r':\r
5113       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5114       return TRUE;\r
5115     default:\r
5116       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5117         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5118         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5119         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5120         SetFocus(h);\r
5121         SendMessage(h, WM_CHAR, wParam, lParam);\r
5122         return TRUE;\r
5123       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5124         PopUpMoveDialog((char)wParam);\r
5125       }\r
5126       break;\r
5127     }\r
5128     break;\r
5129   }\r
5130   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5131 }\r
5132 \r
5133 /* Process messages for Promotion dialog box */\r
5134 LRESULT CALLBACK\r
5135 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5136 {\r
5137   char promoChar;\r
5138 \r
5139   switch (message) {\r
5140   case WM_INITDIALOG: /* message: initialize dialog box */\r
5141     /* Center the dialog over the application window */\r
5142     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5143     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5144       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5145        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5146                SW_SHOW : SW_HIDE);\r
5147     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5148     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5149        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5150          PieceToChar(WhiteAngel) != '~') ||\r
5151         (PieceToChar(BlackAngel) >= 'A' &&\r
5152          PieceToChar(BlackAngel) != '~')   ) ?\r
5153                SW_SHOW : SW_HIDE);\r
5154     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5155        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5156          PieceToChar(WhiteMarshall) != '~') ||\r
5157         (PieceToChar(BlackMarshall) >= 'A' &&\r
5158          PieceToChar(BlackMarshall) != '~')   ) ?\r
5159                SW_SHOW : SW_HIDE);\r
5160     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5161     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5162        gameInfo.variant != VariantShogi ?\r
5163                SW_SHOW : SW_HIDE);\r
5164     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5165        gameInfo.variant != VariantShogi ?\r
5166                SW_SHOW : SW_HIDE);\r
5167     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5168        gameInfo.variant == VariantShogi ?\r
5169                SW_SHOW : SW_HIDE);\r
5170     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5171        gameInfo.variant == VariantShogi ?\r
5172                SW_SHOW : SW_HIDE);\r
5173     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5174        gameInfo.variant == VariantSuper ?\r
5175                SW_SHOW : SW_HIDE);\r
5176     return TRUE;\r
5177 \r
5178   case WM_COMMAND: /* message: received a command */\r
5179     switch (LOWORD(wParam)) {\r
5180     case IDCANCEL:\r
5181       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5182       ClearHighlights();\r
5183       DrawPosition(FALSE, NULL);\r
5184       return TRUE;\r
5185     case PB_King:\r
5186       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5187       break;\r
5188     case PB_Queen:\r
5189       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5190       break;\r
5191     case PB_Rook:\r
5192       promoChar = PieceToChar(BlackRook);\r
5193       break;\r
5194     case PB_Bishop:\r
5195       promoChar = PieceToChar(BlackBishop);\r
5196       break;\r
5197     case PB_Chancellor:\r
5198       promoChar = PieceToChar(BlackMarshall);\r
5199       break;\r
5200     case PB_Archbishop:\r
5201       promoChar = PieceToChar(BlackAngel);\r
5202       break;\r
5203     case PB_Knight:\r
5204       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5205       break;\r
5206     default:\r
5207       return FALSE;\r
5208     }\r
5209     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5210     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5211        only show the popup when we are already sure the move is valid or\r
5212        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5213        will figure out it is a promotion from the promoChar. */\r
5214     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
5215     fromX = fromY = -1;\r
5216     if (!appData.highlightLastMove) {\r
5217       ClearHighlights();\r
5218       DrawPosition(FALSE, NULL);\r
5219     }\r
5220     return TRUE;\r
5221   }\r
5222   return FALSE;\r
5223 }\r
5224 \r
5225 /* Pop up promotion dialog */\r
5226 VOID\r
5227 PromotionPopup(HWND hwnd)\r
5228 {\r
5229   FARPROC lpProc;\r
5230 \r
5231   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5232   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5233     hwnd, (DLGPROC)lpProc);\r
5234   FreeProcInstance(lpProc);\r
5235 }\r
5236 \r
5237 void\r
5238 PromotionPopUp()\r
5239 {\r
5240   DrawPosition(TRUE, NULL);\r
5241   PromotionPopup(hwndMain);\r
5242 }\r
5243 \r
5244 /* Toggle ShowThinking */\r
5245 VOID\r
5246 ToggleShowThinking()\r
5247 {\r
5248   appData.showThinking = !appData.showThinking;\r
5249   ShowThinkingEvent();\r
5250 }\r
5251 \r
5252 VOID\r
5253 LoadGameDialog(HWND hwnd, char* title)\r
5254 {\r
5255   UINT number = 0;\r
5256   FILE *f;\r
5257   char fileTitle[MSG_SIZ];\r
5258   f = OpenFileDialog(hwnd, "rb", "",\r
5259                      appData.oldSaveStyle ? "gam" : "pgn",\r
5260                      GAME_FILT,\r
5261                      title, &number, fileTitle, NULL);\r
5262   if (f != NULL) {\r
5263     cmailMsgLoaded = FALSE;\r
5264     if (number == 0) {\r
5265       int error = GameListBuild(f);\r
5266       if (error) {\r
5267         DisplayError("Cannot build game list", error);\r
5268       } else if (!ListEmpty(&gameList) &&\r
5269                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5270         GameListPopUp(f, fileTitle);\r
5271         return;\r
5272       }\r
5273       GameListDestroy();\r
5274       number = 1;\r
5275     }\r
5276     LoadGame(f, number, fileTitle, FALSE);\r
5277   }\r
5278 }\r
5279 \r
5280 void UpdateICSWidth(HWND hText)\r
5281 {\r
5282         HDC hdc;\r
5283         TEXTMETRIC tm;\r
5284         RECT rc;\r
5285         HFONT hfont, hold_font;\r
5286         LONG old_width, new_width;\r
5287         \r
5288         // get the text metrics\r
5289         hdc = GetDC(hText);\r
5290         hfont = CreateFontIndirect(&font[boardSize][CONSOLE_FONT]->lf);\r
5291         hold_font = SelectObject(hdc, hfont);\r
5292         GetTextMetrics(hdc, &tm);\r
5293         SelectObject(hdc, hold_font);\r
5294         DeleteObject(hfont);\r
5295         ReleaseDC(hText, hdc);\r
5296 \r
5297         // get the rectangle\r
5298         SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5299 \r
5300         // update the width\r
5301         new_width = (rc.right-rc.left) / tm.tmAveCharWidth;\r
5302         old_width = GetWindowLong(hText, GWL_USERDATA);\r
5303         if (new_width != old_width)\r
5304         {\r
5305                 ics_update_width(new_width);\r
5306                 SetWindowLong(hText, GWL_USERDATA, new_width);\r
5307         }\r
5308 }\r
5309 \r
5310 VOID\r
5311 ChangedConsoleFont()\r
5312 {\r
5313   CHARFORMAT cfmt;\r
5314   CHARRANGE tmpsel, sel;\r
5315   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5316   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5317   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5318   PARAFORMAT paraf;\r
5319 \r
5320   cfmt.cbSize = sizeof(CHARFORMAT);\r
5321   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5322   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5323   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5324    * size.  This was undocumented in the version of MSVC++ that I had\r
5325    * when I wrote the code, but is apparently documented now.\r
5326    */\r
5327   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5328   cfmt.bCharSet = f->lf.lfCharSet;\r
5329   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5330   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5331   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5332   /* Why are the following seemingly needed too? */\r
5333   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5334   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5335   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5336   tmpsel.cpMin = 0;\r
5337   tmpsel.cpMax = -1; /*999999?*/\r
5338   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5339   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5340   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5341    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5342    */\r
5343   paraf.cbSize = sizeof(paraf);\r
5344   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5345   paraf.dxStartIndent = 0;\r
5346   paraf.dxOffset = WRAP_INDENT;\r
5347   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5348   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5349   UpdateICSWidth(hText);\r
5350 }\r
5351 \r
5352 /*---------------------------------------------------------------------------*\\r
5353  *\r
5354  * Window Proc for main window\r
5355  *\r
5356 \*---------------------------------------------------------------------------*/\r
5357 \r
5358 /* Process messages for main window, etc. */\r
5359 LRESULT CALLBACK\r
5360 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5361 {\r
5362   FARPROC lpProc;\r
5363   int wmId, wmEvent;\r
5364   char *defName;\r
5365   FILE *f;\r
5366   UINT number;\r
5367   char fileTitle[MSG_SIZ];\r
5368   char buf[MSG_SIZ];\r
5369   static SnapData sd;\r
5370 \r
5371   switch (message) {\r
5372 \r
5373   case WM_PAINT: /* message: repaint portion of window */\r
5374     PaintProc(hwnd);\r
5375     break;\r
5376 \r
5377   case WM_ERASEBKGND:\r
5378     if (IsIconic(hwnd)) {\r
5379       /* Cheat; change the message */\r
5380       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5381     } else {\r
5382       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5383     }\r
5384     break;\r
5385 \r
5386   case WM_LBUTTONDOWN:\r
5387   case WM_MBUTTONDOWN:\r
5388   case WM_RBUTTONDOWN:\r
5389   case WM_LBUTTONUP:\r
5390   case WM_MBUTTONUP:\r
5391   case WM_RBUTTONUP:\r
5392   case WM_MOUSEMOVE:\r
5393   case WM_MOUSEWHEEL:\r
5394     MouseEvent(hwnd, message, wParam, lParam);\r
5395     break;\r
5396 \r
5397   JAWS_KB_NAVIGATION\r
5398 \r
5399   case WM_CHAR:\r
5400     \r
5401     JAWS_ALT_INTERCEPT\r
5402 \r
5403     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5404         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5405         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5406         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5407         SetFocus(h);\r
5408         SendMessage(h, message, wParam, lParam);\r
5409     } else if(lParam != KF_REPEAT) {\r
5410         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5411                 PopUpMoveDialog((char)wParam);\r
5412         } else if((char)wParam == 003) CopyGameToClipboard();\r
5413          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5414     }\r
5415 \r
5416     break;\r
5417 \r
5418   case WM_PALETTECHANGED:\r
5419     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5420       int nnew;\r
5421       HDC hdc = GetDC(hwndMain);\r
5422       SelectPalette(hdc, hPal, TRUE);\r
5423       nnew = RealizePalette(hdc);\r
5424       if (nnew > 0) {\r
5425         paletteChanged = TRUE;\r
5426         InvalidateRect(hwnd, &boardRect, FALSE);\r
5427       }\r
5428       ReleaseDC(hwnd, hdc);\r
5429     }\r
5430     break;\r
5431 \r
5432   case WM_QUERYNEWPALETTE:\r
5433     if (!appData.monoMode /*&& paletteChanged*/) {\r
5434       int nnew;\r
5435       HDC hdc = GetDC(hwndMain);\r
5436       paletteChanged = FALSE;\r
5437       SelectPalette(hdc, hPal, FALSE);\r
5438       nnew = RealizePalette(hdc);\r
5439       if (nnew > 0) {\r
5440         InvalidateRect(hwnd, &boardRect, FALSE);\r
5441       }\r
5442       ReleaseDC(hwnd, hdc);\r
5443       return TRUE;\r
5444     }\r
5445     return FALSE;\r
5446 \r
5447   case WM_COMMAND: /* message: command from application menu */\r
5448     wmId    = LOWORD(wParam);\r
5449     wmEvent = HIWORD(wParam);\r
5450 \r
5451     switch (wmId) {\r
5452     case IDM_NewGame:\r
5453       ResetGameEvent();\r
5454       SAY("new game enter a move to play against the computer with white");\r
5455       break;\r
5456 \r
5457     case IDM_NewGameFRC:\r
5458       if( NewGameFRC() == 0 ) {\r
5459         ResetGameEvent();\r
5460       }\r
5461       break;\r
5462 \r
5463     case IDM_NewVariant:\r
5464       NewVariantPopup(hwnd);\r
5465       break;\r
5466 \r
5467     case IDM_LoadGame:\r
5468       LoadGameDialog(hwnd, "Load Game from File");\r
5469       break;\r
5470 \r
5471     case IDM_LoadNextGame:\r
5472       ReloadGame(1);\r
5473       break;\r
5474 \r
5475     case IDM_LoadPrevGame:\r
5476       ReloadGame(-1);\r
5477       break;\r
5478 \r
5479     case IDM_ReloadGame:\r
5480       ReloadGame(0);\r
5481       break;\r
5482 \r
5483     case IDM_LoadPosition:\r
5484       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5485         Reset(FALSE, TRUE);\r
5486       }\r
5487       number = 1;\r
5488       f = OpenFileDialog(hwnd, "rb", "",\r
5489                          appData.oldSaveStyle ? "pos" : "fen",\r
5490                          POSITION_FILT,\r
5491                          "Load Position from File", &number, fileTitle, NULL);\r
5492       if (f != NULL) {\r
5493         LoadPosition(f, number, fileTitle);\r
5494       }\r
5495       break;\r
5496 \r
5497     case IDM_LoadNextPosition:\r
5498       ReloadPosition(1);\r
5499       break;\r
5500 \r
5501     case IDM_LoadPrevPosition:\r
5502       ReloadPosition(-1);\r
5503       break;\r
5504 \r
5505     case IDM_ReloadPosition:\r
5506       ReloadPosition(0);\r
5507       break;\r
5508 \r
5509     case IDM_SaveGame:\r
5510       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5511       f = OpenFileDialog(hwnd, "a", defName,\r
5512                          appData.oldSaveStyle ? "gam" : "pgn",\r
5513                          GAME_FILT,\r
5514                          "Save Game to File", NULL, fileTitle, NULL);\r
5515       if (f != NULL) {\r
5516         SaveGame(f, 0, "");\r
5517       }\r
5518       break;\r
5519 \r
5520     case IDM_SavePosition:\r
5521       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5522       f = OpenFileDialog(hwnd, "a", defName,\r
5523                          appData.oldSaveStyle ? "pos" : "fen",\r
5524                          POSITION_FILT,\r
5525                          "Save Position to File", NULL, fileTitle, NULL);\r
5526       if (f != NULL) {\r
5527         SavePosition(f, 0, "");\r
5528       }\r
5529       break;\r
5530 \r
5531     case IDM_SaveDiagram:\r
5532       defName = "diagram";\r
5533       f = OpenFileDialog(hwnd, "wb", defName,\r
5534                          "bmp",\r
5535                          DIAGRAM_FILT,\r
5536                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5537       if (f != NULL) {\r
5538         SaveDiagram(f);\r
5539       }\r
5540       break;\r
5541 \r
5542     case IDM_CopyGame:\r
5543       CopyGameToClipboard();\r
5544       break;\r
5545 \r
5546     case IDM_PasteGame:\r
5547       PasteGameFromClipboard();\r
5548       break;\r
5549 \r
5550     case IDM_CopyGameListToClipboard:\r
5551       CopyGameListToClipboard();\r
5552       break;\r
5553 \r
5554     /* [AS] Autodetect FEN or PGN data */\r
5555     case IDM_PasteAny:\r
5556       PasteGameOrFENFromClipboard();\r
5557       break;\r
5558 \r
5559     /* [AS] Move history */\r
5560     case IDM_ShowMoveHistory:\r
5561         if( MoveHistoryIsUp() ) {\r
5562             MoveHistoryPopDown();\r
5563         }\r
5564         else {\r
5565             MoveHistoryPopUp();\r
5566         }\r
5567         break;\r
5568 \r
5569     /* [AS] Eval graph */\r
5570     case IDM_ShowEvalGraph:\r
5571         if( EvalGraphIsUp() ) {\r
5572             EvalGraphPopDown();\r
5573         }\r
5574         else {\r
5575             EvalGraphPopUp();\r
5576             SetFocus(hwndMain);\r
5577         }\r
5578         break;\r
5579 \r
5580     /* [AS] Engine output */\r
5581     case IDM_ShowEngineOutput:\r
5582         if( EngineOutputIsUp() ) {\r
5583             EngineOutputPopDown();\r
5584         }\r
5585         else {\r
5586             EngineOutputPopUp();\r
5587         }\r
5588         break;\r
5589 \r
5590     /* [AS] User adjudication */\r
5591     case IDM_UserAdjudication_White:\r
5592         UserAdjudicationEvent( +1 );\r
5593         break;\r
5594 \r
5595     case IDM_UserAdjudication_Black:\r
5596         UserAdjudicationEvent( -1 );\r
5597         break;\r
5598 \r
5599     case IDM_UserAdjudication_Draw:\r
5600         UserAdjudicationEvent( 0 );\r
5601         break;\r
5602 \r
5603     /* [AS] Game list options dialog */\r
5604     case IDM_GameListOptions:\r
5605       GameListOptions();\r
5606       break;\r
5607 \r
5608     case IDM_NewChat:\r
5609       ChatPopUp();\r
5610       break;\r
5611 \r
5612     case IDM_CopyPosition:\r
5613       CopyFENToClipboard();\r
5614       break;\r
5615 \r
5616     case IDM_PastePosition:\r
5617       PasteFENFromClipboard();\r
5618       break;\r
5619 \r
5620     case IDM_MailMove:\r
5621       MailMoveEvent();\r
5622       break;\r
5623 \r
5624     case IDM_ReloadCMailMsg:\r
5625       Reset(TRUE, TRUE);\r
5626       ReloadCmailMsgEvent(FALSE);\r
5627       break;\r
5628 \r
5629     case IDM_Minimize:\r
5630       ShowWindow(hwnd, SW_MINIMIZE);\r
5631       break;\r
5632 \r
5633     case IDM_Exit:\r
5634       ExitEvent(0);\r
5635       break;\r
5636 \r
5637     case IDM_MachineWhite:\r
5638       MachineWhiteEvent();\r
5639       /*\r
5640        * refresh the tags dialog only if it's visible\r
5641        */\r
5642       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5643           char *tags;\r
5644           tags = PGNTags(&gameInfo);\r
5645           TagsPopUp(tags, CmailMsg());\r
5646           free(tags);\r
5647       }\r
5648       SAY("computer starts playing white");\r
5649       break;\r
5650 \r
5651     case IDM_MachineBlack:\r
5652       MachineBlackEvent();\r
5653       /*\r
5654        * refresh the tags dialog only if it's visible\r
5655        */\r
5656       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5657           char *tags;\r
5658           tags = PGNTags(&gameInfo);\r
5659           TagsPopUp(tags, CmailMsg());\r
5660           free(tags);\r
5661       }\r
5662       SAY("computer starts playing black");\r
5663       break;\r
5664 \r
5665     case IDM_TwoMachines:\r
5666       TwoMachinesEvent();\r
5667       /*\r
5668        * refresh the tags dialog only if it's visible\r
5669        */\r
5670       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5671           char *tags;\r
5672           tags = PGNTags(&gameInfo);\r
5673           TagsPopUp(tags, CmailMsg());\r
5674           free(tags);\r
5675       }\r
5676       SAY("programs start playing each other");\r
5677       break;\r
5678 \r
5679     case IDM_AnalysisMode:\r
5680       if (!first.analysisSupport) {\r
5681         sprintf(buf, "%s does not support analysis", first.tidy);\r
5682         DisplayError(buf, 0);\r
5683       } else {\r
5684         SAY("analyzing current position");\r
5685         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5686         if (appData.icsActive) {\r
5687                if (gameMode != IcsObserving) {\r
5688                        sprintf(buf, "You are not observing a game");\r
5689                        DisplayError(buf, 0);\r
5690                        /* secure check */\r
5691                        if (appData.icsEngineAnalyze) {\r
5692                                if (appData.debugMode) \r
5693                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5694                                ExitAnalyzeMode();\r
5695                                ModeHighlight();\r
5696                                break;\r
5697                        }\r
5698                        break;\r
5699                } else {\r
5700                        /* if enable, user want disable icsEngineAnalyze */\r
5701                        if (appData.icsEngineAnalyze) {\r
5702                                ExitAnalyzeMode();\r
5703                                ModeHighlight();\r
5704                                break;\r
5705                        }\r
5706                        appData.icsEngineAnalyze = TRUE;\r
5707                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5708                }\r
5709         } \r
5710         if (!appData.showThinking) ToggleShowThinking();\r
5711         AnalyzeModeEvent();\r
5712       }\r
5713       break;\r
5714 \r
5715     case IDM_AnalyzeFile:\r
5716       if (!first.analysisSupport) {\r
5717         char buf[MSG_SIZ];\r
5718         sprintf(buf, "%s does not support analysis", first.tidy);\r
5719         DisplayError(buf, 0);\r
5720       } else {\r
5721         if (!appData.showThinking) ToggleShowThinking();\r
5722         AnalyzeFileEvent();\r
5723         LoadGameDialog(hwnd, "Analyze Game from File");\r
5724         AnalysisPeriodicEvent(1);\r
5725       }\r
5726       break;\r
5727 \r
5728     case IDM_IcsClient:\r
5729       IcsClientEvent();\r
5730       break;\r
5731 \r
5732     case IDM_EditGame:\r
5733       EditGameEvent();\r
5734       SAY("edit game");\r
5735       break;\r
5736 \r
5737     case IDM_EditPosition:\r
5738       EditPositionEvent();\r
5739       SAY("to set up a position type a FEN");\r
5740       break;\r
5741 \r
5742     case IDM_Training:\r
5743       TrainingEvent();\r
5744       break;\r
5745 \r
5746     case IDM_ShowGameList:\r
5747       ShowGameListProc();\r
5748       break;\r
5749 \r
5750     case IDM_EditTags:\r
5751       EditTagsProc();\r
5752       break;\r
5753 \r
5754     case IDM_EditComment:\r
5755       if (commentDialogUp && editComment) {\r
5756         CommentPopDown();\r
5757       } else {\r
5758         EditCommentEvent();\r
5759       }\r
5760       break;\r
5761 \r
5762     case IDM_Pause:\r
5763       PauseEvent();\r
5764       break;\r
5765 \r
5766     case IDM_Accept:\r
5767       AcceptEvent();\r
5768       break;\r
5769 \r
5770     case IDM_Decline:\r
5771       DeclineEvent();\r
5772       break;\r
5773 \r
5774     case IDM_Rematch:\r
5775       RematchEvent();\r
5776       break;\r
5777 \r
5778     case IDM_CallFlag:\r
5779       CallFlagEvent();\r
5780       break;\r
5781 \r
5782     case IDM_Draw:\r
5783       DrawEvent();\r
5784       break;\r
5785 \r
5786     case IDM_Adjourn:\r
5787       AdjournEvent();\r
5788       break;\r
5789 \r
5790     case IDM_Abort:\r
5791       AbortEvent();\r
5792       break;\r
5793 \r
5794     case IDM_Resign:\r
5795       ResignEvent();\r
5796       break;\r
5797 \r
5798     case IDM_StopObserving:\r
5799       StopObservingEvent();\r
5800       break;\r
5801 \r
5802     case IDM_StopExamining:\r
5803       StopExaminingEvent();\r
5804       break;\r
5805 \r
5806     case IDM_TypeInMove:\r
5807       PopUpMoveDialog('\000');\r
5808       break;\r
5809 \r
5810     case IDM_TypeInName:\r
5811       PopUpNameDialog('\000');\r
5812       break;\r
5813 \r
5814     case IDM_Backward:\r
5815       BackwardEvent();\r
5816       SetFocus(hwndMain);\r
5817       break;\r
5818 \r
5819     JAWS_MENU_ITEMS\r
5820 \r
5821     case IDM_Forward:\r
5822       ForwardEvent();\r
5823       SetFocus(hwndMain);\r
5824       break;\r
5825 \r
5826     case IDM_ToStart:\r
5827       ToStartEvent();\r
5828       SetFocus(hwndMain);\r
5829       break;\r
5830 \r
5831     case IDM_ToEnd:\r
5832       ToEndEvent();\r
5833       SetFocus(hwndMain);\r
5834       break;\r
5835 \r
5836     case IDM_Revert:\r
5837       RevertEvent();\r
5838       break;\r
5839 \r
5840     case IDM_TruncateGame:\r
5841       TruncateGameEvent();\r
5842       break;\r
5843 \r
5844     case IDM_MoveNow:\r
5845       MoveNowEvent();\r
5846       break;\r
5847 \r
5848     case IDM_RetractMove:\r
5849       RetractMoveEvent();\r
5850       break;\r
5851 \r
5852     case IDM_FlipView:\r
5853       flipView = !flipView;\r
5854       DrawPosition(FALSE, NULL);\r
5855       break;\r
5856 \r
5857     case IDM_FlipClock:\r
5858       flipClock = !flipClock;\r
5859       DisplayBothClocks();\r
5860       DrawPosition(FALSE, NULL);\r
5861       break;\r
5862 \r
5863     case IDM_MuteSounds:\r
5864       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5865       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5866                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5867       break;\r
5868 \r
5869     case IDM_GeneralOptions:\r
5870       GeneralOptionsPopup(hwnd);\r
5871       DrawPosition(TRUE, NULL);\r
5872       break;\r
5873 \r
5874     case IDM_BoardOptions:\r
5875       BoardOptionsPopup(hwnd);\r
5876       break;\r
5877 \r
5878     case IDM_EnginePlayOptions:\r
5879       EnginePlayOptionsPopup(hwnd);\r
5880       break;\r
5881 \r
5882     case IDM_Engine1Options:\r
5883       EngineOptionsPopup(hwnd, &first);\r
5884       break;\r
5885 \r
5886     case IDM_Engine2Options:\r
5887       EngineOptionsPopup(hwnd, &second);\r
5888       break;\r
5889 \r
5890     case IDM_OptionsUCI:\r
5891       UciOptionsPopup(hwnd);\r
5892       break;\r
5893 \r
5894     case IDM_IcsOptions:\r
5895       IcsOptionsPopup(hwnd);\r
5896       break;\r
5897 \r
5898     case IDM_Fonts:\r
5899       FontsOptionsPopup(hwnd);\r
5900       break;\r
5901 \r
5902     case IDM_Sounds:\r
5903       SoundOptionsPopup(hwnd);\r
5904       break;\r
5905 \r
5906     case IDM_CommPort:\r
5907       CommPortOptionsPopup(hwnd);\r
5908       break;\r
5909 \r
5910     case IDM_LoadOptions:\r
5911       LoadOptionsPopup(hwnd);\r
5912       break;\r
5913 \r
5914     case IDM_SaveOptions:\r
5915       SaveOptionsPopup(hwnd);\r
5916       break;\r
5917 \r
5918     case IDM_TimeControl:\r
5919       TimeControlOptionsPopup(hwnd);\r
5920       break;\r
5921 \r
5922     case IDM_SaveSettings:\r
5923       SaveSettings(settingsFileName);\r
5924       break;\r
5925 \r
5926     case IDM_SaveSettingsOnExit:\r
5927       saveSettingsOnExit = !saveSettingsOnExit;\r
5928       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5929                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5930                                          MF_CHECKED : MF_UNCHECKED));\r
5931       break;\r
5932 \r
5933     case IDM_Hint:\r
5934       HintEvent();\r
5935       break;\r
5936 \r
5937     case IDM_Book:\r
5938       BookEvent();\r
5939       break;\r
5940 \r
5941     case IDM_AboutGame:\r
5942       AboutGameEvent();\r
5943       break;\r
5944 \r
5945     case IDM_Debug:\r
5946       appData.debugMode = !appData.debugMode;\r
5947       if (appData.debugMode) {\r
5948         char dir[MSG_SIZ];\r
5949         GetCurrentDirectory(MSG_SIZ, dir);\r
5950         SetCurrentDirectory(installDir);\r
5951         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5952         SetCurrentDirectory(dir);\r
5953         setbuf(debugFP, NULL);\r
5954       } else {\r
5955         fclose(debugFP);\r
5956         debugFP = NULL;\r
5957       }\r
5958       break;\r
5959 \r
5960     case IDM_HELPCONTENTS:\r
5961       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5962           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5963           MessageBox (GetFocus(),\r
5964                     "Unable to activate help",\r
5965                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5966       }\r
5967       break;\r
5968 \r
5969     case IDM_HELPSEARCH:\r
5970         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5971             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5972         MessageBox (GetFocus(),\r
5973                     "Unable to activate help",\r
5974                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5975       }\r
5976       break;\r
5977 \r
5978     case IDM_HELPHELP:\r
5979       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5980         MessageBox (GetFocus(),\r
5981                     "Unable to activate help",\r
5982                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5983       }\r
5984       break;\r
5985 \r
5986     case IDM_ABOUT:\r
5987       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5988       DialogBox(hInst, \r
5989         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5990         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5991       FreeProcInstance(lpProc);\r
5992       break;\r
5993 \r
5994     case IDM_DirectCommand1:\r
5995       AskQuestionEvent("Direct Command",\r
5996                        "Send to chess program:", "", "1");\r
5997       break;\r
5998     case IDM_DirectCommand2:\r
5999       AskQuestionEvent("Direct Command",\r
6000                        "Send to second chess program:", "", "2");\r
6001       break;\r
6002 \r
6003     case EP_WhitePawn:\r
6004       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6005       fromX = fromY = -1;\r
6006       break;\r
6007 \r
6008     case EP_WhiteKnight:\r
6009       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6010       fromX = fromY = -1;\r
6011       break;\r
6012 \r
6013     case EP_WhiteBishop:\r
6014       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6015       fromX = fromY = -1;\r
6016       break;\r
6017 \r
6018     case EP_WhiteRook:\r
6019       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6020       fromX = fromY = -1;\r
6021       break;\r
6022 \r
6023     case EP_WhiteQueen:\r
6024       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6025       fromX = fromY = -1;\r
6026       break;\r
6027 \r
6028     case EP_WhiteFerz:\r
6029       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6030       fromX = fromY = -1;\r
6031       break;\r
6032 \r
6033     case EP_WhiteWazir:\r
6034       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6035       fromX = fromY = -1;\r
6036       break;\r
6037 \r
6038     case EP_WhiteAlfil:\r
6039       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6040       fromX = fromY = -1;\r
6041       break;\r
6042 \r
6043     case EP_WhiteCannon:\r
6044       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6045       fromX = fromY = -1;\r
6046       break;\r
6047 \r
6048     case EP_WhiteCardinal:\r
6049       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6050       fromX = fromY = -1;\r
6051       break;\r
6052 \r
6053     case EP_WhiteMarshall:\r
6054       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6055       fromX = fromY = -1;\r
6056       break;\r
6057 \r
6058     case EP_WhiteKing:\r
6059       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6060       fromX = fromY = -1;\r
6061       break;\r
6062 \r
6063     case EP_BlackPawn:\r
6064       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6065       fromX = fromY = -1;\r
6066       break;\r
6067 \r
6068     case EP_BlackKnight:\r
6069       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6070       fromX = fromY = -1;\r
6071       break;\r
6072 \r
6073     case EP_BlackBishop:\r
6074       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6075       fromX = fromY = -1;\r
6076       break;\r
6077 \r
6078     case EP_BlackRook:\r
6079       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6080       fromX = fromY = -1;\r
6081       break;\r
6082 \r
6083     case EP_BlackQueen:\r
6084       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6085       fromX = fromY = -1;\r
6086       break;\r
6087 \r
6088     case EP_BlackFerz:\r
6089       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6090       fromX = fromY = -1;\r
6091       break;\r
6092 \r
6093     case EP_BlackWazir:\r
6094       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6095       fromX = fromY = -1;\r
6096       break;\r
6097 \r
6098     case EP_BlackAlfil:\r
6099       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6100       fromX = fromY = -1;\r
6101       break;\r
6102 \r
6103     case EP_BlackCannon:\r
6104       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6105       fromX = fromY = -1;\r
6106       break;\r
6107 \r
6108     case EP_BlackCardinal:\r
6109       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6110       fromX = fromY = -1;\r
6111       break;\r
6112 \r
6113     case EP_BlackMarshall:\r
6114       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6115       fromX = fromY = -1;\r
6116       break;\r
6117 \r
6118     case EP_BlackKing:\r
6119       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6120       fromX = fromY = -1;\r
6121       break;\r
6122 \r
6123     case EP_EmptySquare:\r
6124       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6125       fromX = fromY = -1;\r
6126       break;\r
6127 \r
6128     case EP_ClearBoard:\r
6129       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6130       fromX = fromY = -1;\r
6131       break;\r
6132 \r
6133     case EP_White:\r
6134       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6135       fromX = fromY = -1;\r
6136       break;\r
6137 \r
6138     case EP_Black:\r
6139       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6140       fromX = fromY = -1;\r
6141       break;\r
6142 \r
6143     case EP_Promote:\r
6144       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6145       fromX = fromY = -1;\r
6146       break;\r
6147 \r
6148     case EP_Demote:\r
6149       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6150       fromX = fromY = -1;\r
6151       break;\r
6152 \r
6153     case DP_Pawn:\r
6154       DropMenuEvent(WhitePawn, fromX, fromY);\r
6155       fromX = fromY = -1;\r
6156       break;\r
6157 \r
6158     case DP_Knight:\r
6159       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6160       fromX = fromY = -1;\r
6161       break;\r
6162 \r
6163     case DP_Bishop:\r
6164       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6165       fromX = fromY = -1;\r
6166       break;\r
6167 \r
6168     case DP_Rook:\r
6169       DropMenuEvent(WhiteRook, fromX, fromY);\r
6170       fromX = fromY = -1;\r
6171       break;\r
6172 \r
6173     case DP_Queen:\r
6174       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6175       fromX = fromY = -1;\r
6176       break;\r
6177 \r
6178     default:\r
6179       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6180     }\r
6181     break;\r
6182 \r
6183   case WM_TIMER:\r
6184     switch (wParam) {\r
6185     case CLOCK_TIMER_ID:\r
6186       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6187       clockTimerEvent = 0;\r
6188       DecrementClocks(); /* call into back end */\r
6189       break;\r
6190     case LOAD_GAME_TIMER_ID:\r
6191       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6192       loadGameTimerEvent = 0;\r
6193       AutoPlayGameLoop(); /* call into back end */\r
6194       break;\r
6195     case ANALYSIS_TIMER_ID:\r
6196       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6197                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6198         AnalysisPeriodicEvent(0);\r
6199       } else {\r
6200         KillTimer(hwnd, analysisTimerEvent);\r
6201         analysisTimerEvent = 0;\r
6202       }\r
6203       break;\r
6204     case DELAYED_TIMER_ID:\r
6205       KillTimer(hwnd, delayedTimerEvent);\r
6206       delayedTimerEvent = 0;\r
6207       delayedTimerCallback();\r
6208       break;\r
6209     }\r
6210     break;\r
6211 \r
6212   case WM_USER_Input:\r
6213     InputEvent(hwnd, message, wParam, lParam);\r
6214     break;\r
6215 \r
6216   /* [AS] Also move "attached" child windows */\r
6217   case WM_WINDOWPOSCHANGING:\r
6218 \r
6219     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6220         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6221 \r
6222         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6223             /* Window is moving */\r
6224             RECT rcMain;\r
6225 \r
6226 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6227             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6228             rcMain.right  = boardX + winWidth;\r
6229             rcMain.top    = boardY;\r
6230             rcMain.bottom = boardY + winHeight;\r
6231             \r
6232             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6233             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6234             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6235             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6236             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6237             boardX = lpwp->x;\r
6238             boardY = lpwp->y;\r
6239         }\r
6240     }\r
6241     break;\r
6242 \r
6243   /* [AS] Snapping */\r
6244   case WM_ENTERSIZEMOVE:\r
6245     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6246     if (hwnd == hwndMain) {\r
6247       doingSizing = TRUE;\r
6248       lastSizing = 0;\r
6249     }\r
6250     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6251     break;\r
6252 \r
6253   case WM_SIZING:\r
6254     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6255     if (hwnd == hwndMain) {\r
6256       lastSizing = wParam;\r
6257     }\r
6258     break;\r
6259 \r
6260   case WM_MOVING:\r
6261     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6262       return OnMoving( &sd, hwnd, wParam, lParam );\r
6263 \r
6264   case WM_EXITSIZEMOVE:\r
6265     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6266     if (hwnd == hwndMain) {\r
6267       RECT client;\r
6268       doingSizing = FALSE;\r
6269       InvalidateRect(hwnd, &boardRect, FALSE);\r
6270       GetClientRect(hwnd, &client);\r
6271       ResizeBoard(client.right, client.bottom, lastSizing);\r
6272       lastSizing = 0;\r
6273       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6274     }\r
6275     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6276     break;\r
6277 \r
6278   case WM_DESTROY: /* message: window being destroyed */\r
6279     PostQuitMessage(0);\r
6280     break;\r
6281 \r
6282   case WM_CLOSE:\r
6283     if (hwnd == hwndMain) {\r
6284       ExitEvent(0);\r
6285     }\r
6286     break;\r
6287 \r
6288   default:      /* Passes it on if unprocessed */\r
6289     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6290   }\r
6291   return 0;\r
6292 }\r
6293 \r
6294 /*---------------------------------------------------------------------------*\\r
6295  *\r
6296  * Misc utility routines\r
6297  *\r
6298 \*---------------------------------------------------------------------------*/\r
6299 \r
6300 /*\r
6301  * Decent random number generator, at least not as bad as Windows\r
6302  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6303  */\r
6304 unsigned int randstate;\r
6305 \r
6306 int\r
6307 myrandom(void)\r
6308 {\r
6309   randstate = randstate * 1664525 + 1013904223;\r
6310   return (int) randstate & 0x7fffffff;\r
6311 }\r
6312 \r
6313 void\r
6314 mysrandom(unsigned int seed)\r
6315 {\r
6316   randstate = seed;\r
6317 }\r
6318 \r
6319 \r
6320 /* \r
6321  * returns TRUE if user selects a different color, FALSE otherwise \r
6322  */\r
6323 \r
6324 BOOL\r
6325 ChangeColor(HWND hwnd, COLORREF *which)\r
6326 {\r
6327   static BOOL firstTime = TRUE;\r
6328   static DWORD customColors[16];\r
6329   CHOOSECOLOR cc;\r
6330   COLORREF newcolor;\r
6331   int i;\r
6332   ColorClass ccl;\r
6333 \r
6334   if (firstTime) {\r
6335     /* Make initial colors in use available as custom colors */\r
6336     /* Should we put the compiled-in defaults here instead? */\r
6337     i = 0;\r
6338     customColors[i++] = lightSquareColor & 0xffffff;\r
6339     customColors[i++] = darkSquareColor & 0xffffff;\r
6340     customColors[i++] = whitePieceColor & 0xffffff;\r
6341     customColors[i++] = blackPieceColor & 0xffffff;\r
6342     customColors[i++] = highlightSquareColor & 0xffffff;\r
6343     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6344 \r
6345     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6346       customColors[i++] = textAttribs[ccl].color;\r
6347     }\r
6348     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6349     firstTime = FALSE;\r
6350   }\r
6351 \r
6352   cc.lStructSize = sizeof(cc);\r
6353   cc.hwndOwner = hwnd;\r
6354   cc.hInstance = NULL;\r
6355   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6356   cc.lpCustColors = (LPDWORD) customColors;\r
6357   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6358 \r
6359   if (!ChooseColor(&cc)) return FALSE;\r
6360 \r
6361   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6362   if (newcolor == *which) return FALSE;\r
6363   *which = newcolor;\r
6364   return TRUE;\r
6365 \r
6366   /*\r
6367   InitDrawingColors();\r
6368   InvalidateRect(hwnd, &boardRect, FALSE);\r
6369   */\r
6370 }\r
6371 \r
6372 BOOLEAN\r
6373 MyLoadSound(MySound *ms)\r
6374 {\r
6375   BOOL ok = FALSE;\r
6376   struct stat st;\r
6377   FILE *f;\r
6378 \r
6379   if (ms->data) free(ms->data);\r
6380   ms->data = NULL;\r
6381 \r
6382   switch (ms->name[0]) {\r
6383   case NULLCHAR:\r
6384     /* Silence */\r
6385     ok = TRUE;\r
6386     break;\r
6387   case '$':\r
6388     /* System sound from Control Panel.  Don't preload here. */\r
6389     ok = TRUE;\r
6390     break;\r
6391   case '!':\r
6392     if (ms->name[1] == NULLCHAR) {\r
6393       /* "!" alone = silence */\r
6394       ok = TRUE;\r
6395     } else {\r
6396       /* Builtin wave resource.  Error if not found. */\r
6397       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6398       if (h == NULL) break;\r
6399       ms->data = (void *)LoadResource(hInst, h);\r
6400       if (h == NULL) break;\r
6401       ok = TRUE;\r
6402     }\r
6403     break;\r
6404   default:\r
6405     /* .wav file.  Error if not found. */\r
6406     f = fopen(ms->name, "rb");\r
6407     if (f == NULL) break;\r
6408     if (fstat(fileno(f), &st) < 0) break;\r
6409     ms->data = malloc(st.st_size);\r
6410     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6411     fclose(f);\r
6412     ok = TRUE;\r
6413     break;\r
6414   }\r
6415   if (!ok) {\r
6416     char buf[MSG_SIZ];\r
6417     sprintf(buf, "Error loading sound %s", ms->name);\r
6418     DisplayError(buf, GetLastError());\r
6419   }\r
6420   return ok;\r
6421 }\r
6422 \r
6423 BOOLEAN\r
6424 MyPlaySound(MySound *ms)\r
6425 {\r
6426   BOOLEAN ok = FALSE;\r
6427 \r
6428   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6429   switch (ms->name[0]) {\r
6430   case NULLCHAR:\r
6431         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6432     /* Silence */\r
6433     ok = TRUE;\r
6434     break;\r
6435   case '$':\r
6436     /* System sound from Control Panel (deprecated feature).\r
6437        "$" alone or an unset sound name gets default beep (still in use). */\r
6438     if (ms->name[1]) {\r
6439       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6440     }\r
6441     if (!ok) ok = MessageBeep(MB_OK);\r
6442     break; \r
6443   case '!':\r
6444     /* Builtin wave resource, or "!" alone for silence */\r
6445     if (ms->name[1]) {\r
6446       if (ms->data == NULL) return FALSE;\r
6447       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6448     } else {\r
6449       ok = TRUE;\r
6450     }\r
6451     break;\r
6452   default:\r
6453     /* .wav file.  Error if not found. */\r
6454     if (ms->data == NULL) return FALSE;\r
6455     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6456     break;\r
6457   }\r
6458   /* Don't print an error: this can happen innocently if the sound driver\r
6459      is busy; for instance, if another instance of WinBoard is playing\r
6460      a sound at about the same time. */\r
6461   return ok;\r
6462 }\r
6463 \r
6464 \r
6465 LRESULT CALLBACK\r
6466 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6467 {\r
6468   BOOL ok;\r
6469   OPENFILENAME *ofn;\r
6470   static UINT *number; /* gross that this is static */\r
6471 \r
6472   switch (message) {\r
6473   case WM_INITDIALOG: /* message: initialize dialog box */\r
6474     /* Center the dialog over the application window */\r
6475     ofn = (OPENFILENAME *) lParam;\r
6476     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6477       number = (UINT *) ofn->lCustData;\r
6478       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6479     } else {\r
6480       number = NULL;\r
6481     }\r
6482     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6483     return FALSE;  /* Allow for further processing */\r
6484 \r
6485   case WM_COMMAND:\r
6486     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6487       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6488     }\r
6489     return FALSE;  /* Allow for further processing */\r
6490   }\r
6491   return FALSE;\r
6492 }\r
6493 \r
6494 UINT APIENTRY\r
6495 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6496 {\r
6497   static UINT *number;\r
6498   OPENFILENAME *ofname;\r
6499   OFNOTIFY *ofnot;\r
6500   switch (uiMsg) {\r
6501   case WM_INITDIALOG:\r
6502     ofname = (OPENFILENAME *)lParam;\r
6503     number = (UINT *)(ofname->lCustData);\r
6504     break;\r
6505   case WM_NOTIFY:\r
6506     ofnot = (OFNOTIFY *)lParam;\r
6507     if (ofnot->hdr.code == CDN_FILEOK) {\r
6508       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6509     }\r
6510     break;\r
6511   }\r
6512   return 0;\r
6513 }\r
6514 \r
6515 \r
6516 FILE *\r
6517 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6518                char *nameFilt, char *dlgTitle, UINT *number,\r
6519                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6520 {\r
6521   OPENFILENAME openFileName;\r
6522   char buf1[MSG_SIZ];\r
6523   FILE *f;\r
6524 \r
6525   if (fileName == NULL) fileName = buf1;\r
6526   if (defName == NULL) {\r
6527     strcpy(fileName, "*.");\r
6528     strcat(fileName, defExt);\r
6529   } else {\r
6530     strcpy(fileName, defName);\r
6531   }\r
6532   if (fileTitle) strcpy(fileTitle, "");\r
6533   if (number) *number = 0;\r
6534 \r
6535   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6536   openFileName.hwndOwner         = hwnd;\r
6537   openFileName.hInstance         = (HANDLE) hInst;\r
6538   openFileName.lpstrFilter       = nameFilt;\r
6539   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6540   openFileName.nMaxCustFilter    = 0L;\r
6541   openFileName.nFilterIndex      = 1L;\r
6542   openFileName.lpstrFile         = fileName;\r
6543   openFileName.nMaxFile          = MSG_SIZ;\r
6544   openFileName.lpstrFileTitle    = fileTitle;\r
6545   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6546   openFileName.lpstrInitialDir   = NULL;\r
6547   openFileName.lpstrTitle        = dlgTitle;\r
6548   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6549     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6550     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6551     | (oldDialog ? 0 : OFN_EXPLORER);\r
6552   openFileName.nFileOffset       = 0;\r
6553   openFileName.nFileExtension    = 0;\r
6554   openFileName.lpstrDefExt       = defExt;\r
6555   openFileName.lCustData         = (LONG) number;\r
6556   openFileName.lpfnHook          = oldDialog ?\r
6557     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6558   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6559 \r
6560   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6561                         GetOpenFileName(&openFileName)) {\r
6562     /* open the file */\r
6563     f = fopen(openFileName.lpstrFile, write);\r
6564     if (f == NULL) {\r
6565       MessageBox(hwnd, "File open failed", NULL,\r
6566                  MB_OK|MB_ICONEXCLAMATION);\r
6567       return NULL;\r
6568     }\r
6569   } else {\r
6570     int err = CommDlgExtendedError();\r
6571     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6572     return FALSE;\r
6573   }\r
6574   return f;\r
6575 }\r
6576 \r
6577 \r
6578 \r
6579 VOID APIENTRY\r
6580 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6581 {\r
6582   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6583 \r
6584   /*\r
6585    * Get the first pop-up menu in the menu template. This is the\r
6586    * menu that TrackPopupMenu displays.\r
6587    */\r
6588   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6589 \r
6590   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6591 \r
6592   /*\r
6593    * TrackPopup uses screen coordinates, so convert the\r
6594    * coordinates of the mouse click to screen coordinates.\r
6595    */\r
6596   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6597 \r
6598   /* Draw and track the floating pop-up menu. */\r
6599   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6600                  pt.x, pt.y, 0, hwnd, NULL);\r
6601 \r
6602   /* Destroy the menu.*/\r
6603   DestroyMenu(hmenu);\r
6604 }\r
6605    \r
6606 typedef struct {\r
6607   HWND hDlg, hText;\r
6608   int sizeX, sizeY, newSizeX, newSizeY;\r
6609   HDWP hdwp;\r
6610 } ResizeEditPlusButtonsClosure;\r
6611 \r
6612 BOOL CALLBACK\r
6613 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6614 {\r
6615   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6616   RECT rect;\r
6617   POINT pt;\r
6618 \r
6619   if (hChild == cl->hText) return TRUE;\r
6620   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6621   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6622   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6623   ScreenToClient(cl->hDlg, &pt);\r
6624   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6625     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6626   return TRUE;\r
6627 }\r
6628 \r
6629 /* Resize a dialog that has a (rich) edit field filling most of\r
6630    the top, with a row of buttons below */\r
6631 VOID\r
6632 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6633 {\r
6634   RECT rectText;\r
6635   int newTextHeight, newTextWidth;\r
6636   ResizeEditPlusButtonsClosure cl;\r
6637   \r
6638   /*if (IsIconic(hDlg)) return;*/\r
6639   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6640   \r
6641   cl.hdwp = BeginDeferWindowPos(8);\r
6642 \r
6643   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6644   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6645   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6646   if (newTextHeight < 0) {\r
6647     newSizeY += -newTextHeight;\r
6648     newTextHeight = 0;\r
6649   }\r
6650   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6651     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6652 \r
6653   cl.hDlg = hDlg;\r
6654   cl.hText = hText;\r
6655   cl.sizeX = sizeX;\r
6656   cl.sizeY = sizeY;\r
6657   cl.newSizeX = newSizeX;\r
6658   cl.newSizeY = newSizeY;\r
6659   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6660 \r
6661   EndDeferWindowPos(cl.hdwp);\r
6662 }\r
6663 \r
6664 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6665 {\r
6666     RECT    rChild, rParent;\r
6667     int     wChild, hChild, wParent, hParent;\r
6668     int     wScreen, hScreen, xNew, yNew;\r
6669     HDC     hdc;\r
6670 \r
6671     /* Get the Height and Width of the child window */\r
6672     GetWindowRect (hwndChild, &rChild);\r
6673     wChild = rChild.right - rChild.left;\r
6674     hChild = rChild.bottom - rChild.top;\r
6675 \r
6676     /* Get the Height and Width of the parent window */\r
6677     GetWindowRect (hwndParent, &rParent);\r
6678     wParent = rParent.right - rParent.left;\r
6679     hParent = rParent.bottom - rParent.top;\r
6680 \r
6681     /* Get the display limits */\r
6682     hdc = GetDC (hwndChild);\r
6683     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6684     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6685     ReleaseDC(hwndChild, hdc);\r
6686 \r
6687     /* Calculate new X position, then adjust for screen */\r
6688     xNew = rParent.left + ((wParent - wChild) /2);\r
6689     if (xNew < 0) {\r
6690         xNew = 0;\r
6691     } else if ((xNew+wChild) > wScreen) {\r
6692         xNew = wScreen - wChild;\r
6693     }\r
6694 \r
6695     /* Calculate new Y position, then adjust for screen */\r
6696     if( mode == 0 ) {\r
6697         yNew = rParent.top  + ((hParent - hChild) /2);\r
6698     }\r
6699     else {\r
6700         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6701     }\r
6702 \r
6703     if (yNew < 0) {\r
6704         yNew = 0;\r
6705     } else if ((yNew+hChild) > hScreen) {\r
6706         yNew = hScreen - hChild;\r
6707     }\r
6708 \r
6709     /* Set it, and return */\r
6710     return SetWindowPos (hwndChild, NULL,\r
6711                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6712 }\r
6713 \r
6714 /* Center one window over another */\r
6715 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6716 {\r
6717     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6718 }\r
6719 \r
6720 /*---------------------------------------------------------------------------*\\r
6721  *\r
6722  * Startup Dialog functions\r
6723  *\r
6724 \*---------------------------------------------------------------------------*/\r
6725 void\r
6726 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6727 {\r
6728   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6729 \r
6730   while (*cd != NULL) {\r
6731     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6732     cd++;\r
6733   }\r
6734 }\r
6735 \r
6736 void\r
6737 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6738 {\r
6739   char buf1[ARG_MAX];\r
6740   int len;\r
6741 \r
6742   if (str[0] == '@') {\r
6743     FILE* f = fopen(str + 1, "r");\r
6744     if (f == NULL) {\r
6745       DisplayFatalError(str + 1, errno, 2);\r
6746       return;\r
6747     }\r
6748     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6749     fclose(f);\r
6750     buf1[len] = NULLCHAR;\r
6751     str = buf1;\r
6752   }\r
6753 \r
6754   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6755 \r
6756   for (;;) {\r
6757     char buf[MSG_SIZ];\r
6758     char *end = strchr(str, '\n');\r
6759     if (end == NULL) return;\r
6760     memcpy(buf, str, end - str);\r
6761     buf[end - str] = NULLCHAR;\r
6762     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6763     str = end + 1;\r
6764   }\r
6765 }\r
6766 \r
6767 void\r
6768 SetStartupDialogEnables(HWND hDlg)\r
6769 {\r
6770   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6771     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6772     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6773   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6774     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6775   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6776     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6777   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6778     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6779   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6780     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6781     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6782     IsDlgButtonChecked(hDlg, OPT_View));\r
6783 }\r
6784 \r
6785 char *\r
6786 QuoteForFilename(char *filename)\r
6787 {\r
6788   int dquote, space;\r
6789   dquote = strchr(filename, '"') != NULL;\r
6790   space = strchr(filename, ' ') != NULL;\r
6791   if (dquote || space) {\r
6792     if (dquote) {\r
6793       return "'";\r
6794     } else {\r
6795       return "\"";\r
6796     }\r
6797   } else {\r
6798     return "";\r
6799   }\r
6800 }\r
6801 \r
6802 VOID\r
6803 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6804 {\r
6805   char buf[MSG_SIZ];\r
6806   char *q;\r
6807 \r
6808   InitComboStringsFromOption(hwndCombo, nthnames);\r
6809   q = QuoteForFilename(nthcp);\r
6810   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6811   if (*nthdir != NULLCHAR) {\r
6812     q = QuoteForFilename(nthdir);\r
6813     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6814   }\r
6815   if (*nthcp == NULLCHAR) {\r
6816     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6817   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6818     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6819     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6820   }\r
6821 }\r
6822 \r
6823 LRESULT CALLBACK\r
6824 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6825 {\r
6826   char buf[MSG_SIZ];\r
6827   HANDLE hwndCombo;\r
6828   char *p;\r
6829 \r
6830   switch (message) {\r
6831   case WM_INITDIALOG:\r
6832     /* Center the dialog */\r
6833     CenterWindow (hDlg, GetDesktopWindow());\r
6834     /* Initialize the dialog items */\r
6835     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6836                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6837                   firstChessProgramNames);\r
6838     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6839                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6840                   secondChessProgramNames);\r
6841     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6842     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6843     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6844     if (*appData.icsHelper != NULLCHAR) {\r
6845       char *q = QuoteForFilename(appData.icsHelper);\r
6846       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6847     }\r
6848     if (*appData.icsHost == NULLCHAR) {\r
6849       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6850       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6851     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6852       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6853       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6854     }\r
6855 \r
6856     if (appData.icsActive) {\r
6857       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6858     }\r
6859     else if (appData.noChessProgram) {\r
6860       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6861     }\r
6862     else {\r
6863       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6864     }\r
6865 \r
6866     SetStartupDialogEnables(hDlg);\r
6867     return TRUE;\r
6868 \r
6869   case WM_COMMAND:\r
6870     switch (LOWORD(wParam)) {\r
6871     case IDOK:\r
6872       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6873         strcpy(buf, "/fcp=");\r
6874         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6875         p = buf;\r
6876         ParseArgs(StringGet, &p);\r
6877         strcpy(buf, "/scp=");\r
6878         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6879         p = buf;\r
6880         ParseArgs(StringGet, &p);\r
6881         appData.noChessProgram = FALSE;\r
6882         appData.icsActive = FALSE;\r
6883       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6884         strcpy(buf, "/ics /icshost=");\r
6885         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6886         p = buf;\r
6887         ParseArgs(StringGet, &p);\r
6888         if (appData.zippyPlay) {\r
6889           strcpy(buf, "/fcp=");\r
6890           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6891           p = buf;\r
6892           ParseArgs(StringGet, &p);\r
6893         }\r
6894       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6895         appData.noChessProgram = TRUE;\r
6896         appData.icsActive = FALSE;\r
6897       } else {\r
6898         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6899                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6900         return TRUE;\r
6901       }\r
6902       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6903         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6904         p = buf;\r
6905         ParseArgs(StringGet, &p);\r
6906       }\r
6907       EndDialog(hDlg, TRUE);\r
6908       return TRUE;\r
6909 \r
6910     case IDCANCEL:\r
6911       ExitEvent(0);\r
6912       return TRUE;\r
6913 \r
6914     case IDM_HELPCONTENTS:\r
6915       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6916         MessageBox (GetFocus(),\r
6917                     "Unable to activate help",\r
6918                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6919       }\r
6920       break;\r
6921 \r
6922     default:\r
6923       SetStartupDialogEnables(hDlg);\r
6924       break;\r
6925     }\r
6926     break;\r
6927   }\r
6928   return FALSE;\r
6929 }\r
6930 \r
6931 /*---------------------------------------------------------------------------*\\r
6932  *\r
6933  * About box dialog functions\r
6934  *\r
6935 \*---------------------------------------------------------------------------*/\r
6936 \r
6937 /* Process messages for "About" dialog box */\r
6938 LRESULT CALLBACK\r
6939 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6940 {\r
6941   switch (message) {\r
6942   case WM_INITDIALOG: /* message: initialize dialog box */\r
6943     /* Center the dialog over the application window */\r
6944     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6945     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6946     JAWS_COPYRIGHT\r
6947     return (TRUE);\r
6948 \r
6949   case WM_COMMAND: /* message: received a command */\r
6950     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6951         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6952       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6953       return (TRUE);\r
6954     }\r
6955     break;\r
6956   }\r
6957   return (FALSE);\r
6958 }\r
6959 \r
6960 /*---------------------------------------------------------------------------*\\r
6961  *\r
6962  * Comment Dialog functions\r
6963  *\r
6964 \*---------------------------------------------------------------------------*/\r
6965 \r
6966 LRESULT CALLBACK\r
6967 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6968 {\r
6969   static HANDLE hwndText = NULL;\r
6970   int len, newSizeX, newSizeY, flags;\r
6971   static int sizeX, sizeY;\r
6972   char *str;\r
6973   RECT rect;\r
6974   MINMAXINFO *mmi;\r
6975 \r
6976   switch (message) {\r
6977   case WM_INITDIALOG: /* message: initialize dialog box */\r
6978     /* Initialize the dialog items */\r
6979     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6980     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6981     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6982     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6983     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6984     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6985     SetWindowText(hDlg, commentTitle);\r
6986     if (editComment) {\r
6987       SetFocus(hwndText);\r
6988     } else {\r
6989       SetFocus(GetDlgItem(hDlg, IDOK));\r
6990     }\r
6991     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6992                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6993                 MAKELPARAM(FALSE, 0));\r
6994     /* Size and position the dialog */\r
6995     if (!commentDialog) {\r
6996       commentDialog = hDlg;\r
6997       flags = SWP_NOZORDER;\r
6998       GetClientRect(hDlg, &rect);\r
6999       sizeX = rect.right;\r
7000       sizeY = rect.bottom;\r
7001       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7002           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7003         WINDOWPLACEMENT wp;\r
7004         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7005         wp.length = sizeof(WINDOWPLACEMENT);\r
7006         wp.flags = 0;\r
7007         wp.showCmd = SW_SHOW;\r
7008         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7009         wp.rcNormalPosition.left = commentX;\r
7010         wp.rcNormalPosition.right = commentX + commentW;\r
7011         wp.rcNormalPosition.top = commentY;\r
7012         wp.rcNormalPosition.bottom = commentY + commentH;\r
7013         SetWindowPlacement(hDlg, &wp);\r
7014 \r
7015         GetClientRect(hDlg, &rect);\r
7016         newSizeX = rect.right;\r
7017         newSizeY = rect.bottom;\r
7018         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7019                               newSizeX, newSizeY);\r
7020         sizeX = newSizeX;\r
7021         sizeY = newSizeY;\r
7022       }\r
7023     }\r
7024     return FALSE;\r
7025 \r
7026   case WM_COMMAND: /* message: received a command */\r
7027     switch (LOWORD(wParam)) {\r
7028     case IDOK:\r
7029       if (editComment) {\r
7030         char *p, *q;\r
7031         /* Read changed options from the dialog box */\r
7032         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7033         len = GetWindowTextLength(hwndText);\r
7034         str = (char *) malloc(len + 1);\r
7035         GetWindowText(hwndText, str, len + 1);\r
7036         p = q = str;\r
7037         while (*q) {\r
7038           if (*q == '\r')\r
7039             q++;\r
7040           else\r
7041             *p++ = *q++;\r
7042         }\r
7043         *p = NULLCHAR;\r
7044         ReplaceComment(commentIndex, str);\r
7045         free(str);\r
7046       }\r
7047       CommentPopDown();\r
7048       return TRUE;\r
7049 \r
7050     case IDCANCEL:\r
7051     case OPT_CancelComment:\r
7052       CommentPopDown();\r
7053       return TRUE;\r
7054 \r
7055     case OPT_ClearComment:\r
7056       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7057       break;\r
7058 \r
7059     case OPT_EditComment:\r
7060       EditCommentEvent();\r
7061       return TRUE;\r
7062 \r
7063     default:\r
7064       break;\r
7065     }\r
7066     break;\r
7067 \r
7068   case WM_SIZE:\r
7069     newSizeX = LOWORD(lParam);\r
7070     newSizeY = HIWORD(lParam);\r
7071     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7072     sizeX = newSizeX;\r
7073     sizeY = newSizeY;\r
7074     break;\r
7075 \r
7076   case WM_GETMINMAXINFO:\r
7077     /* Prevent resizing window too small */\r
7078     mmi = (MINMAXINFO *) lParam;\r
7079     mmi->ptMinTrackSize.x = 100;\r
7080     mmi->ptMinTrackSize.y = 100;\r
7081     break;\r
7082   }\r
7083   return FALSE;\r
7084 }\r
7085 \r
7086 VOID\r
7087 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7088 {\r
7089   FARPROC lpProc;\r
7090   char *p, *q;\r
7091 \r
7092   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7093 \r
7094   if (str == NULL) str = "";\r
7095   p = (char *) malloc(2 * strlen(str) + 2);\r
7096   q = p;\r
7097   while (*str) {\r
7098     if (*str == '\n') *q++ = '\r';\r
7099     *q++ = *str++;\r
7100   }\r
7101   *q = NULLCHAR;\r
7102   if (commentText != NULL) free(commentText);\r
7103 \r
7104   commentIndex = index;\r
7105   commentTitle = title;\r
7106   commentText = p;\r
7107   editComment = edit;\r
7108 \r
7109   if (commentDialog) {\r
7110     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7111     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7112   } else {\r
7113     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7114     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7115                  hwndMain, (DLGPROC)lpProc);\r
7116     FreeProcInstance(lpProc);\r
7117   }\r
7118   commentDialogUp = TRUE;\r
7119 }\r
7120 \r
7121 \r
7122 /*---------------------------------------------------------------------------*\\r
7123  *\r
7124  * Type-in move dialog functions\r
7125  * \r
7126 \*---------------------------------------------------------------------------*/\r
7127 \r
7128 LRESULT CALLBACK\r
7129 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7130 {\r
7131   char move[MSG_SIZ];\r
7132   HWND hInput;\r
7133   ChessMove moveType;\r
7134   int fromX, fromY, toX, toY;\r
7135   char promoChar;\r
7136 \r
7137   switch (message) {\r
7138   case WM_INITDIALOG:\r
7139     move[0] = (char) lParam;\r
7140     move[1] = NULLCHAR;\r
7141     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7142     hInput = GetDlgItem(hDlg, OPT_Move);\r
7143     SetWindowText(hInput, move);\r
7144     SetFocus(hInput);\r
7145     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7146     return FALSE;\r
7147 \r
7148   case WM_COMMAND:\r
7149     switch (LOWORD(wParam)) {\r
7150     case IDOK:\r
7151       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7152       { int n; Board board;\r
7153         // [HGM] FENedit\r
7154         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7155                 EditPositionPasteFEN(move);\r
7156                 EndDialog(hDlg, TRUE);\r
7157                 return TRUE;\r
7158         }\r
7159         // [HGM] movenum: allow move number to be typed in any mode\r
7160         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7161           currentMove = 2*n-1;\r
7162           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7163           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7164           EndDialog(hDlg, TRUE);\r
7165           DrawPosition(TRUE, boards[currentMove]);\r
7166           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7167           else DisplayMessage("", "");\r
7168           return TRUE;\r
7169         }\r
7170       }\r
7171       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7172         gameMode != Training) {\r
7173         DisplayMoveError("Displayed move is not current");\r
7174       } else {\r
7175 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7176         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7177           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7178         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7179         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7180           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7181           if (gameMode != Training)\r
7182               forwardMostMove = currentMove;\r
7183           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7184         } else {\r
7185           DisplayMoveError("Could not parse move");\r
7186         }\r
7187       }\r
7188       EndDialog(hDlg, TRUE);\r
7189       return TRUE;\r
7190     case IDCANCEL:\r
7191       EndDialog(hDlg, FALSE);\r
7192       return TRUE;\r
7193     default:\r
7194       break;\r
7195     }\r
7196     break;\r
7197   }\r
7198   return FALSE;\r
7199 }\r
7200 \r
7201 VOID\r
7202 PopUpMoveDialog(char firstchar)\r
7203 {\r
7204     FARPROC lpProc;\r
7205     \r
7206     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7207         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7208         gameMode == AnalyzeMode || gameMode == EditGame || \r
7209         gameMode == EditPosition || gameMode == IcsExamining ||\r
7210         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7211         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7212                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7213                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7214         gameMode == Training) {\r
7215       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7216       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7217         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7218       FreeProcInstance(lpProc);\r
7219     }\r
7220 }\r
7221 \r
7222 /*---------------------------------------------------------------------------*\\r
7223  *\r
7224  * Type-in name dialog functions\r
7225  * \r
7226 \*---------------------------------------------------------------------------*/\r
7227 \r
7228 LRESULT CALLBACK\r
7229 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7230 {\r
7231   char move[MSG_SIZ];\r
7232   HWND hInput;\r
7233 \r
7234   switch (message) {\r
7235   case WM_INITDIALOG:\r
7236     move[0] = (char) lParam;\r
7237     move[1] = NULLCHAR;\r
7238     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7239     hInput = GetDlgItem(hDlg, OPT_Name);\r
7240     SetWindowText(hInput, move);\r
7241     SetFocus(hInput);\r
7242     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7243     return FALSE;\r
7244 \r
7245   case WM_COMMAND:\r
7246     switch (LOWORD(wParam)) {\r
7247     case IDOK:\r
7248       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7249       appData.userName = strdup(move);\r
7250       SetUserLogo();\r
7251 \r
7252       EndDialog(hDlg, TRUE);\r
7253       return TRUE;\r
7254     case IDCANCEL:\r
7255       EndDialog(hDlg, FALSE);\r
7256       return TRUE;\r
7257     default:\r
7258       break;\r
7259     }\r
7260     break;\r
7261   }\r
7262   return FALSE;\r
7263 }\r
7264 \r
7265 VOID\r
7266 PopUpNameDialog(char firstchar)\r
7267 {\r
7268     FARPROC lpProc;\r
7269     \r
7270       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7271       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7272         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7273       FreeProcInstance(lpProc);\r
7274 }\r
7275 \r
7276 /*---------------------------------------------------------------------------*\\r
7277  *\r
7278  *  Error dialogs\r
7279  * \r
7280 \*---------------------------------------------------------------------------*/\r
7281 \r
7282 /* Nonmodal error box */\r
7283 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7284                              WPARAM wParam, LPARAM lParam);\r
7285 \r
7286 VOID\r
7287 ErrorPopUp(char *title, char *content)\r
7288 {\r
7289   FARPROC lpProc;\r
7290   char *p, *q;\r
7291   BOOLEAN modal = hwndMain == NULL;\r
7292 \r
7293   p = content;\r
7294   q = errorMessage;\r
7295   while (*p) {\r
7296     if (*p == '\n') {\r
7297       if (modal) {\r
7298         *q++ = ' ';\r
7299         p++;\r
7300       } else {\r
7301         *q++ = '\r';\r
7302         *q++ = *p++;\r
7303       }\r
7304     } else {\r
7305       *q++ = *p++;\r
7306     }\r
7307   }\r
7308   *q = NULLCHAR;\r
7309   strncpy(errorTitle, title, sizeof(errorTitle));\r
7310   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7311   \r
7312   if (modal) {\r
7313     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7314   } else {\r
7315     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7316     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7317                  hwndMain, (DLGPROC)lpProc);\r
7318     FreeProcInstance(lpProc);\r
7319   }\r
7320 }\r
7321 \r
7322 VOID\r
7323 ErrorPopDown()\r
7324 {\r
7325   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7326   if (errorDialog == NULL) return;\r
7327   DestroyWindow(errorDialog);\r
7328   errorDialog = NULL;\r
7329 }\r
7330 \r
7331 LRESULT CALLBACK\r
7332 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7333 {\r
7334   HANDLE hwndText;\r
7335   RECT rChild;\r
7336 \r
7337   switch (message) {\r
7338   case WM_INITDIALOG:\r
7339     GetWindowRect(hDlg, &rChild);\r
7340 \r
7341     /*\r
7342     SetWindowPos(hDlg, NULL, rChild.left,\r
7343       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7344       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7345     */\r
7346 \r
7347     /* \r
7348         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7349         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7350         and it doesn't work when you resize the dialog.\r
7351         For now, just give it a default position.\r
7352     */\r
7353     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7354 \r
7355     errorDialog = hDlg;\r
7356     SetWindowText(hDlg, errorTitle);\r
7357     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7358     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7359     return FALSE;\r
7360 \r
7361   case WM_COMMAND:\r
7362     switch (LOWORD(wParam)) {\r
7363     case IDOK:\r
7364     case IDCANCEL:\r
7365       if (errorDialog == hDlg) errorDialog = NULL;\r
7366       DestroyWindow(hDlg);\r
7367       return TRUE;\r
7368 \r
7369     default:\r
7370       break;\r
7371     }\r
7372     break;\r
7373   }\r
7374   return FALSE;\r
7375 }\r
7376 \r
7377 #ifdef GOTHIC\r
7378 HWND gothicDialog = NULL;\r
7379 \r
7380 LRESULT CALLBACK\r
7381 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7382 {\r
7383   HANDLE hwndText;\r
7384   RECT rChild;\r
7385   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7386 \r
7387   switch (message) {\r
7388   case WM_INITDIALOG:\r
7389     GetWindowRect(hDlg, &rChild);\r
7390 \r
7391     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7392                                                              SWP_NOZORDER);\r
7393 \r
7394     /* \r
7395         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7396         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7397         and it doesn't work when you resize the dialog.\r
7398         For now, just give it a default position.\r
7399     */\r
7400     gothicDialog = hDlg;\r
7401     SetWindowText(hDlg, errorTitle);\r
7402     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7403     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7404     return FALSE;\r
7405 \r
7406   case WM_COMMAND:\r
7407     switch (LOWORD(wParam)) {\r
7408     case IDOK:\r
7409     case IDCANCEL:\r
7410       if (errorDialog == hDlg) errorDialog = NULL;\r
7411       DestroyWindow(hDlg);\r
7412       return TRUE;\r
7413 \r
7414     default:\r
7415       break;\r
7416     }\r
7417     break;\r
7418   }\r
7419   return FALSE;\r
7420 }\r
7421 \r
7422 VOID\r
7423 GothicPopUp(char *title, VariantClass variant)\r
7424 {\r
7425   FARPROC lpProc;\r
7426   static char *lastTitle;\r
7427 \r
7428   strncpy(errorTitle, title, sizeof(errorTitle));\r
7429   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7430 \r
7431   if(lastTitle != title && gothicDialog != NULL) {\r
7432     DestroyWindow(gothicDialog);\r
7433     gothicDialog = NULL;\r
7434   }\r
7435   if(variant != VariantNormal && gothicDialog == NULL) {\r
7436     title = lastTitle;\r
7437     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7438     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7439                  hwndMain, (DLGPROC)lpProc);\r
7440     FreeProcInstance(lpProc);\r
7441   }\r
7442 }\r
7443 #endif\r
7444 \r
7445 /*---------------------------------------------------------------------------*\\r
7446  *\r
7447  *  Ics Interaction console functions\r
7448  *\r
7449 \*---------------------------------------------------------------------------*/\r
7450 \r
7451 #define HISTORY_SIZE 64\r
7452 static char *history[HISTORY_SIZE];\r
7453 int histIn = 0, histP = 0;\r
7454 \r
7455 VOID\r
7456 SaveInHistory(char *cmd)\r
7457 {\r
7458   if (history[histIn] != NULL) {\r
7459     free(history[histIn]);\r
7460     history[histIn] = NULL;\r
7461   }\r
7462   if (*cmd == NULLCHAR) return;\r
7463   history[histIn] = StrSave(cmd);\r
7464   histIn = (histIn + 1) % HISTORY_SIZE;\r
7465   if (history[histIn] != NULL) {\r
7466     free(history[histIn]);\r
7467     history[histIn] = NULL;\r
7468   }\r
7469   histP = histIn;\r
7470 }\r
7471 \r
7472 char *\r
7473 PrevInHistory(char *cmd)\r
7474 {\r
7475   int newhp;\r
7476   if (histP == histIn) {\r
7477     if (history[histIn] != NULL) free(history[histIn]);\r
7478     history[histIn] = StrSave(cmd);\r
7479   }\r
7480   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7481   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7482   histP = newhp;\r
7483   return history[histP];\r
7484 }\r
7485 \r
7486 char *\r
7487 NextInHistory()\r
7488 {\r
7489   if (histP == histIn) return NULL;\r
7490   histP = (histP + 1) % HISTORY_SIZE;\r
7491   return history[histP];\r
7492 }\r
7493 \r
7494 typedef struct {\r
7495   char *item;\r
7496   char *command;\r
7497   BOOLEAN getname;\r
7498   BOOLEAN immediate;\r
7499 } IcsTextMenuEntry;\r
7500 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7501 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7502 \r
7503 void\r
7504 ParseIcsTextMenu(char *icsTextMenuString)\r
7505 {\r
7506 //  int flags = 0;\r
7507   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7508   char *p = icsTextMenuString;\r
7509   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7510     free(e->item);\r
7511     e->item = NULL;\r
7512     if (e->command != NULL) {\r
7513       free(e->command);\r
7514       e->command = NULL;\r
7515     }\r
7516     e++;\r
7517   }\r
7518   e = icsTextMenuEntry;\r
7519   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7520     if (*p == ';' || *p == '\n') {\r
7521       e->item = strdup("-");\r
7522       e->command = NULL;\r
7523       p++;\r
7524     } else if (*p == '-') {\r
7525       e->item = strdup("-");\r
7526       e->command = NULL;\r
7527       p++;\r
7528       if (*p) p++;\r
7529     } else {\r
7530       char *q, *r, *s, *t;\r
7531       char c;\r
7532       q = strchr(p, ',');\r
7533       if (q == NULL) break;\r
7534       *q = NULLCHAR;\r
7535       r = strchr(q + 1, ',');\r
7536       if (r == NULL) break;\r
7537       *r = NULLCHAR;\r
7538       s = strchr(r + 1, ',');\r
7539       if (s == NULL) break;\r
7540       *s = NULLCHAR;\r
7541       c = ';';\r
7542       t = strchr(s + 1, c);\r
7543       if (t == NULL) {\r
7544         c = '\n';\r
7545         t = strchr(s + 1, c);\r
7546       }\r
7547       if (t != NULL) *t = NULLCHAR;\r
7548       e->item = strdup(p);\r
7549       e->command = strdup(q + 1);\r
7550       e->getname = *(r + 1) != '0';\r
7551       e->immediate = *(s + 1) != '0';\r
7552       *q = ',';\r
7553       *r = ',';\r
7554       *s = ',';\r
7555       if (t == NULL) break;\r
7556       *t = c;\r
7557       p = t + 1;\r
7558     }\r
7559     e++;\r
7560   } \r
7561 }\r
7562 \r
7563 HMENU\r
7564 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7565 {\r
7566   HMENU hmenu, h;\r
7567   int i = 0;\r
7568   hmenu = LoadMenu(hInst, "TextMenu");\r
7569   h = GetSubMenu(hmenu, 0);\r
7570   while (e->item) {\r
7571     if (strcmp(e->item, "-") == 0) {\r
7572       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7573     } else {\r
7574       if (e->item[0] == '|') {\r
7575         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7576                    IDM_CommandX + i, &e->item[1]);\r
7577       } else {\r
7578         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7579       }\r
7580     }\r
7581     e++;\r
7582     i++;\r
7583   } \r
7584   return hmenu;\r
7585 }\r
7586 \r
7587 WNDPROC consoleTextWindowProc;\r
7588 \r
7589 void\r
7590 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7591 {\r
7592   char buf[MSG_SIZ], name[MSG_SIZ];\r
7593   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7594   CHARRANGE sel;\r
7595 \r
7596   if (!getname) {\r
7597     SetWindowText(hInput, command);\r
7598     if (immediate) {\r
7599       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7600     } else {\r
7601       sel.cpMin = 999999;\r
7602       sel.cpMax = 999999;\r
7603       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7604       SetFocus(hInput);\r
7605     }\r
7606     return;\r
7607   }    \r
7608   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7609   if (sel.cpMin == sel.cpMax) {\r
7610     /* Expand to surrounding word */\r
7611     TEXTRANGE tr;\r
7612     do {\r
7613       tr.chrg.cpMax = sel.cpMin;\r
7614       tr.chrg.cpMin = --sel.cpMin;\r
7615       if (sel.cpMin < 0) break;\r
7616       tr.lpstrText = name;\r
7617       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7618     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7619     sel.cpMin++;\r
7620 \r
7621     do {\r
7622       tr.chrg.cpMin = sel.cpMax;\r
7623       tr.chrg.cpMax = ++sel.cpMax;\r
7624       tr.lpstrText = name;\r
7625       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7626     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7627     sel.cpMax--;\r
7628 \r
7629     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7630       MessageBeep(MB_ICONEXCLAMATION);\r
7631       return;\r
7632     }\r
7633     tr.chrg = sel;\r
7634     tr.lpstrText = name;\r
7635     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7636   } else {\r
7637     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7638       MessageBeep(MB_ICONEXCLAMATION);\r
7639       return;\r
7640     }\r
7641     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7642   }\r
7643   if (immediate) {\r
7644     sprintf(buf, "%s %s", command, name);\r
7645     SetWindowText(hInput, buf);\r
7646     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7647   } else {\r
7648     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7649     SetWindowText(hInput, buf);\r
7650     sel.cpMin = 999999;\r
7651     sel.cpMax = 999999;\r
7652     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7653     SetFocus(hInput);\r
7654   }\r
7655 }\r
7656 \r
7657 LRESULT CALLBACK \r
7658 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7659 {\r
7660   HWND hInput;\r
7661   CHARRANGE sel;\r
7662 \r
7663   switch (message) {\r
7664   case WM_KEYDOWN:\r
7665     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7666     switch (wParam) {\r
7667     case VK_PRIOR:\r
7668       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7669       return 0;\r
7670     case VK_NEXT:\r
7671       sel.cpMin = 999999;\r
7672       sel.cpMax = 999999;\r
7673       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7674       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7675       return 0;\r
7676     }\r
7677     break;\r
7678   case WM_CHAR:\r
7679    if(wParam != '\022') {\r
7680     if (wParam == '\t') {\r
7681       if (GetKeyState(VK_SHIFT) < 0) {\r
7682         /* shifted */\r
7683         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7684         if (buttonDesc[0].hwnd) {\r
7685           SetFocus(buttonDesc[0].hwnd);\r
7686         } else {\r
7687           SetFocus(hwndMain);\r
7688         }\r
7689       } else {\r
7690         /* unshifted */\r
7691         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7692       }\r
7693     } else {\r
7694       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7695       JAWS_DELETE( SetFocus(hInput); )\r
7696       SendMessage(hInput, message, wParam, lParam);\r
7697     }\r
7698     return 0;\r
7699    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7700   case WM_RBUTTONUP:\r
7701     if (GetKeyState(VK_SHIFT) & ~1) {\r
7702       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7703         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7704     } else {\r
7705       POINT pt;\r
7706       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7707       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7708       if (sel.cpMin == sel.cpMax) {\r
7709         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7710         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7711       }\r
7712       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7713         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7714       }\r
7715       pt.x = LOWORD(lParam);\r
7716       pt.y = HIWORD(lParam);\r
7717       MenuPopup(hwnd, pt, hmenu, -1);\r
7718     }\r
7719     return 0;\r
7720   case WM_PASTE:\r
7721     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7722     SetFocus(hInput);\r
7723     return SendMessage(hInput, message, wParam, lParam);\r
7724   case WM_MBUTTONDOWN:\r
7725     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7726   case WM_RBUTTONDOWN:\r
7727     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7728       /* Move selection here if it was empty */\r
7729       POINT pt;\r
7730       pt.x = LOWORD(lParam);\r
7731       pt.y = HIWORD(lParam);\r
7732       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7733       if (sel.cpMin == sel.cpMax) {\r
7734         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7735         sel.cpMax = sel.cpMin;\r
7736         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7737       }\r
7738       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7739     }\r
7740     return 0;\r
7741   case WM_COMMAND:\r
7742     switch (LOWORD(wParam)) {\r
7743     case IDM_QuickPaste:\r
7744       {\r
7745         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7746         if (sel.cpMin == sel.cpMax) {\r
7747           MessageBeep(MB_ICONEXCLAMATION);\r
7748           return 0;\r
7749         }\r
7750         SendMessage(hwnd, WM_COPY, 0, 0);\r
7751         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7752         SendMessage(hInput, WM_PASTE, 0, 0);\r
7753         SetFocus(hInput);\r
7754         return 0;\r
7755       }\r
7756     case IDM_Cut:\r
7757       SendMessage(hwnd, WM_CUT, 0, 0);\r
7758       return 0;\r
7759     case IDM_Paste:\r
7760       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7761       return 0;\r
7762     case IDM_Copy:\r
7763       SendMessage(hwnd, WM_COPY, 0, 0);\r
7764       return 0;\r
7765     default:\r
7766       {\r
7767         int i = LOWORD(wParam) - IDM_CommandX;\r
7768         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7769             icsTextMenuEntry[i].command != NULL) {\r
7770           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7771                    icsTextMenuEntry[i].getname,\r
7772                    icsTextMenuEntry[i].immediate);\r
7773           return 0;\r
7774         }\r
7775       }\r
7776       break;\r
7777     }\r
7778     break;\r
7779   }\r
7780   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7781 }\r
7782 \r
7783 WNDPROC consoleInputWindowProc;\r
7784 \r
7785 LRESULT CALLBACK\r
7786 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7787 {\r
7788   char buf[MSG_SIZ];\r
7789   char *p;\r
7790   static BOOL sendNextChar = FALSE;\r
7791   static BOOL quoteNextChar = FALSE;\r
7792   InputSource *is = consoleInputSource;\r
7793   CHARFORMAT cf;\r
7794   CHARRANGE sel;\r
7795 \r
7796   switch (message) {\r
7797   case WM_CHAR:\r
7798     if (!appData.localLineEditing || sendNextChar) {\r
7799       is->buf[0] = (CHAR) wParam;\r
7800       is->count = 1;\r
7801       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7802       sendNextChar = FALSE;\r
7803       return 0;\r
7804     }\r
7805     if (quoteNextChar) {\r
7806       buf[0] = (char) wParam;\r
7807       buf[1] = NULLCHAR;\r
7808       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7809       quoteNextChar = FALSE;\r
7810       return 0;\r
7811     }\r
7812     switch (wParam) {\r
7813     case '\r':   /* Enter key */\r
7814       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7815       if (consoleEcho) SaveInHistory(is->buf);\r
7816       is->buf[is->count++] = '\n';\r
7817       is->buf[is->count] = NULLCHAR;\r
7818       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7819       if (consoleEcho) {\r
7820         ConsoleOutput(is->buf, is->count, TRUE);\r
7821       } else if (appData.localLineEditing) {\r
7822         ConsoleOutput("\n", 1, TRUE);\r
7823       }\r
7824       /* fall thru */\r
7825     case '\033': /* Escape key */\r
7826       SetWindowText(hwnd, "");\r
7827       cf.cbSize = sizeof(CHARFORMAT);\r
7828       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7829       if (consoleEcho) {\r
7830         cf.crTextColor = textAttribs[ColorNormal].color;\r
7831       } else {\r
7832         cf.crTextColor = COLOR_ECHOOFF;\r
7833       }\r
7834       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7835       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7836       return 0;\r
7837     case '\t':   /* Tab key */\r
7838       if (GetKeyState(VK_SHIFT) < 0) {\r
7839         /* shifted */\r
7840         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7841       } else {\r
7842         /* unshifted */\r
7843         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7844         if (buttonDesc[0].hwnd) {\r
7845           SetFocus(buttonDesc[0].hwnd);\r
7846         } else {\r
7847           SetFocus(hwndMain);\r
7848         }\r
7849       }\r
7850       return 0;\r
7851     case '\023': /* Ctrl+S */\r
7852       sendNextChar = TRUE;\r
7853       return 0;\r
7854     case '\021': /* Ctrl+Q */\r
7855       quoteNextChar = TRUE;\r
7856       return 0;\r
7857     JAWS_REPLAY\r
7858     default:\r
7859       break;\r
7860     }\r
7861     break;\r
7862   case WM_KEYDOWN:\r
7863     switch (wParam) {\r
7864     case VK_UP:\r
7865       GetWindowText(hwnd, buf, MSG_SIZ);\r
7866       p = PrevInHistory(buf);\r
7867       if (p != NULL) {\r
7868         SetWindowText(hwnd, p);\r
7869         sel.cpMin = 999999;\r
7870         sel.cpMax = 999999;\r
7871         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7872         return 0;\r
7873       }\r
7874       break;\r
7875     case VK_DOWN:\r
7876       p = NextInHistory();\r
7877       if (p != NULL) {\r
7878         SetWindowText(hwnd, p);\r
7879         sel.cpMin = 999999;\r
7880         sel.cpMax = 999999;\r
7881         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7882         return 0;\r
7883       }\r
7884       break;\r
7885     case VK_HOME:\r
7886     case VK_END:\r
7887       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7888       /* fall thru */\r
7889     case VK_PRIOR:\r
7890     case VK_NEXT:\r
7891       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7892       return 0;\r
7893     }\r
7894     break;\r
7895   case WM_MBUTTONDOWN:\r
7896     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7897       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7898     break;\r
7899   case WM_RBUTTONUP:\r
7900     if (GetKeyState(VK_SHIFT) & ~1) {\r
7901       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7902         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7903     } else {\r
7904       POINT pt;\r
7905       HMENU hmenu;\r
7906       hmenu = LoadMenu(hInst, "InputMenu");\r
7907       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7908       if (sel.cpMin == sel.cpMax) {\r
7909         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7910         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7911       }\r
7912       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7913         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7914       }\r
7915       pt.x = LOWORD(lParam);\r
7916       pt.y = HIWORD(lParam);\r
7917       MenuPopup(hwnd, pt, hmenu, -1);\r
7918     }\r
7919     return 0;\r
7920   case WM_COMMAND:\r
7921     switch (LOWORD(wParam)) { \r
7922     case IDM_Undo:\r
7923       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7924       return 0;\r
7925     case IDM_SelectAll:\r
7926       sel.cpMin = 0;\r
7927       sel.cpMax = -1; /*999999?*/\r
7928       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7929       return 0;\r
7930     case IDM_Cut:\r
7931       SendMessage(hwnd, WM_CUT, 0, 0);\r
7932       return 0;\r
7933     case IDM_Paste:\r
7934       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7935       return 0;\r
7936     case IDM_Copy:\r
7937       SendMessage(hwnd, WM_COPY, 0, 0);\r
7938       return 0;\r
7939     }\r
7940     break;\r
7941   }\r
7942   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7943 }\r
7944 \r
7945 #define CO_MAX  100000\r
7946 #define CO_TRIM   1000\r
7947 \r
7948 LRESULT CALLBACK\r
7949 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7950 {\r
7951   static SnapData sd;\r
7952   HWND hText, hInput;\r
7953   RECT rect;\r
7954   static int sizeX, sizeY;\r
7955   int newSizeX, newSizeY;\r
7956   MINMAXINFO *mmi;\r
7957   WORD wMask;\r
7958 \r
7959   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7960   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7961 \r
7962   switch (message) {\r
7963   case WM_NOTIFY:\r
7964     if (((NMHDR*)lParam)->code == EN_LINK)\r
7965     {\r
7966       ENLINK *pLink = (ENLINK*)lParam;\r
7967       if (pLink->msg == WM_LBUTTONUP)\r
7968       {\r
7969         TEXTRANGE tr;\r
7970 \r
7971         tr.chrg = pLink->chrg;\r
7972         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7973         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7974         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7975         free(tr.lpstrText);\r
7976       }\r
7977     }\r
7978     break;\r
7979   case WM_INITDIALOG: /* message: initialize dialog box */\r
7980     hwndConsole = hDlg;\r
7981     SetFocus(hInput);\r
7982     consoleTextWindowProc = (WNDPROC)\r
7983       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7984     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7985     consoleInputWindowProc = (WNDPROC)\r
7986       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7987     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7988     Colorize(ColorNormal, TRUE);\r
7989     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7990     ChangedConsoleFont();\r
7991     GetClientRect(hDlg, &rect);\r
7992     sizeX = rect.right;\r
7993     sizeY = rect.bottom;\r
7994     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7995         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7996       WINDOWPLACEMENT wp;\r
7997       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7998       wp.length = sizeof(WINDOWPLACEMENT);\r
7999       wp.flags = 0;\r
8000       wp.showCmd = SW_SHOW;\r
8001       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8002       wp.rcNormalPosition.left = wpConsole.x;\r
8003       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8004       wp.rcNormalPosition.top = wpConsole.y;\r
8005       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8006       SetWindowPlacement(hDlg, &wp);\r
8007     }\r
8008 \r
8009    // [HGM] Chessknight's change 2004-07-13\r
8010    else { /* Determine Defaults */\r
8011        WINDOWPLACEMENT wp;\r
8012        wpConsole.x = winWidth + 1;\r
8013        wpConsole.y = boardY;\r
8014        wpConsole.width = screenWidth -  winWidth;\r
8015        wpConsole.height = winHeight;\r
8016        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8017        wp.length = sizeof(WINDOWPLACEMENT);\r
8018        wp.flags = 0;\r
8019        wp.showCmd = SW_SHOW;\r
8020        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8021        wp.rcNormalPosition.left = wpConsole.x;\r
8022        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8023        wp.rcNormalPosition.top = wpConsole.y;\r
8024        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8025        SetWindowPlacement(hDlg, &wp);\r
8026     }\r
8027 \r
8028    // Allow hText to highlight URLs and send notifications on them\r
8029    wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8030    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8031    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8032    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
8033 \r
8034     return FALSE;\r
8035 \r
8036   case WM_SETFOCUS:\r
8037     SetFocus(hInput);\r
8038     return 0;\r
8039 \r
8040   case WM_CLOSE:\r
8041     ExitEvent(0);\r
8042     /* not reached */\r
8043     break;\r
8044 \r
8045   case WM_SIZE:\r
8046     if (IsIconic(hDlg)) break;\r
8047     newSizeX = LOWORD(lParam);\r
8048     newSizeY = HIWORD(lParam);\r
8049     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8050       RECT rectText, rectInput;\r
8051       POINT pt;\r
8052       int newTextHeight, newTextWidth;\r
8053       GetWindowRect(hText, &rectText);\r
8054       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8055       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8056       if (newTextHeight < 0) {\r
8057         newSizeY += -newTextHeight;\r
8058         newTextHeight = 0;\r
8059       }\r
8060       SetWindowPos(hText, NULL, 0, 0,\r
8061         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8062       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8063       pt.x = rectInput.left;\r
8064       pt.y = rectInput.top + newSizeY - sizeY;\r
8065       ScreenToClient(hDlg, &pt);\r
8066       SetWindowPos(hInput, NULL, \r
8067         pt.x, pt.y, /* needs client coords */   \r
8068         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8069         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8070     }\r
8071     sizeX = newSizeX;\r
8072     sizeY = newSizeY;\r
8073     break;\r
8074 \r
8075   case WM_GETMINMAXINFO:\r
8076     /* Prevent resizing window too small */\r
8077     mmi = (MINMAXINFO *) lParam;\r
8078     mmi->ptMinTrackSize.x = 100;\r
8079     mmi->ptMinTrackSize.y = 100;\r
8080     break;\r
8081 \r
8082   /* [AS] Snapping */\r
8083   case WM_ENTERSIZEMOVE:\r
8084     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8085 \r
8086   case WM_SIZING:\r
8087     return OnSizing( &sd, hDlg, wParam, lParam );\r
8088 \r
8089   case WM_MOVING:\r
8090     return OnMoving( &sd, hDlg, wParam, lParam );\r
8091 \r
8092   case WM_EXITSIZEMOVE:\r
8093         UpdateICSWidth(hText);\r
8094     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8095   }\r
8096 \r
8097   return DefWindowProc(hDlg, message, wParam, lParam);\r
8098 }\r
8099 \r
8100 \r
8101 VOID\r
8102 ConsoleCreate()\r
8103 {\r
8104   HWND hCons;\r
8105   if (hwndConsole) return;\r
8106   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8107   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8108 }\r
8109 \r
8110 \r
8111 VOID\r
8112 ConsoleOutput(char* data, int length, int forceVisible)\r
8113 {\r
8114   HWND hText;\r
8115   int trim, exlen;\r
8116   char *p, *q;\r
8117   char buf[CO_MAX+1];\r
8118   POINT pEnd;\r
8119   RECT rect;\r
8120   static int delayLF = 0;\r
8121   CHARRANGE savesel, sel;\r
8122 \r
8123   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8124   p = data;\r
8125   q = buf;\r
8126   if (delayLF) {\r
8127     *q++ = '\r';\r
8128     *q++ = '\n';\r
8129     delayLF = 0;\r
8130   }\r
8131   while (length--) {\r
8132     if (*p == '\n') {\r
8133       if (*++p) {\r
8134         *q++ = '\r';\r
8135         *q++ = '\n';\r
8136       } else {\r
8137         delayLF = 1;\r
8138       }\r
8139     } else if (*p == '\007') {\r
8140        MyPlaySound(&sounds[(int)SoundBell]);\r
8141        p++;\r
8142     } else {\r
8143       *q++ = *p++;\r
8144     }\r
8145   }\r
8146   *q = NULLCHAR;\r
8147   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8148   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8149   /* Save current selection */\r
8150   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8151   exlen = GetWindowTextLength(hText);\r
8152   /* Find out whether current end of text is visible */\r
8153   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8154   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8155   /* Trim existing text if it's too long */\r
8156   if (exlen + (q - buf) > CO_MAX) {\r
8157     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8158     sel.cpMin = 0;\r
8159     sel.cpMax = trim;\r
8160     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8161     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8162     exlen -= trim;\r
8163     savesel.cpMin -= trim;\r
8164     savesel.cpMax -= trim;\r
8165     if (exlen < 0) exlen = 0;\r
8166     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8167     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8168   }\r
8169   /* Append the new text */\r
8170   sel.cpMin = exlen;\r
8171   sel.cpMax = exlen;\r
8172   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8173   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8174   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8175   if (forceVisible || exlen == 0 ||\r
8176       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8177        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8178     /* Scroll to make new end of text visible if old end of text\r
8179        was visible or new text is an echo of user typein */\r
8180     sel.cpMin = 9999999;\r
8181     sel.cpMax = 9999999;\r
8182     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8183     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8184     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8185     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8186   }\r
8187   if (savesel.cpMax == exlen || forceVisible) {\r
8188     /* Move insert point to new end of text if it was at the old\r
8189        end of text or if the new text is an echo of user typein */\r
8190     sel.cpMin = 9999999;\r
8191     sel.cpMax = 9999999;\r
8192     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8193   } else {\r
8194     /* Restore previous selection */\r
8195     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8196   }\r
8197   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8198 }\r
8199 \r
8200 /*---------*/\r
8201 \r
8202 \r
8203 void\r
8204 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8205 {\r
8206   char buf[100];\r
8207   char *str;\r
8208   COLORREF oldFg, oldBg;\r
8209   HFONT oldFont;\r
8210   RECT rect;\r
8211 \r
8212   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8213 \r
8214   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8215   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8216   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8217 \r
8218   rect.left = x;\r
8219   rect.right = x + squareSize;\r
8220   rect.top  = y;\r
8221   rect.bottom = y + squareSize;\r
8222   str = buf;\r
8223 \r
8224   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8225                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8226              y, ETO_CLIPPED|ETO_OPAQUE,\r
8227              &rect, str, strlen(str), NULL);\r
8228 \r
8229   (void) SetTextColor(hdc, oldFg);\r
8230   (void) SetBkColor(hdc, oldBg);\r
8231   (void) SelectObject(hdc, oldFont);\r
8232 }\r
8233 \r
8234 void\r
8235 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8236               RECT *rect, char *color, char *flagFell)\r
8237 {\r
8238   char buf[100];\r
8239   char *str;\r
8240   COLORREF oldFg, oldBg;\r
8241   HFONT oldFont;\r
8242 \r
8243   if (appData.clockMode) {\r
8244     if (tinyLayout)\r
8245       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8246     else\r
8247       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8248     str = buf;\r
8249   } else {\r
8250     str = color;\r
8251   }\r
8252 \r
8253   if (highlight) {\r
8254     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8255     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8256   } else {\r
8257     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8258     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8259   }\r
8260   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8261 \r
8262   JAWS_SILENCE\r
8263 \r
8264   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8265              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8266              rect, str, strlen(str), NULL);\r
8267   if(logoHeight > 0 && appData.clockMode) {\r
8268       RECT r;\r
8269       sprintf(buf, "%s %s", buf+7, flagFell);\r
8270       r.top = rect->top + logoHeight/2;\r
8271       r.left = rect->left;\r
8272       r.right = rect->right;\r
8273       r.bottom = rect->bottom;\r
8274       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8275                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8276                  &r, str, strlen(str), NULL);\r
8277   }\r
8278   (void) SetTextColor(hdc, oldFg);\r
8279   (void) SetBkColor(hdc, oldBg);\r
8280   (void) SelectObject(hdc, oldFont);\r
8281 }\r
8282 \r
8283 \r
8284 int\r
8285 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8286            OVERLAPPED *ovl)\r
8287 {\r
8288   int ok, err;\r
8289 \r
8290   /* [AS]  */\r
8291   if( count <= 0 ) {\r
8292     if (appData.debugMode) {\r
8293       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8294     }\r
8295 \r
8296     return ERROR_INVALID_USER_BUFFER;\r
8297   }\r
8298 \r
8299   ResetEvent(ovl->hEvent);\r
8300   ovl->Offset = ovl->OffsetHigh = 0;\r
8301   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8302   if (ok) {\r
8303     err = NO_ERROR;\r
8304   } else {\r
8305     err = GetLastError();\r
8306     if (err == ERROR_IO_PENDING) {\r
8307       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8308       if (ok)\r
8309         err = NO_ERROR;\r
8310       else\r
8311         err = GetLastError();\r
8312     }\r
8313   }\r
8314   return err;\r
8315 }\r
8316 \r
8317 int\r
8318 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8319             OVERLAPPED *ovl)\r
8320 {\r
8321   int ok, err;\r
8322 \r
8323   ResetEvent(ovl->hEvent);\r
8324   ovl->Offset = ovl->OffsetHigh = 0;\r
8325   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8326   if (ok) {\r
8327     err = NO_ERROR;\r
8328   } else {\r
8329     err = GetLastError();\r
8330     if (err == ERROR_IO_PENDING) {\r
8331       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8332       if (ok)\r
8333         err = NO_ERROR;\r
8334       else\r
8335         err = GetLastError();\r
8336     }\r
8337   }\r
8338   return err;\r
8339 }\r
8340 \r
8341 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8342 void CheckForInputBufferFull( InputSource * is )\r
8343 {\r
8344     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8345         /* Look for end of line */\r
8346         char * p = is->buf;\r
8347         \r
8348         while( p < is->next && *p != '\n' ) {\r
8349             p++;\r
8350         }\r
8351 \r
8352         if( p >= is->next ) {\r
8353             if (appData.debugMode) {\r
8354                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8355             }\r
8356 \r
8357             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8358             is->count = (DWORD) -1;\r
8359             is->next = is->buf;\r
8360         }\r
8361     }\r
8362 }\r
8363 \r
8364 DWORD\r
8365 InputThread(LPVOID arg)\r
8366 {\r
8367   InputSource *is;\r
8368   OVERLAPPED ovl;\r
8369 \r
8370   is = (InputSource *) arg;\r
8371   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8372   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8373   while (is->hThread != NULL) {\r
8374     is->error = DoReadFile(is->hFile, is->next,\r
8375                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8376                            &is->count, &ovl);\r
8377     if (is->error == NO_ERROR) {\r
8378       is->next += is->count;\r
8379     } else {\r
8380       if (is->error == ERROR_BROKEN_PIPE) {\r
8381         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8382         is->count = 0;\r
8383       } else {\r
8384         is->count = (DWORD) -1;\r
8385         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8386         break; \r
8387       }\r
8388     }\r
8389 \r
8390     CheckForInputBufferFull( is );\r
8391 \r
8392     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8393 \r
8394     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8395 \r
8396     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8397   }\r
8398 \r
8399   CloseHandle(ovl.hEvent);\r
8400   CloseHandle(is->hFile);\r
8401 \r
8402   if (appData.debugMode) {\r
8403     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8404   }\r
8405 \r
8406   return 0;\r
8407 }\r
8408 \r
8409 \r
8410 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8411 DWORD\r
8412 NonOvlInputThread(LPVOID arg)\r
8413 {\r
8414   InputSource *is;\r
8415   char *p, *q;\r
8416   int i;\r
8417   char prev;\r
8418 \r
8419   is = (InputSource *) arg;\r
8420   while (is->hThread != NULL) {\r
8421     is->error = ReadFile(is->hFile, is->next,\r
8422                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8423                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8424     if (is->error == NO_ERROR) {\r
8425       /* Change CRLF to LF */\r
8426       if (is->next > is->buf) {\r
8427         p = is->next - 1;\r
8428         i = is->count + 1;\r
8429       } else {\r
8430         p = is->next;\r
8431         i = is->count;\r
8432       }\r
8433       q = p;\r
8434       prev = NULLCHAR;\r
8435       while (i > 0) {\r
8436         if (prev == '\r' && *p == '\n') {\r
8437           *(q-1) = '\n';\r
8438           is->count--;\r
8439         } else { \r
8440           *q++ = *p;\r
8441         }\r
8442         prev = *p++;\r
8443         i--;\r
8444       }\r
8445       *q = NULLCHAR;\r
8446       is->next = q;\r
8447     } else {\r
8448       if (is->error == ERROR_BROKEN_PIPE) {\r
8449         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8450         is->count = 0; \r
8451       } else {\r
8452         is->count = (DWORD) -1;\r
8453       }\r
8454     }\r
8455 \r
8456     CheckForInputBufferFull( is );\r
8457 \r
8458     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8459 \r
8460     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8461 \r
8462     if (is->count < 0) break;  /* Quit on error */\r
8463   }\r
8464   CloseHandle(is->hFile);\r
8465   return 0;\r
8466 }\r
8467 \r
8468 DWORD\r
8469 SocketInputThread(LPVOID arg)\r
8470 {\r
8471   InputSource *is;\r
8472 \r
8473   is = (InputSource *) arg;\r
8474   while (is->hThread != NULL) {\r
8475     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8476     if ((int)is->count == SOCKET_ERROR) {\r
8477       is->count = (DWORD) -1;\r
8478       is->error = WSAGetLastError();\r
8479     } else {\r
8480       is->error = NO_ERROR;\r
8481       is->next += is->count;\r
8482       if (is->count == 0 && is->second == is) {\r
8483         /* End of file on stderr; quit with no message */\r
8484         break;\r
8485       }\r
8486     }\r
8487     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8488 \r
8489     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8490 \r
8491     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8492   }\r
8493   return 0;\r
8494 }\r
8495 \r
8496 VOID\r
8497 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8498 {\r
8499   InputSource *is;\r
8500 \r
8501   is = (InputSource *) lParam;\r
8502   if (is->lineByLine) {\r
8503     /* Feed in lines one by one */\r
8504     char *p = is->buf;\r
8505     char *q = p;\r
8506     while (q < is->next) {\r
8507       if (*q++ == '\n') {\r
8508         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8509         p = q;\r
8510       }\r
8511     }\r
8512     \r
8513     /* Move any partial line to the start of the buffer */\r
8514     q = is->buf;\r
8515     while (p < is->next) {\r
8516       *q++ = *p++;\r
8517     }\r
8518     is->next = q;\r
8519 \r
8520     if (is->error != NO_ERROR || is->count == 0) {\r
8521       /* Notify backend of the error.  Note: If there was a partial\r
8522          line at the end, it is not flushed through. */\r
8523       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8524     }\r
8525   } else {\r
8526     /* Feed in the whole chunk of input at once */\r
8527     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8528     is->next = is->buf;\r
8529   }\r
8530 }\r
8531 \r
8532 /*---------------------------------------------------------------------------*\\r
8533  *\r
8534  *  Menu enables. Used when setting various modes.\r
8535  *\r
8536 \*---------------------------------------------------------------------------*/\r
8537 \r
8538 typedef struct {\r
8539   int item;\r
8540   int flags;\r
8541 } Enables;\r
8542 \r
8543 VOID\r
8544 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8545 {\r
8546   while (enab->item > 0) {\r
8547     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8548     enab++;\r
8549   }\r
8550 }\r
8551 \r
8552 Enables gnuEnables[] = {\r
8553   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8560   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8561   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8564   { -1, -1 }\r
8565 };\r
8566 \r
8567 Enables icsEnables[] = {\r
8568   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8570   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8574   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8575   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8576   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8578   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8580   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8581   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8583   { -1, -1 }\r
8584 };\r
8585 \r
8586 #ifdef ZIPPY\r
8587 Enables zippyEnables[] = {\r
8588   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8589   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8590   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8591   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8592   { -1, -1 }\r
8593 };\r
8594 #endif\r
8595 \r
8596 Enables ncpEnables[] = {\r
8597   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8598   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8605   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8606   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8616   { -1, -1 }\r
8617 };\r
8618 \r
8619 Enables trainingOnEnables[] = {\r
8620   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8621   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8622   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8623   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8624   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8625   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8626   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8627   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8628   { -1, -1 }\r
8629 };\r
8630 \r
8631 Enables trainingOffEnables[] = {\r
8632   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8633   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8634   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8635   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8636   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8637   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8638   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8639   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8640   { -1, -1 }\r
8641 };\r
8642 \r
8643 /* These modify either ncpEnables or gnuEnables */\r
8644 Enables cmailEnables[] = {\r
8645   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8646   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8647   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8648   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8650   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8651   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8652   { -1, -1 }\r
8653 };\r
8654 \r
8655 Enables machineThinkingEnables[] = {\r
8656   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8657   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8658   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8659   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8660   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8661   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8662   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8663   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8664   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8665   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8666   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8667   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8668   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8669   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8670   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8671   { -1, -1 }\r
8672 };\r
8673 \r
8674 Enables userThinkingEnables[] = {\r
8675   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8676   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8677   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8678   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8679   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8680   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8681   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8682   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8683   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8684   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8685   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8686   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8687   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8688   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8689   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8690   { -1, -1 }\r
8691 };\r
8692 \r
8693 /*---------------------------------------------------------------------------*\\r
8694  *\r
8695  *  Front-end interface functions exported by XBoard.\r
8696  *  Functions appear in same order as prototypes in frontend.h.\r
8697  * \r
8698 \*---------------------------------------------------------------------------*/\r
8699 VOID\r
8700 ModeHighlight()\r
8701 {\r
8702   static UINT prevChecked = 0;\r
8703   static int prevPausing = 0;\r
8704   UINT nowChecked;\r
8705 \r
8706   if (pausing != prevPausing) {\r
8707     prevPausing = pausing;\r
8708     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8709                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8710     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8711   }\r
8712 \r
8713   switch (gameMode) {\r
8714   case BeginningOfGame:\r
8715     if (appData.icsActive)\r
8716       nowChecked = IDM_IcsClient;\r
8717     else if (appData.noChessProgram)\r
8718       nowChecked = IDM_EditGame;\r
8719     else\r
8720       nowChecked = IDM_MachineBlack;\r
8721     break;\r
8722   case MachinePlaysBlack:\r
8723     nowChecked = IDM_MachineBlack;\r
8724     break;\r
8725   case MachinePlaysWhite:\r
8726     nowChecked = IDM_MachineWhite;\r
8727     break;\r
8728   case TwoMachinesPlay:\r
8729     nowChecked = IDM_TwoMachines;\r
8730     break;\r
8731   case AnalyzeMode:\r
8732     nowChecked = IDM_AnalysisMode;\r
8733     break;\r
8734   case AnalyzeFile:\r
8735     nowChecked = IDM_AnalyzeFile;\r
8736     break;\r
8737   case EditGame:\r
8738     nowChecked = IDM_EditGame;\r
8739     break;\r
8740   case PlayFromGameFile:\r
8741     nowChecked = IDM_LoadGame;\r
8742     break;\r
8743   case EditPosition:\r
8744     nowChecked = IDM_EditPosition;\r
8745     break;\r
8746   case Training:\r
8747     nowChecked = IDM_Training;\r
8748     break;\r
8749   case IcsPlayingWhite:\r
8750   case IcsPlayingBlack:\r
8751   case IcsObserving:\r
8752   case IcsIdle:\r
8753     nowChecked = IDM_IcsClient;\r
8754     break;\r
8755   default:\r
8756   case EndOfGame:\r
8757     nowChecked = 0;\r
8758     break;\r
8759   }\r
8760   if (prevChecked != 0)\r
8761     (void) CheckMenuItem(GetMenu(hwndMain),\r
8762                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8763   if (nowChecked != 0)\r
8764     (void) CheckMenuItem(GetMenu(hwndMain),\r
8765                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8766 \r
8767   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8768     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8769                           MF_BYCOMMAND|MF_ENABLED);\r
8770   } else {\r
8771     (void) EnableMenuItem(GetMenu(hwndMain), \r
8772                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8773   }\r
8774 \r
8775   prevChecked = nowChecked;\r
8776 \r
8777   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8778   if (appData.icsActive) {\r
8779        if (appData.icsEngineAnalyze) {\r
8780                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8781                        MF_BYCOMMAND|MF_CHECKED);\r
8782        } else {\r
8783                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8784                        MF_BYCOMMAND|MF_UNCHECKED);\r
8785        }\r
8786   }\r
8787 }\r
8788 \r
8789 VOID\r
8790 SetICSMode()\r
8791 {\r
8792   HMENU hmenu = GetMenu(hwndMain);\r
8793   SetMenuEnables(hmenu, icsEnables);\r
8794   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8795     MF_BYPOSITION|MF_ENABLED);\r
8796 #ifdef ZIPPY\r
8797   if (appData.zippyPlay) {\r
8798     SetMenuEnables(hmenu, zippyEnables);\r
8799     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8800          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8801           MF_BYCOMMAND|MF_ENABLED);\r
8802   }\r
8803 #endif\r
8804 }\r
8805 \r
8806 VOID\r
8807 SetGNUMode()\r
8808 {\r
8809   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8810 }\r
8811 \r
8812 VOID\r
8813 SetNCPMode()\r
8814 {\r
8815   HMENU hmenu = GetMenu(hwndMain);\r
8816   SetMenuEnables(hmenu, ncpEnables);\r
8817   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8818     MF_BYPOSITION|MF_GRAYED);\r
8819     DrawMenuBar(hwndMain);\r
8820 }\r
8821 \r
8822 VOID\r
8823 SetCmailMode()\r
8824 {\r
8825   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8826 }\r
8827 \r
8828 VOID \r
8829 SetTrainingModeOn()\r
8830 {\r
8831   int i;\r
8832   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8833   for (i = 0; i < N_BUTTONS; i++) {\r
8834     if (buttonDesc[i].hwnd != NULL)\r
8835       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8836   }\r
8837   CommentPopDown();\r
8838 }\r
8839 \r
8840 VOID SetTrainingModeOff()\r
8841 {\r
8842   int i;\r
8843   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8844   for (i = 0; i < N_BUTTONS; i++) {\r
8845     if (buttonDesc[i].hwnd != NULL)\r
8846       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8847   }\r
8848 }\r
8849 \r
8850 \r
8851 VOID\r
8852 SetUserThinkingEnables()\r
8853 {\r
8854   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8855 }\r
8856 \r
8857 VOID\r
8858 SetMachineThinkingEnables()\r
8859 {\r
8860   HMENU hMenu = GetMenu(hwndMain);\r
8861   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8862 \r
8863   SetMenuEnables(hMenu, machineThinkingEnables);\r
8864 \r
8865   if (gameMode == MachinePlaysBlack) {\r
8866     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8867   } else if (gameMode == MachinePlaysWhite) {\r
8868     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8869   } else if (gameMode == TwoMachinesPlay) {\r
8870     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8871   }\r
8872 }\r
8873 \r
8874 \r
8875 VOID\r
8876 DisplayTitle(char *str)\r
8877 {\r
8878   char title[MSG_SIZ], *host;\r
8879   if (str[0] != NULLCHAR) {\r
8880     strcpy(title, str);\r
8881   } else if (appData.icsActive) {\r
8882     if (appData.icsCommPort[0] != NULLCHAR)\r
8883       host = "ICS";\r
8884     else \r
8885       host = appData.icsHost;\r
8886     sprintf(title, "%s: %s", szTitle, host);\r
8887   } else if (appData.noChessProgram) {\r
8888     strcpy(title, szTitle);\r
8889   } else {\r
8890     strcpy(title, szTitle);\r
8891     strcat(title, ": ");\r
8892     strcat(title, first.tidy);\r
8893   }\r
8894   SetWindowText(hwndMain, title);\r
8895 }\r
8896 \r
8897 \r
8898 VOID\r
8899 DisplayMessage(char *str1, char *str2)\r
8900 {\r
8901   HDC hdc;\r
8902   HFONT oldFont;\r
8903   int remain = MESSAGE_TEXT_MAX - 1;\r
8904   int len;\r
8905 \r
8906   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8907   messageText[0] = NULLCHAR;\r
8908   if (*str1) {\r
8909     len = strlen(str1);\r
8910     if (len > remain) len = remain;\r
8911     strncpy(messageText, str1, len);\r
8912     messageText[len] = NULLCHAR;\r
8913     remain -= len;\r
8914   }\r
8915   if (*str2 && remain >= 2) {\r
8916     if (*str1) {\r
8917       strcat(messageText, "  ");\r
8918       remain -= 2;\r
8919     }\r
8920     len = strlen(str2);\r
8921     if (len > remain) len = remain;\r
8922     strncat(messageText, str2, len);\r
8923   }\r
8924   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8925 \r
8926   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8927 \r
8928   SAYMACHINEMOVE();\r
8929 \r
8930   hdc = GetDC(hwndMain);\r
8931   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8932   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8933              &messageRect, messageText, strlen(messageText), NULL);\r
8934   (void) SelectObject(hdc, oldFont);\r
8935   (void) ReleaseDC(hwndMain, hdc);\r
8936 }\r
8937 \r
8938 VOID\r
8939 DisplayError(char *str, int error)\r
8940 {\r
8941   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8942   int len;\r
8943 \r
8944   if (error == 0) {\r
8945     strcpy(buf, str);\r
8946   } else {\r
8947     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8948                         NULL, error, LANG_NEUTRAL,\r
8949                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8950     if (len > 0) {\r
8951       sprintf(buf, "%s:\n%s", str, buf2);\r
8952     } else {\r
8953       ErrorMap *em = errmap;\r
8954       while (em->err != 0 && em->err != error) em++;\r
8955       if (em->err != 0) {\r
8956         sprintf(buf, "%s:\n%s", str, em->msg);\r
8957       } else {\r
8958         sprintf(buf, "%s:\nError code %d", str, error);\r
8959       }\r
8960     }\r
8961   }\r
8962   \r
8963   ErrorPopUp("Error", buf);\r
8964 }\r
8965 \r
8966 \r
8967 VOID\r
8968 DisplayMoveError(char *str)\r
8969 {\r
8970   fromX = fromY = -1;\r
8971   ClearHighlights();\r
8972   DrawPosition(FALSE, NULL);\r
8973   if (appData.popupMoveErrors) {\r
8974     ErrorPopUp("Error", str);\r
8975   } else {\r
8976     DisplayMessage(str, "");\r
8977     moveErrorMessageUp = TRUE;\r
8978   }\r
8979 }\r
8980 \r
8981 VOID\r
8982 DisplayFatalError(char *str, int error, int exitStatus)\r
8983 {\r
8984   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8985   int len;\r
8986   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8987 \r
8988   if (error != 0) {\r
8989     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8990                         NULL, error, LANG_NEUTRAL,\r
8991                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8992     if (len > 0) {\r
8993       sprintf(buf, "%s:\n%s", str, buf2);\r
8994     } else {\r
8995       ErrorMap *em = errmap;\r
8996       while (em->err != 0 && em->err != error) em++;\r
8997       if (em->err != 0) {\r
8998         sprintf(buf, "%s:\n%s", str, em->msg);\r
8999       } else {\r
9000         sprintf(buf, "%s:\nError code %d", str, error);\r
9001       }\r
9002     }\r
9003     str = buf;\r
9004   }\r
9005   if (appData.debugMode) {\r
9006     fprintf(debugFP, "%s: %s\n", label, str);\r
9007   }\r
9008   if (appData.popupExitMessage) {\r
9009     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9010                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9011   }\r
9012   ExitEvent(exitStatus);\r
9013 }\r
9014 \r
9015 \r
9016 VOID\r
9017 DisplayInformation(char *str)\r
9018 {\r
9019   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9020 }\r
9021 \r
9022 \r
9023 VOID\r
9024 DisplayNote(char *str)\r
9025 {\r
9026   ErrorPopUp("Note", str);\r
9027 }\r
9028 \r
9029 \r
9030 typedef struct {\r
9031   char *title, *question, *replyPrefix;\r
9032   ProcRef pr;\r
9033 } QuestionParams;\r
9034 \r
9035 LRESULT CALLBACK\r
9036 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9037 {\r
9038   static QuestionParams *qp;\r
9039   char reply[MSG_SIZ];\r
9040   int len, err;\r
9041 \r
9042   switch (message) {\r
9043   case WM_INITDIALOG:\r
9044     qp = (QuestionParams *) lParam;\r
9045     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9046     SetWindowText(hDlg, qp->title);\r
9047     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9048     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9049     return FALSE;\r
9050 \r
9051   case WM_COMMAND:\r
9052     switch (LOWORD(wParam)) {\r
9053     case IDOK:\r
9054       strcpy(reply, qp->replyPrefix);\r
9055       if (*reply) strcat(reply, " ");\r
9056       len = strlen(reply);\r
9057       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9058       strcat(reply, "\n");\r
9059       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9060       EndDialog(hDlg, TRUE);\r
9061       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9062       return TRUE;\r
9063     case IDCANCEL:\r
9064       EndDialog(hDlg, FALSE);\r
9065       return TRUE;\r
9066     default:\r
9067       break;\r
9068     }\r
9069     break;\r
9070   }\r
9071   return FALSE;\r
9072 }\r
9073 \r
9074 VOID\r
9075 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9076 {\r
9077     QuestionParams qp;\r
9078     FARPROC lpProc;\r
9079     \r
9080     qp.title = title;\r
9081     qp.question = question;\r
9082     qp.replyPrefix = replyPrefix;\r
9083     qp.pr = pr;\r
9084     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9085     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9086       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9087     FreeProcInstance(lpProc);\r
9088 }\r
9089 \r
9090 /* [AS] Pick FRC position */\r
9091 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9092 {\r
9093     static int * lpIndexFRC;\r
9094     BOOL index_is_ok;\r
9095     char buf[16];\r
9096 \r
9097     switch( message )\r
9098     {\r
9099     case WM_INITDIALOG:\r
9100         lpIndexFRC = (int *) lParam;\r
9101 \r
9102         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9103 \r
9104         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9105         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9106         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9107         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9108 \r
9109         break;\r
9110 \r
9111     case WM_COMMAND:\r
9112         switch( LOWORD(wParam) ) {\r
9113         case IDOK:\r
9114             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9115             EndDialog( hDlg, 0 );\r
9116             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9117             return TRUE;\r
9118         case IDCANCEL:\r
9119             EndDialog( hDlg, 1 );   \r
9120             return TRUE;\r
9121         case IDC_NFG_Edit:\r
9122             if( HIWORD(wParam) == EN_CHANGE ) {\r
9123                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9124 \r
9125                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9126             }\r
9127             return TRUE;\r
9128         case IDC_NFG_Random:\r
9129             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9130             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9131             return TRUE;\r
9132         }\r
9133 \r
9134         break;\r
9135     }\r
9136 \r
9137     return FALSE;\r
9138 }\r
9139 \r
9140 int NewGameFRC()\r
9141 {\r
9142     int result;\r
9143     int index = appData.defaultFrcPosition;\r
9144     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9145 \r
9146     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9147 \r
9148     if( result == 0 ) {\r
9149         appData.defaultFrcPosition = index;\r
9150     }\r
9151 \r
9152     return result;\r
9153 }\r
9154 \r
9155 /* [AS] Game list options */\r
9156 typedef struct {\r
9157     char id;\r
9158     char * name;\r
9159 } GLT_Item;\r
9160 \r
9161 static GLT_Item GLT_ItemInfo[] = {\r
9162     { GLT_EVENT,      "Event" },\r
9163     { GLT_SITE,       "Site" },\r
9164     { GLT_DATE,       "Date" },\r
9165     { GLT_ROUND,      "Round" },\r
9166     { GLT_PLAYERS,    "Players" },\r
9167     { GLT_RESULT,     "Result" },\r
9168     { GLT_WHITE_ELO,  "White Rating" },\r
9169     { GLT_BLACK_ELO,  "Black Rating" },\r
9170     { GLT_TIME_CONTROL,"Time Control" },\r
9171     { GLT_VARIANT,    "Variant" },\r
9172     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9173     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9174     { 0, 0 }\r
9175 };\r
9176 \r
9177 const char * GLT_FindItem( char id )\r
9178 {\r
9179     const char * result = 0;\r
9180 \r
9181     GLT_Item * list = GLT_ItemInfo;\r
9182 \r
9183     while( list->id != 0 ) {\r
9184         if( list->id == id ) {\r
9185             result = list->name;\r
9186             break;\r
9187         }\r
9188 \r
9189         list++;\r
9190     }\r
9191 \r
9192     return result;\r
9193 }\r
9194 \r
9195 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9196 {\r
9197     const char * name = GLT_FindItem( id );\r
9198 \r
9199     if( name != 0 ) {\r
9200         if( index >= 0 ) {\r
9201             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9202         }\r
9203         else {\r
9204             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9205         }\r
9206     }\r
9207 }\r
9208 \r
9209 void GLT_TagsToList( HWND hDlg, char * tags )\r
9210 {\r
9211     char * pc = tags;\r
9212 \r
9213     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9214 \r
9215     while( *pc ) {\r
9216         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9217         pc++;\r
9218     }\r
9219 \r
9220     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9221 \r
9222     pc = GLT_ALL_TAGS;\r
9223 \r
9224     while( *pc ) {\r
9225         if( strchr( tags, *pc ) == 0 ) {\r
9226             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9227         }\r
9228         pc++;\r
9229     }\r
9230 \r
9231     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9232 }\r
9233 \r
9234 char GLT_ListItemToTag( HWND hDlg, int index )\r
9235 {\r
9236     char result = '\0';\r
9237     char name[128];\r
9238 \r
9239     GLT_Item * list = GLT_ItemInfo;\r
9240 \r
9241     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9242         while( list->id != 0 ) {\r
9243             if( strcmp( list->name, name ) == 0 ) {\r
9244                 result = list->id;\r
9245                 break;\r
9246             }\r
9247 \r
9248             list++;\r
9249         }\r
9250     }\r
9251 \r
9252     return result;\r
9253 }\r
9254 \r
9255 void GLT_MoveSelection( HWND hDlg, int delta )\r
9256 {\r
9257     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9258     int idx2 = idx1 + delta;\r
9259     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9260 \r
9261     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9262         char buf[128];\r
9263 \r
9264         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9265         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9266         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9267         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9268     }\r
9269 }\r
9270 \r
9271 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9272 {\r
9273     static char glt[64];\r
9274     static char * lpUserGLT;\r
9275 \r
9276     switch( message )\r
9277     {\r
9278     case WM_INITDIALOG:\r
9279         lpUserGLT = (char *) lParam;\r
9280         \r
9281         strcpy( glt, lpUserGLT );\r
9282 \r
9283         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9284 \r
9285         /* Initialize list */\r
9286         GLT_TagsToList( hDlg, glt );\r
9287 \r
9288         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9289 \r
9290         break;\r
9291 \r
9292     case WM_COMMAND:\r
9293         switch( LOWORD(wParam) ) {\r
9294         case IDOK:\r
9295             {\r
9296                 char * pc = lpUserGLT;\r
9297                 int idx = 0;\r
9298 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9299                 char id;\r
9300 \r
9301                 do {\r
9302                     id = GLT_ListItemToTag( hDlg, idx );\r
9303 \r
9304                     *pc++ = id;\r
9305                     idx++;\r
9306                 } while( id != '\0' );\r
9307             }\r
9308             EndDialog( hDlg, 0 );\r
9309             return TRUE;\r
9310         case IDCANCEL:\r
9311             EndDialog( hDlg, 1 );\r
9312             return TRUE;\r
9313 \r
9314         case IDC_GLT_Default:\r
9315             strcpy( glt, GLT_DEFAULT_TAGS );\r
9316             GLT_TagsToList( hDlg, glt );\r
9317             return TRUE;\r
9318 \r
9319         case IDC_GLT_Restore:\r
9320             strcpy( glt, lpUserGLT );\r
9321             GLT_TagsToList( hDlg, glt );\r
9322             return TRUE;\r
9323 \r
9324         case IDC_GLT_Up:\r
9325             GLT_MoveSelection( hDlg, -1 );\r
9326             return TRUE;\r
9327 \r
9328         case IDC_GLT_Down:\r
9329             GLT_MoveSelection( hDlg, +1 );\r
9330             return TRUE;\r
9331         }\r
9332 \r
9333         break;\r
9334     }\r
9335 \r
9336     return FALSE;\r
9337 }\r
9338 \r
9339 int GameListOptions()\r
9340 {\r
9341     char glt[64];\r
9342     int result;\r
9343     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9344 \r
9345     strcpy( glt, appData.gameListTags );\r
9346 \r
9347     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9348 \r
9349     if( result == 0 ) {\r
9350         /* [AS] Memory leak here! */\r
9351         appData.gameListTags = strdup( glt ); \r
9352     }\r
9353 \r
9354     return result;\r
9355 }\r
9356 \r
9357 \r
9358 VOID\r
9359 DisplayIcsInteractionTitle(char *str)\r
9360 {\r
9361   char consoleTitle[MSG_SIZ];\r
9362 \r
9363   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9364   SetWindowText(hwndConsole, consoleTitle);\r
9365 }\r
9366 \r
9367 void\r
9368 DrawPosition(int fullRedraw, Board board)\r
9369 {\r
9370   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9371 }\r
9372 \r
9373 void NotifyFrontendLogin()\r
9374 {\r
9375         if (hwndConsole)\r
9376                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9377 }\r
9378 \r
9379 VOID\r
9380 ResetFrontEnd()\r
9381 {\r
9382   fromX = fromY = -1;\r
9383   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9384     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9385     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9386     dragInfo.lastpos = dragInfo.pos;\r
9387     dragInfo.start.x = dragInfo.start.y = -1;\r
9388     dragInfo.from = dragInfo.start;\r
9389     ReleaseCapture();\r
9390     DrawPosition(TRUE, NULL);\r
9391   }\r
9392 }\r
9393 \r
9394 \r
9395 VOID\r
9396 CommentPopUp(char *title, char *str)\r
9397 {\r
9398   HWND hwnd = GetActiveWindow();\r
9399   EitherCommentPopUp(0, title, str, FALSE);\r
9400   SAY(str);\r
9401   SetActiveWindow(hwnd);\r
9402 }\r
9403 \r
9404 VOID\r
9405 CommentPopDown(void)\r
9406 {\r
9407   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9408   if (commentDialog) {\r
9409     ShowWindow(commentDialog, SW_HIDE);\r
9410   }\r
9411   commentDialogUp = FALSE;\r
9412 }\r
9413 \r
9414 VOID\r
9415 EditCommentPopUp(int index, char *title, char *str)\r
9416 {\r
9417   EitherCommentPopUp(index, title, str, TRUE);\r
9418 }\r
9419 \r
9420 \r
9421 VOID\r
9422 RingBell()\r
9423 {\r
9424   MyPlaySound(&sounds[(int)SoundMove]);\r
9425 }\r
9426 \r
9427 VOID PlayIcsWinSound()\r
9428 {\r
9429   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9430 }\r
9431 \r
9432 VOID PlayIcsLossSound()\r
9433 {\r
9434   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9435 }\r
9436 \r
9437 VOID PlayIcsDrawSound()\r
9438 {\r
9439   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9440 }\r
9441 \r
9442 VOID PlayIcsUnfinishedSound()\r
9443 {\r
9444   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9445 }\r
9446 \r
9447 VOID\r
9448 PlayAlarmSound()\r
9449 {\r
9450   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9451 }\r
9452 \r
9453 \r
9454 VOID\r
9455 EchoOn()\r
9456 {\r
9457   HWND hInput;\r
9458   consoleEcho = TRUE;\r
9459   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9460   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9461   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9462 }\r
9463 \r
9464 \r
9465 VOID\r
9466 EchoOff()\r
9467 {\r
9468   CHARFORMAT cf;\r
9469   HWND hInput;\r
9470   consoleEcho = FALSE;\r
9471   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9472   /* This works OK: set text and background both to the same color */\r
9473   cf = consoleCF;\r
9474   cf.crTextColor = COLOR_ECHOOFF;\r
9475   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9476   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9477 }\r
9478 \r
9479 /* No Raw()...? */\r
9480 \r
9481 void Colorize(ColorClass cc, int continuation)\r
9482 {\r
9483   currentColorClass = cc;\r
9484   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9485   consoleCF.crTextColor = textAttribs[cc].color;\r
9486   consoleCF.dwEffects = textAttribs[cc].effects;\r
9487   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9488 }\r
9489 \r
9490 char *\r
9491 UserName()\r
9492 {\r
9493   static char buf[MSG_SIZ];\r
9494   DWORD bufsiz = MSG_SIZ;\r
9495 \r
9496   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9497         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9498   }\r
9499   if (!GetUserName(buf, &bufsiz)) {\r
9500     /*DisplayError("Error getting user name", GetLastError());*/\r
9501     strcpy(buf, "User");\r
9502   }\r
9503   return buf;\r
9504 }\r
9505 \r
9506 char *\r
9507 HostName()\r
9508 {\r
9509   static char buf[MSG_SIZ];\r
9510   DWORD bufsiz = MSG_SIZ;\r
9511 \r
9512   if (!GetComputerName(buf, &bufsiz)) {\r
9513     /*DisplayError("Error getting host name", GetLastError());*/\r
9514     strcpy(buf, "Unknown");\r
9515   }\r
9516   return buf;\r
9517 }\r
9518 \r
9519 \r
9520 int\r
9521 ClockTimerRunning()\r
9522 {\r
9523   return clockTimerEvent != 0;\r
9524 }\r
9525 \r
9526 int\r
9527 StopClockTimer()\r
9528 {\r
9529   if (clockTimerEvent == 0) return FALSE;\r
9530   KillTimer(hwndMain, clockTimerEvent);\r
9531   clockTimerEvent = 0;\r
9532   return TRUE;\r
9533 }\r
9534 \r
9535 void\r
9536 StartClockTimer(long millisec)\r
9537 {\r
9538   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9539                              (UINT) millisec, NULL);\r
9540 }\r
9541 \r
9542 void\r
9543 DisplayWhiteClock(long timeRemaining, int highlight)\r
9544 {\r
9545   HDC hdc;\r
9546   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9547 \r
9548   if(appData.noGUI) return;\r
9549   hdc = GetDC(hwndMain);\r
9550   if (!IsIconic(hwndMain)) {\r
9551     DisplayAClock(hdc, timeRemaining, highlight, \r
9552                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9553   }\r
9554   if (highlight && iconCurrent == iconBlack) {\r
9555     iconCurrent = iconWhite;\r
9556     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9557     if (IsIconic(hwndMain)) {\r
9558       DrawIcon(hdc, 2, 2, iconCurrent);\r
9559     }\r
9560   }\r
9561   (void) ReleaseDC(hwndMain, hdc);\r
9562   if (hwndConsole)\r
9563     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9564 }\r
9565 \r
9566 void\r
9567 DisplayBlackClock(long timeRemaining, int highlight)\r
9568 {\r
9569   HDC hdc;\r
9570   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9571 \r
9572   if(appData.noGUI) return;\r
9573   hdc = GetDC(hwndMain);\r
9574   if (!IsIconic(hwndMain)) {\r
9575     DisplayAClock(hdc, timeRemaining, highlight, \r
9576                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9577   }\r
9578   if (highlight && iconCurrent == iconWhite) {\r
9579     iconCurrent = iconBlack;\r
9580     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9581     if (IsIconic(hwndMain)) {\r
9582       DrawIcon(hdc, 2, 2, iconCurrent);\r
9583     }\r
9584   }\r
9585   (void) ReleaseDC(hwndMain, hdc);\r
9586   if (hwndConsole)\r
9587     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9588 }\r
9589 \r
9590 \r
9591 int\r
9592 LoadGameTimerRunning()\r
9593 {\r
9594   return loadGameTimerEvent != 0;\r
9595 }\r
9596 \r
9597 int\r
9598 StopLoadGameTimer()\r
9599 {\r
9600   if (loadGameTimerEvent == 0) return FALSE;\r
9601   KillTimer(hwndMain, loadGameTimerEvent);\r
9602   loadGameTimerEvent = 0;\r
9603   return TRUE;\r
9604 }\r
9605 \r
9606 void\r
9607 StartLoadGameTimer(long millisec)\r
9608 {\r
9609   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9610                                 (UINT) millisec, NULL);\r
9611 }\r
9612 \r
9613 void\r
9614 AutoSaveGame()\r
9615 {\r
9616   char *defName;\r
9617   FILE *f;\r
9618   char fileTitle[MSG_SIZ];\r
9619 \r
9620   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9621   f = OpenFileDialog(hwndMain, "a", defName,\r
9622                      appData.oldSaveStyle ? "gam" : "pgn",\r
9623                      GAME_FILT, \r
9624                      "Save Game to File", NULL, fileTitle, NULL);\r
9625   if (f != NULL) {\r
9626     SaveGame(f, 0, "");\r
9627     fclose(f);\r
9628   }\r
9629 }\r
9630 \r
9631 \r
9632 void\r
9633 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9634 {\r
9635   if (delayedTimerEvent != 0) {\r
9636     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9637       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9638     }\r
9639     KillTimer(hwndMain, delayedTimerEvent);\r
9640     delayedTimerEvent = 0;\r
9641     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9642     delayedTimerCallback();\r
9643   }\r
9644   delayedTimerCallback = cb;\r
9645   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9646                                 (UINT) millisec, NULL);\r
9647 }\r
9648 \r
9649 DelayedEventCallback\r
9650 GetDelayedEvent()\r
9651 {\r
9652   if (delayedTimerEvent) {\r
9653     return delayedTimerCallback;\r
9654   } else {\r
9655     return NULL;\r
9656   }\r
9657 }\r
9658 \r
9659 void\r
9660 CancelDelayedEvent()\r
9661 {\r
9662   if (delayedTimerEvent) {\r
9663     KillTimer(hwndMain, delayedTimerEvent);\r
9664     delayedTimerEvent = 0;\r
9665   }\r
9666 }\r
9667 \r
9668 DWORD GetWin32Priority(int nice)\r
9669 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9670 /*\r
9671 REALTIME_PRIORITY_CLASS     0x00000100\r
9672 HIGH_PRIORITY_CLASS         0x00000080\r
9673 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9674 NORMAL_PRIORITY_CLASS       0x00000020\r
9675 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9676 IDLE_PRIORITY_CLASS         0x00000040\r
9677 */\r
9678         if (nice < -15) return 0x00000080;\r
9679         if (nice < 0)   return 0x00008000;\r
9680         if (nice == 0)  return 0x00000020;\r
9681         if (nice < 15)  return 0x00004000;\r
9682         return 0x00000040;\r
9683 }\r
9684 \r
9685 /* Start a child process running the given program.\r
9686    The process's standard output can be read from "from", and its\r
9687    standard input can be written to "to".\r
9688    Exit with fatal error if anything goes wrong.\r
9689    Returns an opaque pointer that can be used to destroy the process\r
9690    later.\r
9691 */\r
9692 int\r
9693 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9694 {\r
9695 #define BUFSIZE 4096\r
9696 \r
9697   HANDLE hChildStdinRd, hChildStdinWr,\r
9698     hChildStdoutRd, hChildStdoutWr;\r
9699   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9700   SECURITY_ATTRIBUTES saAttr;\r
9701   BOOL fSuccess;\r
9702   PROCESS_INFORMATION piProcInfo;\r
9703   STARTUPINFO siStartInfo;\r
9704   ChildProc *cp;\r
9705   char buf[MSG_SIZ];\r
9706   DWORD err;\r
9707 \r
9708   if (appData.debugMode) {\r
9709     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9710   }\r
9711 \r
9712   *pr = NoProc;\r
9713 \r
9714   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9715   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9716   saAttr.bInheritHandle = TRUE;\r
9717   saAttr.lpSecurityDescriptor = NULL;\r
9718 \r
9719   /*\r
9720    * The steps for redirecting child's STDOUT:\r
9721    *     1. Create anonymous pipe to be STDOUT for child.\r
9722    *     2. Create a noninheritable duplicate of read handle,\r
9723    *         and close the inheritable read handle.\r
9724    */\r
9725 \r
9726   /* Create a pipe for the child's STDOUT. */\r
9727   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9728     return GetLastError();\r
9729   }\r
9730 \r
9731   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9732   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9733                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9734                              FALSE,     /* not inherited */\r
9735                              DUPLICATE_SAME_ACCESS);\r
9736   if (! fSuccess) {\r
9737     return GetLastError();\r
9738   }\r
9739   CloseHandle(hChildStdoutRd);\r
9740 \r
9741   /*\r
9742    * The steps for redirecting child's STDIN:\r
9743    *     1. Create anonymous pipe to be STDIN for child.\r
9744    *     2. Create a noninheritable duplicate of write handle,\r
9745    *         and close the inheritable write handle.\r
9746    */\r
9747 \r
9748   /* Create a pipe for the child's STDIN. */\r
9749   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9750     return GetLastError();\r
9751   }\r
9752 \r
9753   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9754   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9755                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9756                              FALSE,     /* not inherited */\r
9757                              DUPLICATE_SAME_ACCESS);\r
9758   if (! fSuccess) {\r
9759     return GetLastError();\r
9760   }\r
9761   CloseHandle(hChildStdinWr);\r
9762 \r
9763   /* Arrange to (1) look in dir for the child .exe file, and\r
9764    * (2) have dir be the child's working directory.  Interpret\r
9765    * dir relative to the directory WinBoard loaded from. */\r
9766   GetCurrentDirectory(MSG_SIZ, buf);\r
9767   SetCurrentDirectory(installDir);\r
9768   SetCurrentDirectory(dir);\r
9769 \r
9770   /* Now create the child process. */\r
9771 \r
9772   siStartInfo.cb = sizeof(STARTUPINFO);\r
9773   siStartInfo.lpReserved = NULL;\r
9774   siStartInfo.lpDesktop = NULL;\r
9775   siStartInfo.lpTitle = NULL;\r
9776   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9777   siStartInfo.cbReserved2 = 0;\r
9778   siStartInfo.lpReserved2 = NULL;\r
9779   siStartInfo.hStdInput = hChildStdinRd;\r
9780   siStartInfo.hStdOutput = hChildStdoutWr;\r
9781   siStartInfo.hStdError = hChildStdoutWr;\r
9782 \r
9783   fSuccess = CreateProcess(NULL,\r
9784                            cmdLine,        /* command line */\r
9785                            NULL,           /* process security attributes */\r
9786                            NULL,           /* primary thread security attrs */\r
9787                            TRUE,           /* handles are inherited */\r
9788                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9789                            NULL,           /* use parent's environment */\r
9790                            NULL,\r
9791                            &siStartInfo, /* STARTUPINFO pointer */\r
9792                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9793 \r
9794   err = GetLastError();\r
9795   SetCurrentDirectory(buf); /* return to prev directory */\r
9796   if (! fSuccess) {\r
9797     return err;\r
9798   }\r
9799 \r
9800   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9801     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9802     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9803   }\r
9804 \r
9805   /* Close the handles we don't need in the parent */\r
9806   CloseHandle(piProcInfo.hThread);\r
9807   CloseHandle(hChildStdinRd);\r
9808   CloseHandle(hChildStdoutWr);\r
9809 \r
9810   /* Prepare return value */\r
9811   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9812   cp->kind = CPReal;\r
9813   cp->hProcess = piProcInfo.hProcess;\r
9814   cp->pid = piProcInfo.dwProcessId;\r
9815   cp->hFrom = hChildStdoutRdDup;\r
9816   cp->hTo = hChildStdinWrDup;\r
9817 \r
9818   *pr = (void *) cp;\r
9819 \r
9820   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9821      2000 where engines sometimes don't see the initial command(s)\r
9822      from WinBoard and hang.  I don't understand how that can happen,\r
9823      but the Sleep is harmless, so I've put it in.  Others have also\r
9824      reported what may be the same problem, so hopefully this will fix\r
9825      it for them too.  */\r
9826   Sleep(500);\r
9827 \r
9828   return NO_ERROR;\r
9829 }\r
9830 \r
9831 \r
9832 void\r
9833 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9834 {\r
9835   ChildProc *cp; int result;\r
9836 \r
9837   cp = (ChildProc *) pr;\r
9838   if (cp == NULL) return;\r
9839 \r
9840   switch (cp->kind) {\r
9841   case CPReal:\r
9842     /* TerminateProcess is considered harmful, so... */\r
9843     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9844     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9845     /* The following doesn't work because the chess program\r
9846        doesn't "have the same console" as WinBoard.  Maybe\r
9847        we could arrange for this even though neither WinBoard\r
9848        nor the chess program uses a console for stdio? */\r
9849     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9850 \r
9851     /* [AS] Special termination modes for misbehaving programs... */\r
9852     if( signal == 9 ) { \r
9853         result = TerminateProcess( cp->hProcess, 0 );\r
9854 \r
9855         if ( appData.debugMode) {\r
9856             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9857         }\r
9858     }\r
9859     else if( signal == 10 ) {\r
9860         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9861 \r
9862         if( dw != WAIT_OBJECT_0 ) {\r
9863             result = TerminateProcess( cp->hProcess, 0 );\r
9864 \r
9865             if ( appData.debugMode) {\r
9866                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9867             }\r
9868 \r
9869         }\r
9870     }\r
9871 \r
9872     CloseHandle(cp->hProcess);\r
9873     break;\r
9874 \r
9875   case CPComm:\r
9876     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9877     break;\r
9878 \r
9879   case CPSock:\r
9880     closesocket(cp->sock);\r
9881     WSACleanup();\r
9882     break;\r
9883 \r
9884   case CPRcmd:\r
9885     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9886     closesocket(cp->sock);\r
9887     closesocket(cp->sock2);\r
9888     WSACleanup();\r
9889     break;\r
9890   }\r
9891   free(cp);\r
9892 }\r
9893 \r
9894 void\r
9895 InterruptChildProcess(ProcRef pr)\r
9896 {\r
9897   ChildProc *cp;\r
9898 \r
9899   cp = (ChildProc *) pr;\r
9900   if (cp == NULL) return;\r
9901   switch (cp->kind) {\r
9902   case CPReal:\r
9903     /* The following doesn't work because the chess program\r
9904        doesn't "have the same console" as WinBoard.  Maybe\r
9905        we could arrange for this even though neither WinBoard\r
9906        nor the chess program uses a console for stdio */\r
9907     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9908     break;\r
9909 \r
9910   case CPComm:\r
9911   case CPSock:\r
9912     /* Can't interrupt */\r
9913     break;\r
9914 \r
9915   case CPRcmd:\r
9916     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9917     break;\r
9918   }\r
9919 }\r
9920 \r
9921 \r
9922 int\r
9923 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9924 {\r
9925   char cmdLine[MSG_SIZ];\r
9926 \r
9927   if (port[0] == NULLCHAR) {\r
9928     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9929   } else {\r
9930     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9931   }\r
9932   return StartChildProcess(cmdLine, "", pr);\r
9933 }\r
9934 \r
9935 \r
9936 /* Code to open TCP sockets */\r
9937 \r
9938 int\r
9939 OpenTCP(char *host, char *port, ProcRef *pr)\r
9940 {\r
9941   ChildProc *cp;\r
9942   int err;\r
9943   SOCKET s;\r
9944   struct sockaddr_in sa, mysa;\r
9945   struct hostent FAR *hp;\r
9946   unsigned short uport;\r
9947   WORD wVersionRequested;\r
9948   WSADATA wsaData;\r
9949 \r
9950   /* Initialize socket DLL */\r
9951   wVersionRequested = MAKEWORD(1, 1);\r
9952   err = WSAStartup(wVersionRequested, &wsaData);\r
9953   if (err != 0) return err;\r
9954 \r
9955   /* Make socket */\r
9956   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9957     err = WSAGetLastError();\r
9958     WSACleanup();\r
9959     return err;\r
9960   }\r
9961 \r
9962   /* Bind local address using (mostly) don't-care values.\r
9963    */\r
9964   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9965   mysa.sin_family = AF_INET;\r
9966   mysa.sin_addr.s_addr = INADDR_ANY;\r
9967   uport = (unsigned short) 0;\r
9968   mysa.sin_port = htons(uport);\r
9969   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9970       == SOCKET_ERROR) {\r
9971     err = WSAGetLastError();\r
9972     WSACleanup();\r
9973     return err;\r
9974   }\r
9975 \r
9976   /* Resolve remote host name */\r
9977   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9978   if (!(hp = gethostbyname(host))) {\r
9979     unsigned int b0, b1, b2, b3;\r
9980 \r
9981     err = WSAGetLastError();\r
9982 \r
9983     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9984       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9985       hp->h_addrtype = AF_INET;\r
9986       hp->h_length = 4;\r
9987       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9988       hp->h_addr_list[0] = (char *) malloc(4);\r
9989       hp->h_addr_list[0][0] = (char) b0;\r
9990       hp->h_addr_list[0][1] = (char) b1;\r
9991       hp->h_addr_list[0][2] = (char) b2;\r
9992       hp->h_addr_list[0][3] = (char) b3;\r
9993     } else {\r
9994       WSACleanup();\r
9995       return err;\r
9996     }\r
9997   }\r
9998   sa.sin_family = hp->h_addrtype;\r
9999   uport = (unsigned short) atoi(port);\r
10000   sa.sin_port = htons(uport);\r
10001   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10002 \r
10003   /* Make connection */\r
10004   if (connect(s, (struct sockaddr *) &sa,\r
10005               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10006     err = WSAGetLastError();\r
10007     WSACleanup();\r
10008     return err;\r
10009   }\r
10010 \r
10011   /* Prepare return value */\r
10012   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10013   cp->kind = CPSock;\r
10014   cp->sock = s;\r
10015   *pr = (ProcRef *) cp;\r
10016 \r
10017   return NO_ERROR;\r
10018 }\r
10019 \r
10020 int\r
10021 OpenCommPort(char *name, ProcRef *pr)\r
10022 {\r
10023   HANDLE h;\r
10024   COMMTIMEOUTS ct;\r
10025   ChildProc *cp;\r
10026   char fullname[MSG_SIZ];\r
10027 \r
10028   if (*name != '\\')\r
10029     sprintf(fullname, "\\\\.\\%s", name);\r
10030   else\r
10031     strcpy(fullname, name);\r
10032 \r
10033   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10034                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10035   if (h == (HANDLE) -1) {\r
10036     return GetLastError();\r
10037   }\r
10038   hCommPort = h;\r
10039 \r
10040   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10041 \r
10042   /* Accumulate characters until a 100ms pause, then parse */\r
10043   ct.ReadIntervalTimeout = 100;\r
10044   ct.ReadTotalTimeoutMultiplier = 0;\r
10045   ct.ReadTotalTimeoutConstant = 0;\r
10046   ct.WriteTotalTimeoutMultiplier = 0;\r
10047   ct.WriteTotalTimeoutConstant = 0;\r
10048   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10049 \r
10050   /* Prepare return value */\r
10051   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10052   cp->kind = CPComm;\r
10053   cp->hFrom = h;\r
10054   cp->hTo = h;\r
10055   *pr = (ProcRef *) cp;\r
10056 \r
10057   return NO_ERROR;\r
10058 }\r
10059 \r
10060 int\r
10061 OpenLoopback(ProcRef *pr)\r
10062 {\r
10063   DisplayFatalError("Not implemented", 0, 1);\r
10064   return NO_ERROR;\r
10065 }\r
10066 \r
10067 \r
10068 int\r
10069 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10070 {\r
10071   ChildProc *cp;\r
10072   int err;\r
10073   SOCKET s, s2, s3;\r
10074   struct sockaddr_in sa, mysa;\r
10075   struct hostent FAR *hp;\r
10076   unsigned short uport;\r
10077   WORD wVersionRequested;\r
10078   WSADATA wsaData;\r
10079   int fromPort;\r
10080   char stderrPortStr[MSG_SIZ];\r
10081 \r
10082   /* Initialize socket DLL */\r
10083   wVersionRequested = MAKEWORD(1, 1);\r
10084   err = WSAStartup(wVersionRequested, &wsaData);\r
10085   if (err != 0) return err;\r
10086 \r
10087   /* Resolve remote host name */\r
10088   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10089   if (!(hp = gethostbyname(host))) {\r
10090     unsigned int b0, b1, b2, b3;\r
10091 \r
10092     err = WSAGetLastError();\r
10093 \r
10094     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10095       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10096       hp->h_addrtype = AF_INET;\r
10097       hp->h_length = 4;\r
10098       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10099       hp->h_addr_list[0] = (char *) malloc(4);\r
10100       hp->h_addr_list[0][0] = (char) b0;\r
10101       hp->h_addr_list[0][1] = (char) b1;\r
10102       hp->h_addr_list[0][2] = (char) b2;\r
10103       hp->h_addr_list[0][3] = (char) b3;\r
10104     } else {\r
10105       WSACleanup();\r
10106       return err;\r
10107     }\r
10108   }\r
10109   sa.sin_family = hp->h_addrtype;\r
10110   uport = (unsigned short) 514;\r
10111   sa.sin_port = htons(uport);\r
10112   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10113 \r
10114   /* Bind local socket to unused "privileged" port address\r
10115    */\r
10116   s = INVALID_SOCKET;\r
10117   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10118   mysa.sin_family = AF_INET;\r
10119   mysa.sin_addr.s_addr = INADDR_ANY;\r
10120   for (fromPort = 1023;; fromPort--) {\r
10121     if (fromPort < 0) {\r
10122       WSACleanup();\r
10123       return WSAEADDRINUSE;\r
10124     }\r
10125     if (s == INVALID_SOCKET) {\r
10126       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10127         err = WSAGetLastError();\r
10128         WSACleanup();\r
10129         return err;\r
10130       }\r
10131     }\r
10132     uport = (unsigned short) fromPort;\r
10133     mysa.sin_port = htons(uport);\r
10134     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10135         == SOCKET_ERROR) {\r
10136       err = WSAGetLastError();\r
10137       if (err == WSAEADDRINUSE) continue;\r
10138       WSACleanup();\r
10139       return err;\r
10140     }\r
10141     if (connect(s, (struct sockaddr *) &sa,\r
10142       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10143       err = WSAGetLastError();\r
10144       if (err == WSAEADDRINUSE) {\r
10145         closesocket(s);\r
10146         s = -1;\r
10147         continue;\r
10148       }\r
10149       WSACleanup();\r
10150       return err;\r
10151     }\r
10152     break;\r
10153   }\r
10154 \r
10155   /* Bind stderr local socket to unused "privileged" port address\r
10156    */\r
10157   s2 = INVALID_SOCKET;\r
10158   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10159   mysa.sin_family = AF_INET;\r
10160   mysa.sin_addr.s_addr = INADDR_ANY;\r
10161   for (fromPort = 1023;; fromPort--) {\r
10162     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10163     if (fromPort < 0) {\r
10164       (void) closesocket(s);\r
10165       WSACleanup();\r
10166       return WSAEADDRINUSE;\r
10167     }\r
10168     if (s2 == INVALID_SOCKET) {\r
10169       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10170         err = WSAGetLastError();\r
10171         closesocket(s);\r
10172         WSACleanup();\r
10173         return err;\r
10174       }\r
10175     }\r
10176     uport = (unsigned short) fromPort;\r
10177     mysa.sin_port = htons(uport);\r
10178     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10179         == SOCKET_ERROR) {\r
10180       err = WSAGetLastError();\r
10181       if (err == WSAEADDRINUSE) continue;\r
10182       (void) closesocket(s);\r
10183       WSACleanup();\r
10184       return err;\r
10185     }\r
10186     if (listen(s2, 1) == SOCKET_ERROR) {\r
10187       err = WSAGetLastError();\r
10188       if (err == WSAEADDRINUSE) {\r
10189         closesocket(s2);\r
10190         s2 = INVALID_SOCKET;\r
10191         continue;\r
10192       }\r
10193       (void) closesocket(s);\r
10194       (void) closesocket(s2);\r
10195       WSACleanup();\r
10196       return err;\r
10197     }\r
10198     break;\r
10199   }\r
10200   prevStderrPort = fromPort; // remember port used\r
10201   sprintf(stderrPortStr, "%d", fromPort);\r
10202 \r
10203   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10204     err = WSAGetLastError();\r
10205     (void) closesocket(s);\r
10206     (void) closesocket(s2);\r
10207     WSACleanup();\r
10208     return err;\r
10209   }\r
10210 \r
10211   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10212     err = WSAGetLastError();\r
10213     (void) closesocket(s);\r
10214     (void) closesocket(s2);\r
10215     WSACleanup();\r
10216     return err;\r
10217   }\r
10218   if (*user == NULLCHAR) user = UserName();\r
10219   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10220     err = WSAGetLastError();\r
10221     (void) closesocket(s);\r
10222     (void) closesocket(s2);\r
10223     WSACleanup();\r
10224     return err;\r
10225   }\r
10226   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10227     err = WSAGetLastError();\r
10228     (void) closesocket(s);\r
10229     (void) closesocket(s2);\r
10230     WSACleanup();\r
10231     return err;\r
10232   }\r
10233 \r
10234   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10235     err = WSAGetLastError();\r
10236     (void) closesocket(s);\r
10237     (void) closesocket(s2);\r
10238     WSACleanup();\r
10239     return err;\r
10240   }\r
10241   (void) closesocket(s2);  /* Stop listening */\r
10242 \r
10243   /* Prepare return value */\r
10244   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10245   cp->kind = CPRcmd;\r
10246   cp->sock = s;\r
10247   cp->sock2 = s3;\r
10248   *pr = (ProcRef *) cp;\r
10249 \r
10250   return NO_ERROR;\r
10251 }\r
10252 \r
10253 \r
10254 InputSourceRef\r
10255 AddInputSource(ProcRef pr, int lineByLine,\r
10256                InputCallback func, VOIDSTAR closure)\r
10257 {\r
10258   InputSource *is, *is2 = NULL;\r
10259   ChildProc *cp = (ChildProc *) pr;\r
10260 \r
10261   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10262   is->lineByLine = lineByLine;\r
10263   is->func = func;\r
10264   is->closure = closure;\r
10265   is->second = NULL;\r
10266   is->next = is->buf;\r
10267   if (pr == NoProc) {\r
10268     is->kind = CPReal;\r
10269     consoleInputSource = is;\r
10270   } else {\r
10271     is->kind = cp->kind;\r
10272     /* \r
10273         [AS] Try to avoid a race condition if the thread is given control too early:\r
10274         we create all threads suspended so that the is->hThread variable can be\r
10275         safely assigned, then let the threads start with ResumeThread.\r
10276     */\r
10277     switch (cp->kind) {\r
10278     case CPReal:\r
10279       is->hFile = cp->hFrom;\r
10280       cp->hFrom = NULL; /* now owned by InputThread */\r
10281       is->hThread =\r
10282         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10283                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10284       break;\r
10285 \r
10286     case CPComm:\r
10287       is->hFile = cp->hFrom;\r
10288       cp->hFrom = NULL; /* now owned by InputThread */\r
10289       is->hThread =\r
10290         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10291                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10292       break;\r
10293 \r
10294     case CPSock:\r
10295       is->sock = cp->sock;\r
10296       is->hThread =\r
10297         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10298                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10299       break;\r
10300 \r
10301     case CPRcmd:\r
10302       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10303       *is2 = *is;\r
10304       is->sock = cp->sock;\r
10305       is->second = is2;\r
10306       is2->sock = cp->sock2;\r
10307       is2->second = is2;\r
10308       is->hThread =\r
10309         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10310                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10311       is2->hThread =\r
10312         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10313                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10314       break;\r
10315     }\r
10316 \r
10317     if( is->hThread != NULL ) {\r
10318         ResumeThread( is->hThread );\r
10319     }\r
10320 \r
10321     if( is2 != NULL && is2->hThread != NULL ) {\r
10322         ResumeThread( is2->hThread );\r
10323     }\r
10324   }\r
10325 \r
10326   return (InputSourceRef) is;\r
10327 }\r
10328 \r
10329 void\r
10330 RemoveInputSource(InputSourceRef isr)\r
10331 {\r
10332   InputSource *is;\r
10333 \r
10334   is = (InputSource *) isr;\r
10335   is->hThread = NULL;  /* tell thread to stop */\r
10336   CloseHandle(is->hThread);\r
10337   if (is->second != NULL) {\r
10338     is->second->hThread = NULL;\r
10339     CloseHandle(is->second->hThread);\r
10340   }\r
10341 }\r
10342 \r
10343 \r
10344 int\r
10345 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10346 {\r
10347   DWORD dOutCount;\r
10348   int outCount = SOCKET_ERROR;\r
10349   ChildProc *cp = (ChildProc *) pr;\r
10350   static OVERLAPPED ovl;\r
10351 \r
10352   if (pr == NoProc) {\r
10353     ConsoleOutput(message, count, FALSE);\r
10354     return count;\r
10355   } \r
10356 \r
10357   if (ovl.hEvent == NULL) {\r
10358     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10359   }\r
10360   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10361 \r
10362   switch (cp->kind) {\r
10363   case CPSock:\r
10364   case CPRcmd:\r
10365     outCount = send(cp->sock, message, count, 0);\r
10366     if (outCount == SOCKET_ERROR) {\r
10367       *outError = WSAGetLastError();\r
10368     } else {\r
10369       *outError = NO_ERROR;\r
10370     }\r
10371     break;\r
10372 \r
10373   case CPReal:\r
10374     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10375                   &dOutCount, NULL)) {\r
10376       *outError = NO_ERROR;\r
10377       outCount = (int) dOutCount;\r
10378     } else {\r
10379       *outError = GetLastError();\r
10380     }\r
10381     break;\r
10382 \r
10383   case CPComm:\r
10384     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10385                             &dOutCount, &ovl);\r
10386     if (*outError == NO_ERROR) {\r
10387       outCount = (int) dOutCount;\r
10388     }\r
10389     break;\r
10390   }\r
10391   return outCount;\r
10392 }\r
10393 \r
10394 int\r
10395 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10396                        long msdelay)\r
10397 {\r
10398   /* Ignore delay, not implemented for WinBoard */\r
10399   return OutputToProcess(pr, message, count, outError);\r
10400 }\r
10401 \r
10402 \r
10403 void\r
10404 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10405                         char *buf, int count, int error)\r
10406 {\r
10407   DisplayFatalError("Not implemented", 0, 1);\r
10408 }\r
10409 \r
10410 /* see wgamelist.c for Game List functions */\r
10411 /* see wedittags.c for Edit Tags functions */\r
10412 \r
10413 \r
10414 VOID\r
10415 ICSInitScript()\r
10416 {\r
10417   FILE *f;\r
10418   char buf[MSG_SIZ];\r
10419   char *dummy;\r
10420 \r
10421   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10422     f = fopen(buf, "r");\r
10423     if (f != NULL) {\r
10424       ProcessICSInitScript(f);\r
10425       fclose(f);\r
10426     }\r
10427   }\r
10428 }\r
10429 \r
10430 \r
10431 VOID\r
10432 StartAnalysisClock()\r
10433 {\r
10434   if (analysisTimerEvent) return;\r
10435   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10436                                         (UINT) 2000, NULL);\r
10437 }\r
10438 \r
10439 LRESULT CALLBACK\r
10440 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10441 {\r
10442   static HANDLE hwndText;\r
10443   RECT rect;\r
10444   static int sizeX, sizeY;\r
10445   int newSizeX, newSizeY, flags;\r
10446   MINMAXINFO *mmi;\r
10447 \r
10448   switch (message) {\r
10449   case WM_INITDIALOG: /* message: initialize dialog box */\r
10450     /* Initialize the dialog items */\r
10451     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10452     SetWindowText(hDlg, analysisTitle);\r
10453     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10454     /* Size and position the dialog */\r
10455     if (!analysisDialog) {\r
10456       analysisDialog = hDlg;\r
10457       flags = SWP_NOZORDER;\r
10458       GetClientRect(hDlg, &rect);\r
10459       sizeX = rect.right;\r
10460       sizeY = rect.bottom;\r
10461       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10462           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10463         WINDOWPLACEMENT wp;\r
10464         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10465         wp.length = sizeof(WINDOWPLACEMENT);\r
10466         wp.flags = 0;\r
10467         wp.showCmd = SW_SHOW;\r
10468         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10469         wp.rcNormalPosition.left = analysisX;\r
10470         wp.rcNormalPosition.right = analysisX + analysisW;\r
10471         wp.rcNormalPosition.top = analysisY;\r
10472         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10473         SetWindowPlacement(hDlg, &wp);\r
10474 \r
10475         GetClientRect(hDlg, &rect);\r
10476         newSizeX = rect.right;\r
10477         newSizeY = rect.bottom;\r
10478         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10479                               newSizeX, newSizeY);\r
10480         sizeX = newSizeX;\r
10481         sizeY = newSizeY;\r
10482       }\r
10483     }\r
10484     return FALSE;\r
10485 \r
10486   case WM_COMMAND: /* message: received a command */\r
10487     switch (LOWORD(wParam)) {\r
10488     case IDCANCEL:\r
10489       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10490           ExitAnalyzeMode();\r
10491           ModeHighlight();\r
10492           return TRUE;\r
10493       }\r
10494       EditGameEvent();\r
10495       return TRUE;\r
10496     default:\r
10497       break;\r
10498     }\r
10499     break;\r
10500 \r
10501   case WM_SIZE:\r
10502     newSizeX = LOWORD(lParam);\r
10503     newSizeY = HIWORD(lParam);\r
10504     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10505     sizeX = newSizeX;\r
10506     sizeY = newSizeY;\r
10507     break;\r
10508 \r
10509   case WM_GETMINMAXINFO:\r
10510     /* Prevent resizing window too small */\r
10511     mmi = (MINMAXINFO *) lParam;\r
10512     mmi->ptMinTrackSize.x = 100;\r
10513     mmi->ptMinTrackSize.y = 100;\r
10514     break;\r
10515   }\r
10516   return FALSE;\r
10517 }\r
10518 \r
10519 VOID\r
10520 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10521 {\r
10522   highlightInfo.sq[0].x = fromX;\r
10523   highlightInfo.sq[0].y = fromY;\r
10524   highlightInfo.sq[1].x = toX;\r
10525   highlightInfo.sq[1].y = toY;\r
10526 }\r
10527 \r
10528 VOID\r
10529 ClearHighlights()\r
10530 {\r
10531   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10532     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10533 }\r
10534 \r
10535 VOID\r
10536 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10537 {\r
10538   premoveHighlightInfo.sq[0].x = fromX;\r
10539   premoveHighlightInfo.sq[0].y = fromY;\r
10540   premoveHighlightInfo.sq[1].x = toX;\r
10541   premoveHighlightInfo.sq[1].y = toY;\r
10542 }\r
10543 \r
10544 VOID\r
10545 ClearPremoveHighlights()\r
10546 {\r
10547   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10548     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10549 }\r
10550 \r
10551 VOID\r
10552 ShutDownFrontEnd()\r
10553 {\r
10554   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10555   DeleteClipboardTempFiles();\r
10556 }\r
10557 \r
10558 void\r
10559 BoardToTop()\r
10560 {\r
10561     if (IsIconic(hwndMain))\r
10562       ShowWindow(hwndMain, SW_RESTORE);\r
10563 \r
10564     SetActiveWindow(hwndMain);\r
10565 }\r
10566 \r
10567 /*\r
10568  * Prototypes for animation support routines\r
10569  */\r
10570 static void ScreenSquare(int column, int row, POINT * pt);\r
10571 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10572      POINT frames[], int * nFrames);\r
10573 \r
10574 \r
10575 void\r
10576 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10577 {       // [HGM] atomic: animate blast wave\r
10578         int i;\r
10579 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10580         explodeInfo.fromX = fromX;\r
10581         explodeInfo.fromY = fromY;\r
10582         explodeInfo.toX = toX;\r
10583         explodeInfo.toY = toY;\r
10584         for(i=1; i<nFrames; i++) {\r
10585             explodeInfo.radius = (i*180)/(nFrames-1);\r
10586             DrawPosition(FALSE, NULL);\r
10587             Sleep(appData.animSpeed);\r
10588         }\r
10589         explodeInfo.radius = 0;\r
10590         DrawPosition(TRUE, NULL);\r
10591 }\r
10592 \r
10593 #define kFactor 4\r
10594 \r
10595 void\r
10596 AnimateMove(board, fromX, fromY, toX, toY)\r
10597      Board board;\r
10598      int fromX;\r
10599      int fromY;\r
10600      int toX;\r
10601      int toY;\r
10602 {\r
10603   ChessSquare piece;\r
10604   POINT start, finish, mid;\r
10605   POINT frames[kFactor * 2 + 1];\r
10606   int nFrames, n;\r
10607 \r
10608   if (!appData.animate) return;\r
10609   if (doingSizing) return;\r
10610   if (fromY < 0 || fromX < 0) return;\r
10611   piece = board[fromY][fromX];\r
10612   if (piece >= EmptySquare) return;\r
10613 \r
10614   ScreenSquare(fromX, fromY, &start);\r
10615   ScreenSquare(toX, toY, &finish);\r
10616 \r
10617   /* All pieces except knights move in straight line */\r
10618   if (piece != WhiteKnight && piece != BlackKnight) {\r
10619     mid.x = start.x + (finish.x - start.x) / 2;\r
10620     mid.y = start.y + (finish.y - start.y) / 2;\r
10621   } else {\r
10622     /* Knight: make diagonal movement then straight */\r
10623     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10624        mid.x = start.x + (finish.x - start.x) / 2;\r
10625        mid.y = finish.y;\r
10626      } else {\r
10627        mid.x = finish.x;\r
10628        mid.y = start.y + (finish.y - start.y) / 2;\r
10629      }\r
10630   }\r
10631   \r
10632   /* Don't use as many frames for very short moves */\r
10633   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10634     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10635   else\r
10636     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10637 \r
10638   animInfo.from.x = fromX;\r
10639   animInfo.from.y = fromY;\r
10640   animInfo.to.x = toX;\r
10641   animInfo.to.y = toY;\r
10642   animInfo.lastpos = start;\r
10643   animInfo.piece = piece;\r
10644   for (n = 0; n < nFrames; n++) {\r
10645     animInfo.pos = frames[n];\r
10646     DrawPosition(FALSE, NULL);\r
10647     animInfo.lastpos = animInfo.pos;\r
10648     Sleep(appData.animSpeed);\r
10649   }\r
10650   animInfo.pos = finish;\r
10651   DrawPosition(FALSE, NULL);\r
10652   animInfo.piece = EmptySquare;\r
10653   if(gameInfo.variant == VariantAtomic && \r
10654      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10655         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10656 }\r
10657 \r
10658 /*      Convert board position to corner of screen rect and color       */\r
10659 \r
10660 static void\r
10661 ScreenSquare(column, row, pt)\r
10662      int column; int row; POINT * pt;\r
10663 {\r
10664   if (flipView) {\r
10665     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10666     pt->y = lineGap + row * (squareSize + lineGap);\r
10667   } else {\r
10668     pt->x = lineGap + column * (squareSize + lineGap);\r
10669     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10670   }\r
10671 }\r
10672 \r
10673 /*      Generate a series of frame coords from start->mid->finish.\r
10674         The movement rate doubles until the half way point is\r
10675         reached, then halves back down to the final destination,\r
10676         which gives a nice slow in/out effect. The algorithmn\r
10677         may seem to generate too many intermediates for short\r
10678         moves, but remember that the purpose is to attract the\r
10679         viewers attention to the piece about to be moved and\r
10680         then to where it ends up. Too few frames would be less\r
10681         noticeable.                                             */\r
10682 \r
10683 static void\r
10684 Tween(start, mid, finish, factor, frames, nFrames)\r
10685      POINT * start; POINT * mid;\r
10686      POINT * finish; int factor;\r
10687      POINT frames[]; int * nFrames;\r
10688 {\r
10689   int n, fraction = 1, count = 0;\r
10690 \r
10691   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10692   for (n = 0; n < factor; n++)\r
10693     fraction *= 2;\r
10694   for (n = 0; n < factor; n++) {\r
10695     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10696     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10697     count ++;\r
10698     fraction = fraction / 2;\r
10699   }\r
10700   \r
10701   /* Midpoint */\r
10702   frames[count] = *mid;\r
10703   count ++;\r
10704   \r
10705   /* Slow out, stepping 1/2, then 1/4, ... */\r
10706   fraction = 2;\r
10707   for (n = 0; n < factor; n++) {\r
10708     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10709     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10710     count ++;\r
10711     fraction = fraction * 2;\r
10712   }\r
10713   *nFrames = count;\r
10714 }\r
10715 \r
10716 void\r
10717 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10718 {\r
10719     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10720 \r
10721     EvalGraphSet( first, last, current, pvInfoList );\r
10722 }\r
10723 \r
10724 void SetProgramStats( FrontEndProgramStats * stats )\r
10725 {\r
10726     EngineOutputUpdate( stats );\r
10727 }\r