fixed engingeoutput routine
[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\r
4843 EventToSquare(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 >= BOARD_SIZE)\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 /* Event handler for mouse messages */\r
4896 VOID\r
4897 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4898 {\r
4899   int x, y;\r
4900   POINT pt;\r
4901   static int recursive = 0;\r
4902   HMENU hmenu;\r
4903 //  BOOLEAN needsRedraw = FALSE;\r
4904   BOOLEAN saveAnimate;\r
4905   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4906   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4907   ChessMove moveType;\r
4908 \r
4909   if (recursive) {\r
4910     if (message == WM_MBUTTONUP) {\r
4911       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4912          to the middle button: we simulate pressing the left button too!\r
4913          */\r
4914       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4915       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4916     }\r
4917     return;\r
4918   }\r
4919   recursive++;\r
4920   \r
4921   pt.x = LOWORD(lParam);\r
4922   pt.y = HIWORD(lParam);\r
4923   x = EventToSquare(pt.x - boardRect.left);\r
4924   y = EventToSquare(pt.y - boardRect.top);\r
4925   if (!flipView && y >= 0) {\r
4926     y = BOARD_HEIGHT - 1 - y;\r
4927   }\r
4928   if (flipView && x >= 0) {\r
4929     x = BOARD_WIDTH - 1 - x;\r
4930   }\r
4931 \r
4932   switch (message) {\r
4933   case WM_LBUTTONDOWN:\r
4934     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4935         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4936         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4937         if(gameInfo.holdingsWidth && \r
4938                 (WhiteOnMove(currentMove) \r
4939                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4940                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4941             // click in right holdings, for determining promotion piece\r
4942             ChessSquare p = boards[currentMove][y][x];\r
4943             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4944             if(p != EmptySquare) {\r
4945                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4946                 fromX = fromY = -1;\r
4947                 break;\r
4948             }\r
4949         }\r
4950         DrawPosition(FALSE, boards[currentMove]);\r
4951         break;\r
4952     }\r
4953     ErrorPopDown();\r
4954     sameAgain = FALSE;\r
4955     if (y == -2) {\r
4956       /* Downclick vertically off board; check if on clock */\r
4957       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4958         if (gameMode == EditPosition) {\r
4959           SetWhiteToPlayEvent();\r
4960         } else if (gameMode == IcsPlayingBlack ||\r
4961                    gameMode == MachinePlaysWhite) {\r
4962           CallFlagEvent();\r
4963         } else if (gameMode == EditGame) {\r
4964           AdjustClock(flipClock, -1);\r
4965         }\r
4966       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4967         if (gameMode == EditPosition) {\r
4968           SetBlackToPlayEvent();\r
4969         } else if (gameMode == IcsPlayingWhite ||\r
4970                    gameMode == MachinePlaysBlack) {\r
4971           CallFlagEvent();\r
4972         } else if (gameMode == EditGame) {\r
4973           AdjustClock(!flipClock, -1);\r
4974         }\r
4975       }\r
4976       if (!appData.highlightLastMove) {\r
4977         ClearHighlights();\r
4978         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4979       }\r
4980       fromX = fromY = -1;\r
4981       dragInfo.start.x = dragInfo.start.y = -1;\r
4982       dragInfo.from = dragInfo.start;\r
4983       break;\r
4984     } else if (x < 0 || y < 0\r
4985       /* [HGM] block clicks between board and holdings */\r
4986               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4987               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4988               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4989         /* EditPosition, empty square, or different color piece;\r
4990            click-click move is possible */\r
4991                                ) {\r
4992       break;\r
4993     } else if (fromX == x && fromY == y) {\r
4994       /* Downclick on same square again */\r
4995       ClearHighlights();\r
4996       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4997       sameAgain = TRUE;  \r
4998     } else if (fromX != -1 &&\r
4999                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5000                                                                         ) {\r
5001       /* Downclick on different square. */\r
5002       /* [HGM] if on holdings file, should count as new first click ! */\r
5003       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5004         toX = x;\r
5005         toY = y;\r
5006         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5007            to make sure move is legal before showing promotion popup */\r
5008         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE);\r
5009         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5010                 fromX = fromY = -1; \r
5011                 ClearHighlights();\r
5012                 DrawPosition(FALSE, boards[currentMove]);\r
5013                 break; \r
5014         } else \r
5015         if(moveType != ImpossibleMove && moveType != Comment) {\r
5016           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5017           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5018             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5019               appData.alwaysPromoteToQueen)) {\r
5020                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5021                   if (!appData.highlightLastMove) {\r
5022                       ClearHighlights();\r
5023                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5024                   }\r
5025           } else\r
5026           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5027                   SetHighlights(fromX, fromY, toX, toY);\r
5028                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5029                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5030                      If promotion to Q is legal, all are legal! */\r
5031                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5032                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5033                     // kludge to temporarily execute move on display, without promoting yet\r
5034                     promotionChoice = TRUE;\r
5035                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5036                     boards[currentMove][toY][toX] = p;\r
5037                     DrawPosition(FALSE, boards[currentMove]);\r
5038                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5039                     boards[currentMove][toY][toX] = q;\r
5040                     DisplayMessage("Select piece from holdings", "");\r
5041                   } else\r
5042                   PromotionPopup(hwnd);\r
5043                   goto noClear;\r
5044           } else { // not a promotion. Move can be illegal if testLegality off, and should be made then.\r
5045              if (appData.animate || appData.highlightLastMove) {\r
5046                  SetHighlights(fromX, fromY, toX, toY);\r
5047              } else {\r
5048                  ClearHighlights();\r
5049              }\r
5050              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5051              if (appData.animate && !appData.highlightLastMove) {\r
5052                   ClearHighlights();\r
5053                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5054              }\r
5055           }\r
5056           fromX = fromY = -1;\r
5057         noClear:\r
5058           break;\r
5059         }\r
5060         if (gotPremove && moveType != Comment) {\r
5061             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5062 //            DrawPosition(forceFullRepaint || FALSE, NULL);\r
5063         } else ClearHighlights();\r
5064         fromX = fromY = -1;\r
5065         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5066         if(moveType != Comment) break;\r
5067     }\r
5068     /* First downclick, or restart on a square with same color piece */\r
5069     if (!frozen && OKToStartUserMove(x, y)) {\r
5070       fromX = x;\r
5071       fromY = y;\r
5072       dragInfo.lastpos = pt;\r
5073       dragInfo.from.x = fromX;\r
5074       dragInfo.from.y = fromY;\r
5075       dragInfo.start = dragInfo.from;\r
5076       SetCapture(hwndMain);\r
5077     } else {\r
5078       fromX = fromY = -1;\r
5079       dragInfo.start.x = dragInfo.start.y = -1;\r
5080       dragInfo.from = dragInfo.start;\r
5081       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5082     }\r
5083     break;\r
5084 \r
5085   case WM_LBUTTONUP:\r
5086     ReleaseCapture();\r
5087     if (fromX == -1) break;\r
5088     if (x == fromX && y == fromY) {\r
5089       dragInfo.from.x = dragInfo.from.y = -1;\r
5090       /* Upclick on same square */\r
5091       if (sameAgain) {\r
5092         /* Clicked same square twice: abort click-click move */\r
5093         fromX = fromY = -1;\r
5094         gotPremove = 0;\r
5095         ClearPremoveHighlights();\r
5096       } else {\r
5097         /* First square clicked: start click-click move */\r
5098         SetHighlights(fromX, fromY, -1, -1);\r
5099       }\r
5100       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5101     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5102       /* Errant click; ignore */\r
5103       break;\r
5104     } else {\r
5105       /* Finish drag move. */\r
5106     if (appData.debugMode) {\r
5107         fprintf(debugFP, "release\n");\r
5108     }\r
5109       dragInfo.from.x = dragInfo.from.y = -1;\r
5110       toX = x;\r
5111       toY = y;\r
5112       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5113       appData.animate = appData.animate && !appData.animateDragging;\r
5114       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, TRUE);\r
5115       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5116                 fromX = fromY = -1; \r
5117                 ClearHighlights();\r
5118                 DrawPosition(FALSE, boards[currentMove]);\r
5119                 appData.animate = saveAnimate;\r
5120                 break; \r
5121       } else \r
5122       if(moveType != ImpossibleMove) {\r
5123           /* [HGM] use move type to determine if move is promotion.\r
5124              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5125           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5126             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5127               appData.alwaysPromoteToQueen)) \r
5128                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5129           else \r
5130           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5131                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5132                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5133                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5134                     // kludge to temporarily execute move on display, wthout promotng yet\r
5135                     promotionChoice = TRUE;\r
5136                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5137                     boards[currentMove][toY][toX] = p;\r
5138                     DrawPosition(FALSE, boards[currentMove]);\r
5139                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5140                     boards[currentMove][toY][toX] = q;\r
5141                     appData.animate = saveAnimate;\r
5142                     DisplayMessage("Select piece from holdings", "");\r
5143                     break;\r
5144                   } else\r
5145                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5146           } else {\r
5147             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5148                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5149                                         moveType == WhiteCapturesEnPassant || \r
5150                                         moveType == BlackCapturesEnPassant   ) )\r
5151                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5152             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5153           }\r
5154       }\r
5155       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5156       appData.animate = saveAnimate;\r
5157       fromX = fromY = -1;\r
5158       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5159         ClearHighlights();\r
5160       }\r
5161       if (appData.animate || appData.animateDragging ||\r
5162           appData.highlightDragging || gotPremove) {\r
5163         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5164       }\r
5165     }\r
5166     dragInfo.start.x = dragInfo.start.y = -1; \r
5167     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5168     break;\r
5169 \r
5170   case WM_MOUSEMOVE:\r
5171     if ((appData.animateDragging || appData.highlightDragging)\r
5172         && (wParam & MK_LBUTTON)\r
5173         && dragInfo.from.x >= 0) \r
5174     {\r
5175       BOOL full_repaint = FALSE;\r
5176 \r
5177       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5178       if (appData.animateDragging) {\r
5179         dragInfo.pos = pt;\r
5180       }\r
5181       if (appData.highlightDragging) {\r
5182         SetHighlights(fromX, fromY, x, y);\r
5183         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5184             full_repaint = TRUE;\r
5185         }\r
5186       }\r
5187       \r
5188       DrawPosition( full_repaint, NULL);\r
5189       \r
5190       dragInfo.lastpos = dragInfo.pos;\r
5191     }\r
5192     break;\r
5193 \r
5194   case WM_MOUSEWHEEL: // [DM]\r
5195     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5196        /* Mouse Wheel is being rolled forward\r
5197         * Play moves forward\r
5198         */\r
5199        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5200                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5201        /* Mouse Wheel is being rolled backward\r
5202         * Play moves backward\r
5203         */\r
5204        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5205                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5206     }\r
5207     break;\r
5208 \r
5209   case WM_MBUTTONDOWN:\r
5210   case WM_RBUTTONDOWN:\r
5211     ErrorPopDown();\r
5212     ReleaseCapture();\r
5213     fromX = fromY = -1;\r
5214     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5215     dragInfo.start.x = dragInfo.start.y = -1;\r
5216     dragInfo.from = dragInfo.start;\r
5217     dragInfo.lastpos = dragInfo.pos;\r
5218     if (appData.highlightDragging) {\r
5219       ClearHighlights();\r
5220     }\r
5221     if(y == -2) {\r
5222       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5223       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5224           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5225       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5226           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5227       }\r
5228     }\r
5229     DrawPosition(TRUE, NULL);\r
5230 \r
5231     switch (gameMode) {\r
5232     case EditPosition:\r
5233     case IcsExamining:\r
5234       if (x < 0 || y < 0) break;\r
5235       fromX = x;\r
5236       fromY = y;\r
5237       if (message == WM_MBUTTONDOWN) {\r
5238         buttonCount = 3;  /* even if system didn't think so */\r
5239         if (wParam & MK_SHIFT) \r
5240           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5241         else\r
5242           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5243       } else { /* message == WM_RBUTTONDOWN */\r
5244         /* Just have one menu, on the right button.  Windows users don't\r
5245            think to try the middle one, and sometimes other software steals\r
5246            it, or it doesn't really exist. */\r
5247         if(gameInfo.variant != VariantShogi)\r
5248             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5249         else\r
5250             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5251       }\r
5252       break;\r
5253     case IcsPlayingWhite:\r
5254     case IcsPlayingBlack:\r
5255     case EditGame:\r
5256     case MachinePlaysWhite:\r
5257     case MachinePlaysBlack:\r
5258       if (appData.testLegality &&\r
5259           gameInfo.variant != VariantBughouse &&\r
5260           gameInfo.variant != VariantCrazyhouse) break;\r
5261       if (x < 0 || y < 0) break;\r
5262       fromX = x;\r
5263       fromY = y;\r
5264       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5265       SetupDropMenu(hmenu);\r
5266       MenuPopup(hwnd, pt, hmenu, -1);\r
5267       break;\r
5268     default:\r
5269       break;\r
5270     }\r
5271     break;\r
5272   }\r
5273 \r
5274   recursive--;\r
5275 }\r
5276 \r
5277 /* Preprocess messages for buttons in main window */\r
5278 LRESULT CALLBACK\r
5279 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5280 {\r
5281   int id = GetWindowLong(hwnd, GWL_ID);\r
5282   int i, dir;\r
5283 \r
5284   for (i=0; i<N_BUTTONS; i++) {\r
5285     if (buttonDesc[i].id == id) break;\r
5286   }\r
5287   if (i == N_BUTTONS) return 0;\r
5288   switch (message) {\r
5289   case WM_KEYDOWN:\r
5290     switch (wParam) {\r
5291     case VK_LEFT:\r
5292     case VK_RIGHT:\r
5293       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5294       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5295       return TRUE;\r
5296     }\r
5297     break;\r
5298   case WM_CHAR:\r
5299     switch (wParam) {\r
5300     case '\r':\r
5301       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5302       return TRUE;\r
5303     default:\r
5304       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5305         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5306         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5307         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5308         SetFocus(h);\r
5309         SendMessage(h, WM_CHAR, wParam, lParam);\r
5310         return TRUE;\r
5311       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5312         PopUpMoveDialog((char)wParam);\r
5313       }\r
5314       break;\r
5315     }\r
5316     break;\r
5317   }\r
5318   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5319 }\r
5320 \r
5321 /* Process messages for Promotion dialog box */\r
5322 LRESULT CALLBACK\r
5323 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5324 {\r
5325   char promoChar;\r
5326 \r
5327   switch (message) {\r
5328   case WM_INITDIALOG: /* message: initialize dialog box */\r
5329     /* Center the dialog over the application window */\r
5330     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5331     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5332       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5333        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5334                SW_SHOW : SW_HIDE);\r
5335     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5336     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5337        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5338          PieceToChar(WhiteAngel) != '~') ||\r
5339         (PieceToChar(BlackAngel) >= 'A' &&\r
5340          PieceToChar(BlackAngel) != '~')   ) ?\r
5341                SW_SHOW : SW_HIDE);\r
5342     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5343        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5344          PieceToChar(WhiteMarshall) != '~') ||\r
5345         (PieceToChar(BlackMarshall) >= 'A' &&\r
5346          PieceToChar(BlackMarshall) != '~')   ) ?\r
5347                SW_SHOW : SW_HIDE);\r
5348     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5349     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5350        gameInfo.variant != VariantShogi ?\r
5351                SW_SHOW : SW_HIDE);\r
5352     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5353        gameInfo.variant != VariantShogi ?\r
5354                SW_SHOW : SW_HIDE);\r
5355     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5356        gameInfo.variant == VariantShogi ?\r
5357                SW_SHOW : SW_HIDE);\r
5358     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5359        gameInfo.variant == VariantShogi ?\r
5360                SW_SHOW : SW_HIDE);\r
5361     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5362        gameInfo.variant == VariantSuper ?\r
5363                SW_SHOW : SW_HIDE);\r
5364     return TRUE;\r
5365 \r
5366   case WM_COMMAND: /* message: received a command */\r
5367     switch (LOWORD(wParam)) {\r
5368     case IDCANCEL:\r
5369       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5370       ClearHighlights();\r
5371       DrawPosition(FALSE, NULL);\r
5372       return TRUE;\r
5373     case PB_King:\r
5374       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5375       break;\r
5376     case PB_Queen:\r
5377       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5378       break;\r
5379     case PB_Rook:\r
5380       promoChar = PieceToChar(BlackRook);\r
5381       break;\r
5382     case PB_Bishop:\r
5383       promoChar = PieceToChar(BlackBishop);\r
5384       break;\r
5385     case PB_Chancellor:\r
5386       promoChar = PieceToChar(BlackMarshall);\r
5387       break;\r
5388     case PB_Archbishop:\r
5389       promoChar = PieceToChar(BlackAngel);\r
5390       break;\r
5391     case PB_Knight:\r
5392       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5393       break;\r
5394     default:\r
5395       return FALSE;\r
5396     }\r
5397     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5398     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5399        only show the popup when we are already sure the move is valid or\r
5400        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5401        will figure out it is a promotion from the promoChar. */\r
5402     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5403     if (!appData.highlightLastMove) {\r
5404       ClearHighlights();\r
5405       DrawPosition(FALSE, NULL);\r
5406     }\r
5407     return TRUE;\r
5408   }\r
5409   return FALSE;\r
5410 }\r
5411 \r
5412 /* Pop up promotion dialog */\r
5413 VOID\r
5414 PromotionPopup(HWND hwnd)\r
5415 {\r
5416   FARPROC lpProc;\r
5417 \r
5418   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5419   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5420     hwnd, (DLGPROC)lpProc);\r
5421   FreeProcInstance(lpProc);\r
5422 }\r
5423 \r
5424 /* Toggle ShowThinking */\r
5425 VOID\r
5426 ToggleShowThinking()\r
5427 {\r
5428   appData.showThinking = !appData.showThinking;\r
5429   ShowThinkingEvent();\r
5430 }\r
5431 \r
5432 VOID\r
5433 LoadGameDialog(HWND hwnd, char* title)\r
5434 {\r
5435   UINT number = 0;\r
5436   FILE *f;\r
5437   char fileTitle[MSG_SIZ];\r
5438   f = OpenFileDialog(hwnd, "rb", "",\r
5439                      appData.oldSaveStyle ? "gam" : "pgn",\r
5440                      GAME_FILT,\r
5441                      title, &number, fileTitle, NULL);\r
5442   if (f != NULL) {\r
5443     cmailMsgLoaded = FALSE;\r
5444     if (number == 0) {\r
5445       int error = GameListBuild(f);\r
5446       if (error) {\r
5447         DisplayError("Cannot build game list", error);\r
5448       } else if (!ListEmpty(&gameList) &&\r
5449                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5450         GameListPopUp(f, fileTitle);\r
5451         return;\r
5452       }\r
5453       GameListDestroy();\r
5454       number = 1;\r
5455     }\r
5456     LoadGame(f, number, fileTitle, FALSE);\r
5457   }\r
5458 }\r
5459 \r
5460 void UpdateICSWidth(HWND hText)\r
5461 {\r
5462         HDC hdc;\r
5463         TEXTMETRIC tm;\r
5464         RECT rc;\r
5465         HFONT hfont, hold_font;\r
5466         LONG old_width, new_width;\r
5467         \r
5468         // get the text metrics\r
5469         hdc = GetDC(hText);\r
5470         hfont = CreateFontIndirect(&font[boardSize][CONSOLE_FONT]->lf);\r
5471         hold_font = SelectObject(hdc, hfont);\r
5472         GetTextMetrics(hdc, &tm);\r
5473         SelectObject(hdc, hold_font);\r
5474         DeleteObject(hfont);\r
5475         ReleaseDC(hText, hdc);\r
5476 \r
5477         // get the rectangle\r
5478         SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5479 \r
5480         // update the width\r
5481         new_width = (rc.right-rc.left) / tm.tmAveCharWidth;\r
5482         old_width = GetWindowLong(hText, GWL_USERDATA);\r
5483         if (new_width != old_width)\r
5484         {\r
5485                 ics_update_width(new_width);\r
5486                 SetWindowLong(hText, GWL_USERDATA, new_width);\r
5487         }\r
5488 }\r
5489 \r
5490 VOID\r
5491 ChangedConsoleFont()\r
5492 {\r
5493   CHARFORMAT cfmt;\r
5494   CHARRANGE tmpsel, sel;\r
5495   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5496   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5497   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5498   PARAFORMAT paraf;\r
5499 \r
5500   cfmt.cbSize = sizeof(CHARFORMAT);\r
5501   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5502   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5503   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5504    * size.  This was undocumented in the version of MSVC++ that I had\r
5505    * when I wrote the code, but is apparently documented now.\r
5506    */\r
5507   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5508   cfmt.bCharSet = f->lf.lfCharSet;\r
5509   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5510   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5511   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5512   /* Why are the following seemingly needed too? */\r
5513   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5514   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5515   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5516   tmpsel.cpMin = 0;\r
5517   tmpsel.cpMax = -1; /*999999?*/\r
5518   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5519   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5520   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5521    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5522    */\r
5523   paraf.cbSize = sizeof(paraf);\r
5524   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5525   paraf.dxStartIndent = 0;\r
5526   paraf.dxOffset = WRAP_INDENT;\r
5527   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5528   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5529   UpdateICSWidth(hText);\r
5530 }\r
5531 \r
5532 /*---------------------------------------------------------------------------*\\r
5533  *\r
5534  * Window Proc for main window\r
5535  *\r
5536 \*---------------------------------------------------------------------------*/\r
5537 \r
5538 /* Process messages for main window, etc. */\r
5539 LRESULT CALLBACK\r
5540 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5541 {\r
5542   FARPROC lpProc;\r
5543   int wmId, wmEvent;\r
5544   char *defName;\r
5545   FILE *f;\r
5546   UINT number;\r
5547   char fileTitle[MSG_SIZ];\r
5548   char buf[MSG_SIZ];\r
5549   static SnapData sd;\r
5550 \r
5551   switch (message) {\r
5552 \r
5553   case WM_PAINT: /* message: repaint portion of window */\r
5554     PaintProc(hwnd);\r
5555     break;\r
5556 \r
5557   case WM_ERASEBKGND:\r
5558     if (IsIconic(hwnd)) {\r
5559       /* Cheat; change the message */\r
5560       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5561     } else {\r
5562       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5563     }\r
5564     break;\r
5565 \r
5566   case WM_LBUTTONDOWN:\r
5567   case WM_MBUTTONDOWN:\r
5568   case WM_RBUTTONDOWN:\r
5569   case WM_LBUTTONUP:\r
5570   case WM_MBUTTONUP:\r
5571   case WM_RBUTTONUP:\r
5572   case WM_MOUSEMOVE:\r
5573   case WM_MOUSEWHEEL:\r
5574     MouseEvent(hwnd, message, wParam, lParam);\r
5575     break;\r
5576 \r
5577   JAWS_KB_NAVIGATION\r
5578 \r
5579   case WM_CHAR:\r
5580     \r
5581     JAWS_ALT_INTERCEPT\r
5582 \r
5583     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5584         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5585         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5586         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5587         SetFocus(h);\r
5588         SendMessage(h, message, wParam, lParam);\r
5589     } else if(lParam != KF_REPEAT) {\r
5590         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5591                 PopUpMoveDialog((char)wParam);\r
5592         } else if((char)wParam == 003) CopyGameToClipboard();\r
5593          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5594     }\r
5595 \r
5596     break;\r
5597 \r
5598   case WM_PALETTECHANGED:\r
5599     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5600       int nnew;\r
5601       HDC hdc = GetDC(hwndMain);\r
5602       SelectPalette(hdc, hPal, TRUE);\r
5603       nnew = RealizePalette(hdc);\r
5604       if (nnew > 0) {\r
5605         paletteChanged = TRUE;\r
5606         InvalidateRect(hwnd, &boardRect, FALSE);\r
5607       }\r
5608       ReleaseDC(hwnd, hdc);\r
5609     }\r
5610     break;\r
5611 \r
5612   case WM_QUERYNEWPALETTE:\r
5613     if (!appData.monoMode /*&& paletteChanged*/) {\r
5614       int nnew;\r
5615       HDC hdc = GetDC(hwndMain);\r
5616       paletteChanged = FALSE;\r
5617       SelectPalette(hdc, hPal, FALSE);\r
5618       nnew = RealizePalette(hdc);\r
5619       if (nnew > 0) {\r
5620         InvalidateRect(hwnd, &boardRect, FALSE);\r
5621       }\r
5622       ReleaseDC(hwnd, hdc);\r
5623       return TRUE;\r
5624     }\r
5625     return FALSE;\r
5626 \r
5627   case WM_COMMAND: /* message: command from application menu */\r
5628     wmId    = LOWORD(wParam);\r
5629     wmEvent = HIWORD(wParam);\r
5630 \r
5631     switch (wmId) {\r
5632     case IDM_NewGame:\r
5633       ResetGameEvent();\r
5634       SAY("new game enter a move to play against the computer with white");\r
5635       break;\r
5636 \r
5637     case IDM_NewGameFRC:\r
5638       if( NewGameFRC() == 0 ) {\r
5639         ResetGameEvent();\r
5640       }\r
5641       break;\r
5642 \r
5643     case IDM_NewVariant:\r
5644       NewVariantPopup(hwnd);\r
5645       break;\r
5646 \r
5647     case IDM_LoadGame:\r
5648       LoadGameDialog(hwnd, "Load Game from File");\r
5649       break;\r
5650 \r
5651     case IDM_LoadNextGame:\r
5652       ReloadGame(1);\r
5653       break;\r
5654 \r
5655     case IDM_LoadPrevGame:\r
5656       ReloadGame(-1);\r
5657       break;\r
5658 \r
5659     case IDM_ReloadGame:\r
5660       ReloadGame(0);\r
5661       break;\r
5662 \r
5663     case IDM_LoadPosition:\r
5664       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5665         Reset(FALSE, TRUE);\r
5666       }\r
5667       number = 1;\r
5668       f = OpenFileDialog(hwnd, "rb", "",\r
5669                          appData.oldSaveStyle ? "pos" : "fen",\r
5670                          POSITION_FILT,\r
5671                          "Load Position from File", &number, fileTitle, NULL);\r
5672       if (f != NULL) {\r
5673         LoadPosition(f, number, fileTitle);\r
5674       }\r
5675       break;\r
5676 \r
5677     case IDM_LoadNextPosition:\r
5678       ReloadPosition(1);\r
5679       break;\r
5680 \r
5681     case IDM_LoadPrevPosition:\r
5682       ReloadPosition(-1);\r
5683       break;\r
5684 \r
5685     case IDM_ReloadPosition:\r
5686       ReloadPosition(0);\r
5687       break;\r
5688 \r
5689     case IDM_SaveGame:\r
5690       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5691       f = OpenFileDialog(hwnd, "a", defName,\r
5692                          appData.oldSaveStyle ? "gam" : "pgn",\r
5693                          GAME_FILT,\r
5694                          "Save Game to File", NULL, fileTitle, NULL);\r
5695       if (f != NULL) {\r
5696         SaveGame(f, 0, "");\r
5697       }\r
5698       break;\r
5699 \r
5700     case IDM_SavePosition:\r
5701       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5702       f = OpenFileDialog(hwnd, "a", defName,\r
5703                          appData.oldSaveStyle ? "pos" : "fen",\r
5704                          POSITION_FILT,\r
5705                          "Save Position to File", NULL, fileTitle, NULL);\r
5706       if (f != NULL) {\r
5707         SavePosition(f, 0, "");\r
5708       }\r
5709       break;\r
5710 \r
5711     case IDM_SaveDiagram:\r
5712       defName = "diagram";\r
5713       f = OpenFileDialog(hwnd, "wb", defName,\r
5714                          "bmp",\r
5715                          DIAGRAM_FILT,\r
5716                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5717       if (f != NULL) {\r
5718         SaveDiagram(f);\r
5719       }\r
5720       break;\r
5721 \r
5722     case IDM_CopyGame:\r
5723       CopyGameToClipboard();\r
5724       break;\r
5725 \r
5726     case IDM_PasteGame:\r
5727       PasteGameFromClipboard();\r
5728       break;\r
5729 \r
5730     case IDM_CopyGameListToClipboard:\r
5731       CopyGameListToClipboard();\r
5732       break;\r
5733 \r
5734     /* [AS] Autodetect FEN or PGN data */\r
5735     case IDM_PasteAny:\r
5736       PasteGameOrFENFromClipboard();\r
5737       break;\r
5738 \r
5739     /* [AS] Move history */\r
5740     case IDM_ShowMoveHistory:\r
5741         if( MoveHistoryIsUp() ) {\r
5742             MoveHistoryPopDown();\r
5743         }\r
5744         else {\r
5745             MoveHistoryPopUp();\r
5746         }\r
5747         break;\r
5748 \r
5749     /* [AS] Eval graph */\r
5750     case IDM_ShowEvalGraph:\r
5751         if( EvalGraphIsUp() ) {\r
5752             EvalGraphPopDown();\r
5753         }\r
5754         else {\r
5755             EvalGraphPopUp();\r
5756             SetFocus(hwndMain);\r
5757         }\r
5758         break;\r
5759 \r
5760     /* [AS] Engine output */\r
5761     case IDM_ShowEngineOutput:\r
5762         if( EngineOutputIsUp() ) {\r
5763             EngineOutputPopDown();\r
5764         }\r
5765         else {\r
5766             EngineOutputPopUp();\r
5767         }\r
5768         break;\r
5769 \r
5770     /* [AS] User adjudication */\r
5771     case IDM_UserAdjudication_White:\r
5772         UserAdjudicationEvent( +1 );\r
5773         break;\r
5774 \r
5775     case IDM_UserAdjudication_Black:\r
5776         UserAdjudicationEvent( -1 );\r
5777         break;\r
5778 \r
5779     case IDM_UserAdjudication_Draw:\r
5780         UserAdjudicationEvent( 0 );\r
5781         break;\r
5782 \r
5783     /* [AS] Game list options dialog */\r
5784     case IDM_GameListOptions:\r
5785       GameListOptions();\r
5786       break;\r
5787 \r
5788     case IDM_NewChat:\r
5789       ChatPopUp();\r
5790       break;\r
5791 \r
5792     case IDM_CopyPosition:\r
5793       CopyFENToClipboard();\r
5794       break;\r
5795 \r
5796     case IDM_PastePosition:\r
5797       PasteFENFromClipboard();\r
5798       break;\r
5799 \r
5800     case IDM_MailMove:\r
5801       MailMoveEvent();\r
5802       break;\r
5803 \r
5804     case IDM_ReloadCMailMsg:\r
5805       Reset(TRUE, TRUE);\r
5806       ReloadCmailMsgEvent(FALSE);\r
5807       break;\r
5808 \r
5809     case IDM_Minimize:\r
5810       ShowWindow(hwnd, SW_MINIMIZE);\r
5811       break;\r
5812 \r
5813     case IDM_Exit:\r
5814       ExitEvent(0);\r
5815       break;\r
5816 \r
5817     case IDM_MachineWhite:\r
5818       MachineWhiteEvent();\r
5819       /*\r
5820        * refresh the tags dialog only if it's visible\r
5821        */\r
5822       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5823           char *tags;\r
5824           tags = PGNTags(&gameInfo);\r
5825           TagsPopUp(tags, CmailMsg());\r
5826           free(tags);\r
5827       }\r
5828       SAY("computer starts playing white");\r
5829       break;\r
5830 \r
5831     case IDM_MachineBlack:\r
5832       MachineBlackEvent();\r
5833       /*\r
5834        * refresh the tags dialog only if it's visible\r
5835        */\r
5836       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5837           char *tags;\r
5838           tags = PGNTags(&gameInfo);\r
5839           TagsPopUp(tags, CmailMsg());\r
5840           free(tags);\r
5841       }\r
5842       SAY("computer starts playing black");\r
5843       break;\r
5844 \r
5845     case IDM_TwoMachines:\r
5846       TwoMachinesEvent();\r
5847       /*\r
5848        * refresh the tags dialog only if it's visible\r
5849        */\r
5850       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5851           char *tags;\r
5852           tags = PGNTags(&gameInfo);\r
5853           TagsPopUp(tags, CmailMsg());\r
5854           free(tags);\r
5855       }\r
5856       SAY("programs start playing each other");\r
5857       break;\r
5858 \r
5859     case IDM_AnalysisMode:\r
5860       if (!first.analysisSupport) {\r
5861         sprintf(buf, "%s does not support analysis", first.tidy);\r
5862         DisplayError(buf, 0);\r
5863       } else {\r
5864         SAY("analyzing current position");\r
5865         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5866         if (appData.icsActive) {\r
5867                if (gameMode != IcsObserving) {\r
5868                        sprintf(buf, "You are not observing a game");\r
5869                        DisplayError(buf, 0);\r
5870                        /* secure check */\r
5871                        if (appData.icsEngineAnalyze) {\r
5872                                if (appData.debugMode) \r
5873                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5874                                ExitAnalyzeMode();\r
5875                                ModeHighlight();\r
5876                                break;\r
5877                        }\r
5878                        break;\r
5879                } else {\r
5880                        /* if enable, user want disable icsEngineAnalyze */\r
5881                        if (appData.icsEngineAnalyze) {\r
5882                                ExitAnalyzeMode();\r
5883                                ModeHighlight();\r
5884                                break;\r
5885                        }\r
5886                        appData.icsEngineAnalyze = TRUE;\r
5887                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5888                }\r
5889         } \r
5890         if (!appData.showThinking) ToggleShowThinking();\r
5891         AnalyzeModeEvent();\r
5892       }\r
5893       break;\r
5894 \r
5895     case IDM_AnalyzeFile:\r
5896       if (!first.analysisSupport) {\r
5897         char buf[MSG_SIZ];\r
5898         sprintf(buf, "%s does not support analysis", first.tidy);\r
5899         DisplayError(buf, 0);\r
5900       } else {\r
5901         if (!appData.showThinking) ToggleShowThinking();\r
5902         AnalyzeFileEvent();\r
5903         LoadGameDialog(hwnd, "Analyze Game from File");\r
5904         AnalysisPeriodicEvent(1);\r
5905       }\r
5906       break;\r
5907 \r
5908     case IDM_IcsClient:\r
5909       IcsClientEvent();\r
5910       break;\r
5911 \r
5912     case IDM_EditGame:\r
5913       EditGameEvent();\r
5914       SAY("edit game");\r
5915       break;\r
5916 \r
5917     case IDM_EditPosition:\r
5918       EditPositionEvent();\r
5919       SAY("to set up a position type a FEN");\r
5920       break;\r
5921 \r
5922     case IDM_Training:\r
5923       TrainingEvent();\r
5924       break;\r
5925 \r
5926     case IDM_ShowGameList:\r
5927       ShowGameListProc();\r
5928       break;\r
5929 \r
5930     case IDM_EditTags:\r
5931       EditTagsProc();\r
5932       break;\r
5933 \r
5934     case IDM_EditComment:\r
5935       if (commentDialogUp && editComment) {\r
5936         CommentPopDown();\r
5937       } else {\r
5938         EditCommentEvent();\r
5939       }\r
5940       break;\r
5941 \r
5942     case IDM_Pause:\r
5943       PauseEvent();\r
5944       break;\r
5945 \r
5946     case IDM_Accept:\r
5947       AcceptEvent();\r
5948       break;\r
5949 \r
5950     case IDM_Decline:\r
5951       DeclineEvent();\r
5952       break;\r
5953 \r
5954     case IDM_Rematch:\r
5955       RematchEvent();\r
5956       break;\r
5957 \r
5958     case IDM_CallFlag:\r
5959       CallFlagEvent();\r
5960       break;\r
5961 \r
5962     case IDM_Draw:\r
5963       DrawEvent();\r
5964       break;\r
5965 \r
5966     case IDM_Adjourn:\r
5967       AdjournEvent();\r
5968       break;\r
5969 \r
5970     case IDM_Abort:\r
5971       AbortEvent();\r
5972       break;\r
5973 \r
5974     case IDM_Resign:\r
5975       ResignEvent();\r
5976       break;\r
5977 \r
5978     case IDM_StopObserving:\r
5979       StopObservingEvent();\r
5980       break;\r
5981 \r
5982     case IDM_StopExamining:\r
5983       StopExaminingEvent();\r
5984       break;\r
5985 \r
5986     case IDM_TypeInMove:\r
5987       PopUpMoveDialog('\000');\r
5988       break;\r
5989 \r
5990     case IDM_TypeInName:\r
5991       PopUpNameDialog('\000');\r
5992       break;\r
5993 \r
5994     case IDM_Backward:\r
5995       BackwardEvent();\r
5996       SetFocus(hwndMain);\r
5997       break;\r
5998 \r
5999     JAWS_MENU_ITEMS\r
6000 \r
6001     case IDM_Forward:\r
6002       ForwardEvent();\r
6003       SetFocus(hwndMain);\r
6004       break;\r
6005 \r
6006     case IDM_ToStart:\r
6007       ToStartEvent();\r
6008       SetFocus(hwndMain);\r
6009       break;\r
6010 \r
6011     case IDM_ToEnd:\r
6012       ToEndEvent();\r
6013       SetFocus(hwndMain);\r
6014       break;\r
6015 \r
6016     case IDM_Revert:\r
6017       RevertEvent();\r
6018       break;\r
6019 \r
6020     case IDM_TruncateGame:\r
6021       TruncateGameEvent();\r
6022       break;\r
6023 \r
6024     case IDM_MoveNow:\r
6025       MoveNowEvent();\r
6026       break;\r
6027 \r
6028     case IDM_RetractMove:\r
6029       RetractMoveEvent();\r
6030       break;\r
6031 \r
6032     case IDM_FlipView:\r
6033       flipView = !flipView;\r
6034       DrawPosition(FALSE, NULL);\r
6035       break;\r
6036 \r
6037     case IDM_FlipClock:\r
6038       flipClock = !flipClock;\r
6039       DisplayBothClocks();\r
6040       DrawPosition(FALSE, NULL);\r
6041       break;\r
6042 \r
6043     case IDM_MuteSounds:\r
6044       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6045       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6046                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6047       break;\r
6048 \r
6049     case IDM_GeneralOptions:\r
6050       GeneralOptionsPopup(hwnd);\r
6051       DrawPosition(TRUE, NULL);\r
6052       break;\r
6053 \r
6054     case IDM_BoardOptions:\r
6055       BoardOptionsPopup(hwnd);\r
6056       break;\r
6057 \r
6058     case IDM_EnginePlayOptions:\r
6059       EnginePlayOptionsPopup(hwnd);\r
6060       break;\r
6061 \r
6062     case IDM_Engine1Options:\r
6063       EngineOptionsPopup(hwnd, &first);\r
6064       break;\r
6065 \r
6066     case IDM_Engine2Options:\r
6067       EngineOptionsPopup(hwnd, &second);\r
6068       break;\r
6069 \r
6070     case IDM_OptionsUCI:\r
6071       UciOptionsPopup(hwnd);\r
6072       break;\r
6073 \r
6074     case IDM_IcsOptions:\r
6075       IcsOptionsPopup(hwnd);\r
6076       break;\r
6077 \r
6078     case IDM_Fonts:\r
6079       FontsOptionsPopup(hwnd);\r
6080       break;\r
6081 \r
6082     case IDM_Sounds:\r
6083       SoundOptionsPopup(hwnd);\r
6084       break;\r
6085 \r
6086     case IDM_CommPort:\r
6087       CommPortOptionsPopup(hwnd);\r
6088       break;\r
6089 \r
6090     case IDM_LoadOptions:\r
6091       LoadOptionsPopup(hwnd);\r
6092       break;\r
6093 \r
6094     case IDM_SaveOptions:\r
6095       SaveOptionsPopup(hwnd);\r
6096       break;\r
6097 \r
6098     case IDM_TimeControl:\r
6099       TimeControlOptionsPopup(hwnd);\r
6100       break;\r
6101 \r
6102     case IDM_SaveSettings:\r
6103       SaveSettings(settingsFileName);\r
6104       break;\r
6105 \r
6106     case IDM_SaveSettingsOnExit:\r
6107       saveSettingsOnExit = !saveSettingsOnExit;\r
6108       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6109                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6110                                          MF_CHECKED : MF_UNCHECKED));\r
6111       break;\r
6112 \r
6113     case IDM_Hint:\r
6114       HintEvent();\r
6115       break;\r
6116 \r
6117     case IDM_Book:\r
6118       BookEvent();\r
6119       break;\r
6120 \r
6121     case IDM_AboutGame:\r
6122       AboutGameEvent();\r
6123       break;\r
6124 \r
6125     case IDM_Debug:\r
6126       appData.debugMode = !appData.debugMode;\r
6127       if (appData.debugMode) {\r
6128         char dir[MSG_SIZ];\r
6129         GetCurrentDirectory(MSG_SIZ, dir);\r
6130         SetCurrentDirectory(installDir);\r
6131         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6132         SetCurrentDirectory(dir);\r
6133         setbuf(debugFP, NULL);\r
6134       } else {\r
6135         fclose(debugFP);\r
6136         debugFP = NULL;\r
6137       }\r
6138       break;\r
6139 \r
6140     case IDM_HELPCONTENTS:\r
6141       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6142           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6143           MessageBox (GetFocus(),\r
6144                     "Unable to activate help",\r
6145                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6146       }\r
6147       break;\r
6148 \r
6149     case IDM_HELPSEARCH:\r
6150         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6151             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6152         MessageBox (GetFocus(),\r
6153                     "Unable to activate help",\r
6154                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6155       }\r
6156       break;\r
6157 \r
6158     case IDM_HELPHELP:\r
6159       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6160         MessageBox (GetFocus(),\r
6161                     "Unable to activate help",\r
6162                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6163       }\r
6164       break;\r
6165 \r
6166     case IDM_ABOUT:\r
6167       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6168       DialogBox(hInst, \r
6169         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6170         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6171       FreeProcInstance(lpProc);\r
6172       break;\r
6173 \r
6174     case IDM_DirectCommand1:\r
6175       AskQuestionEvent("Direct Command",\r
6176                        "Send to chess program:", "", "1");\r
6177       break;\r
6178     case IDM_DirectCommand2:\r
6179       AskQuestionEvent("Direct Command",\r
6180                        "Send to second chess program:", "", "2");\r
6181       break;\r
6182 \r
6183     case EP_WhitePawn:\r
6184       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6185       fromX = fromY = -1;\r
6186       break;\r
6187 \r
6188     case EP_WhiteKnight:\r
6189       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6190       fromX = fromY = -1;\r
6191       break;\r
6192 \r
6193     case EP_WhiteBishop:\r
6194       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6195       fromX = fromY = -1;\r
6196       break;\r
6197 \r
6198     case EP_WhiteRook:\r
6199       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6200       fromX = fromY = -1;\r
6201       break;\r
6202 \r
6203     case EP_WhiteQueen:\r
6204       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6205       fromX = fromY = -1;\r
6206       break;\r
6207 \r
6208     case EP_WhiteFerz:\r
6209       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6210       fromX = fromY = -1;\r
6211       break;\r
6212 \r
6213     case EP_WhiteWazir:\r
6214       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6215       fromX = fromY = -1;\r
6216       break;\r
6217 \r
6218     case EP_WhiteAlfil:\r
6219       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6220       fromX = fromY = -1;\r
6221       break;\r
6222 \r
6223     case EP_WhiteCannon:\r
6224       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6225       fromX = fromY = -1;\r
6226       break;\r
6227 \r
6228     case EP_WhiteCardinal:\r
6229       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6230       fromX = fromY = -1;\r
6231       break;\r
6232 \r
6233     case EP_WhiteMarshall:\r
6234       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6235       fromX = fromY = -1;\r
6236       break;\r
6237 \r
6238     case EP_WhiteKing:\r
6239       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6240       fromX = fromY = -1;\r
6241       break;\r
6242 \r
6243     case EP_BlackPawn:\r
6244       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6245       fromX = fromY = -1;\r
6246       break;\r
6247 \r
6248     case EP_BlackKnight:\r
6249       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6250       fromX = fromY = -1;\r
6251       break;\r
6252 \r
6253     case EP_BlackBishop:\r
6254       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6255       fromX = fromY = -1;\r
6256       break;\r
6257 \r
6258     case EP_BlackRook:\r
6259       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6260       fromX = fromY = -1;\r
6261       break;\r
6262 \r
6263     case EP_BlackQueen:\r
6264       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6265       fromX = fromY = -1;\r
6266       break;\r
6267 \r
6268     case EP_BlackFerz:\r
6269       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6270       fromX = fromY = -1;\r
6271       break;\r
6272 \r
6273     case EP_BlackWazir:\r
6274       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6275       fromX = fromY = -1;\r
6276       break;\r
6277 \r
6278     case EP_BlackAlfil:\r
6279       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6280       fromX = fromY = -1;\r
6281       break;\r
6282 \r
6283     case EP_BlackCannon:\r
6284       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6285       fromX = fromY = -1;\r
6286       break;\r
6287 \r
6288     case EP_BlackCardinal:\r
6289       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6290       fromX = fromY = -1;\r
6291       break;\r
6292 \r
6293     case EP_BlackMarshall:\r
6294       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6295       fromX = fromY = -1;\r
6296       break;\r
6297 \r
6298     case EP_BlackKing:\r
6299       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6300       fromX = fromY = -1;\r
6301       break;\r
6302 \r
6303     case EP_EmptySquare:\r
6304       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6305       fromX = fromY = -1;\r
6306       break;\r
6307 \r
6308     case EP_ClearBoard:\r
6309       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6310       fromX = fromY = -1;\r
6311       break;\r
6312 \r
6313     case EP_White:\r
6314       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6315       fromX = fromY = -1;\r
6316       break;\r
6317 \r
6318     case EP_Black:\r
6319       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6320       fromX = fromY = -1;\r
6321       break;\r
6322 \r
6323     case EP_Promote:\r
6324       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6325       fromX = fromY = -1;\r
6326       break;\r
6327 \r
6328     case EP_Demote:\r
6329       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6330       fromX = fromY = -1;\r
6331       break;\r
6332 \r
6333     case DP_Pawn:\r
6334       DropMenuEvent(WhitePawn, fromX, fromY);\r
6335       fromX = fromY = -1;\r
6336       break;\r
6337 \r
6338     case DP_Knight:\r
6339       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6340       fromX = fromY = -1;\r
6341       break;\r
6342 \r
6343     case DP_Bishop:\r
6344       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6345       fromX = fromY = -1;\r
6346       break;\r
6347 \r
6348     case DP_Rook:\r
6349       DropMenuEvent(WhiteRook, fromX, fromY);\r
6350       fromX = fromY = -1;\r
6351       break;\r
6352 \r
6353     case DP_Queen:\r
6354       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6355       fromX = fromY = -1;\r
6356       break;\r
6357 \r
6358     default:\r
6359       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6360     }\r
6361     break;\r
6362 \r
6363   case WM_TIMER:\r
6364     switch (wParam) {\r
6365     case CLOCK_TIMER_ID:\r
6366       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6367       clockTimerEvent = 0;\r
6368       DecrementClocks(); /* call into back end */\r
6369       break;\r
6370     case LOAD_GAME_TIMER_ID:\r
6371       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6372       loadGameTimerEvent = 0;\r
6373       AutoPlayGameLoop(); /* call into back end */\r
6374       break;\r
6375     case ANALYSIS_TIMER_ID:\r
6376       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6377                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6378         AnalysisPeriodicEvent(0);\r
6379       } else {\r
6380         KillTimer(hwnd, analysisTimerEvent);\r
6381         analysisTimerEvent = 0;\r
6382       }\r
6383       break;\r
6384     case DELAYED_TIMER_ID:\r
6385       KillTimer(hwnd, delayedTimerEvent);\r
6386       delayedTimerEvent = 0;\r
6387       delayedTimerCallback();\r
6388       break;\r
6389     }\r
6390     break;\r
6391 \r
6392   case WM_USER_Input:\r
6393     InputEvent(hwnd, message, wParam, lParam);\r
6394     break;\r
6395 \r
6396   /* [AS] Also move "attached" child windows */\r
6397   case WM_WINDOWPOSCHANGING:\r
6398 \r
6399     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6400         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6401 \r
6402         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6403             /* Window is moving */\r
6404             RECT rcMain;\r
6405 \r
6406 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6407             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6408             rcMain.right  = boardX + winWidth;\r
6409             rcMain.top    = boardY;\r
6410             rcMain.bottom = boardY + winHeight;\r
6411             \r
6412             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6413             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6414             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6415             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6416             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6417             boardX = lpwp->x;\r
6418             boardY = lpwp->y;\r
6419         }\r
6420     }\r
6421     break;\r
6422 \r
6423   /* [AS] Snapping */\r
6424   case WM_ENTERSIZEMOVE:\r
6425     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6426     if (hwnd == hwndMain) {\r
6427       doingSizing = TRUE;\r
6428       lastSizing = 0;\r
6429     }\r
6430     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6431     break;\r
6432 \r
6433   case WM_SIZING:\r
6434     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6435     if (hwnd == hwndMain) {\r
6436       lastSizing = wParam;\r
6437     }\r
6438     break;\r
6439 \r
6440   case WM_MOVING:\r
6441     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6442       return OnMoving( &sd, hwnd, wParam, lParam );\r
6443 \r
6444   case WM_EXITSIZEMOVE:\r
6445     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6446     if (hwnd == hwndMain) {\r
6447       RECT client;\r
6448       doingSizing = FALSE;\r
6449       InvalidateRect(hwnd, &boardRect, FALSE);\r
6450       GetClientRect(hwnd, &client);\r
6451       ResizeBoard(client.right, client.bottom, lastSizing);\r
6452       lastSizing = 0;\r
6453       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6454     }\r
6455     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6456     break;\r
6457 \r
6458   case WM_DESTROY: /* message: window being destroyed */\r
6459     PostQuitMessage(0);\r
6460     break;\r
6461 \r
6462   case WM_CLOSE:\r
6463     if (hwnd == hwndMain) {\r
6464       ExitEvent(0);\r
6465     }\r
6466     break;\r
6467 \r
6468   default:      /* Passes it on if unprocessed */\r
6469     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6470   }\r
6471   return 0;\r
6472 }\r
6473 \r
6474 /*---------------------------------------------------------------------------*\\r
6475  *\r
6476  * Misc utility routines\r
6477  *\r
6478 \*---------------------------------------------------------------------------*/\r
6479 \r
6480 /*\r
6481  * Decent random number generator, at least not as bad as Windows\r
6482  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6483  */\r
6484 unsigned int randstate;\r
6485 \r
6486 int\r
6487 myrandom(void)\r
6488 {\r
6489   randstate = randstate * 1664525 + 1013904223;\r
6490   return (int) randstate & 0x7fffffff;\r
6491 }\r
6492 \r
6493 void\r
6494 mysrandom(unsigned int seed)\r
6495 {\r
6496   randstate = seed;\r
6497 }\r
6498 \r
6499 \r
6500 /* \r
6501  * returns TRUE if user selects a different color, FALSE otherwise \r
6502  */\r
6503 \r
6504 BOOL\r
6505 ChangeColor(HWND hwnd, COLORREF *which)\r
6506 {\r
6507   static BOOL firstTime = TRUE;\r
6508   static DWORD customColors[16];\r
6509   CHOOSECOLOR cc;\r
6510   COLORREF newcolor;\r
6511   int i;\r
6512   ColorClass ccl;\r
6513 \r
6514   if (firstTime) {\r
6515     /* Make initial colors in use available as custom colors */\r
6516     /* Should we put the compiled-in defaults here instead? */\r
6517     i = 0;\r
6518     customColors[i++] = lightSquareColor & 0xffffff;\r
6519     customColors[i++] = darkSquareColor & 0xffffff;\r
6520     customColors[i++] = whitePieceColor & 0xffffff;\r
6521     customColors[i++] = blackPieceColor & 0xffffff;\r
6522     customColors[i++] = highlightSquareColor & 0xffffff;\r
6523     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6524 \r
6525     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6526       customColors[i++] = textAttribs[ccl].color;\r
6527     }\r
6528     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6529     firstTime = FALSE;\r
6530   }\r
6531 \r
6532   cc.lStructSize = sizeof(cc);\r
6533   cc.hwndOwner = hwnd;\r
6534   cc.hInstance = NULL;\r
6535   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6536   cc.lpCustColors = (LPDWORD) customColors;\r
6537   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6538 \r
6539   if (!ChooseColor(&cc)) return FALSE;\r
6540 \r
6541   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6542   if (newcolor == *which) return FALSE;\r
6543   *which = newcolor;\r
6544   return TRUE;\r
6545 \r
6546   /*\r
6547   InitDrawingColors();\r
6548   InvalidateRect(hwnd, &boardRect, FALSE);\r
6549   */\r
6550 }\r
6551 \r
6552 BOOLEAN\r
6553 MyLoadSound(MySound *ms)\r
6554 {\r
6555   BOOL ok = FALSE;\r
6556   struct stat st;\r
6557   FILE *f;\r
6558 \r
6559   if (ms->data) free(ms->data);\r
6560   ms->data = NULL;\r
6561 \r
6562   switch (ms->name[0]) {\r
6563   case NULLCHAR:\r
6564     /* Silence */\r
6565     ok = TRUE;\r
6566     break;\r
6567   case '$':\r
6568     /* System sound from Control Panel.  Don't preload here. */\r
6569     ok = TRUE;\r
6570     break;\r
6571   case '!':\r
6572     if (ms->name[1] == NULLCHAR) {\r
6573       /* "!" alone = silence */\r
6574       ok = TRUE;\r
6575     } else {\r
6576       /* Builtin wave resource.  Error if not found. */\r
6577       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6578       if (h == NULL) break;\r
6579       ms->data = (void *)LoadResource(hInst, h);\r
6580       if (h == NULL) break;\r
6581       ok = TRUE;\r
6582     }\r
6583     break;\r
6584   default:\r
6585     /* .wav file.  Error if not found. */\r
6586     f = fopen(ms->name, "rb");\r
6587     if (f == NULL) break;\r
6588     if (fstat(fileno(f), &st) < 0) break;\r
6589     ms->data = malloc(st.st_size);\r
6590     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6591     fclose(f);\r
6592     ok = TRUE;\r
6593     break;\r
6594   }\r
6595   if (!ok) {\r
6596     char buf[MSG_SIZ];\r
6597     sprintf(buf, "Error loading sound %s", ms->name);\r
6598     DisplayError(buf, GetLastError());\r
6599   }\r
6600   return ok;\r
6601 }\r
6602 \r
6603 BOOLEAN\r
6604 MyPlaySound(MySound *ms)\r
6605 {\r
6606   BOOLEAN ok = FALSE;\r
6607 \r
6608   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6609   switch (ms->name[0]) {\r
6610   case NULLCHAR:\r
6611         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6612     /* Silence */\r
6613     ok = TRUE;\r
6614     break;\r
6615   case '$':\r
6616     /* System sound from Control Panel (deprecated feature).\r
6617        "$" alone or an unset sound name gets default beep (still in use). */\r
6618     if (ms->name[1]) {\r
6619       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6620     }\r
6621     if (!ok) ok = MessageBeep(MB_OK);\r
6622     break; \r
6623   case '!':\r
6624     /* Builtin wave resource, or "!" alone for silence */\r
6625     if (ms->name[1]) {\r
6626       if (ms->data == NULL) return FALSE;\r
6627       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6628     } else {\r
6629       ok = TRUE;\r
6630     }\r
6631     break;\r
6632   default:\r
6633     /* .wav file.  Error if not found. */\r
6634     if (ms->data == NULL) return FALSE;\r
6635     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6636     break;\r
6637   }\r
6638   /* Don't print an error: this can happen innocently if the sound driver\r
6639      is busy; for instance, if another instance of WinBoard is playing\r
6640      a sound at about the same time. */\r
6641   return ok;\r
6642 }\r
6643 \r
6644 \r
6645 LRESULT CALLBACK\r
6646 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6647 {\r
6648   BOOL ok;\r
6649   OPENFILENAME *ofn;\r
6650   static UINT *number; /* gross that this is static */\r
6651 \r
6652   switch (message) {\r
6653   case WM_INITDIALOG: /* message: initialize dialog box */\r
6654     /* Center the dialog over the application window */\r
6655     ofn = (OPENFILENAME *) lParam;\r
6656     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6657       number = (UINT *) ofn->lCustData;\r
6658       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6659     } else {\r
6660       number = NULL;\r
6661     }\r
6662     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6663     return FALSE;  /* Allow for further processing */\r
6664 \r
6665   case WM_COMMAND:\r
6666     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6667       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6668     }\r
6669     return FALSE;  /* Allow for further processing */\r
6670   }\r
6671   return FALSE;\r
6672 }\r
6673 \r
6674 UINT APIENTRY\r
6675 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6676 {\r
6677   static UINT *number;\r
6678   OPENFILENAME *ofname;\r
6679   OFNOTIFY *ofnot;\r
6680   switch (uiMsg) {\r
6681   case WM_INITDIALOG:\r
6682     ofname = (OPENFILENAME *)lParam;\r
6683     number = (UINT *)(ofname->lCustData);\r
6684     break;\r
6685   case WM_NOTIFY:\r
6686     ofnot = (OFNOTIFY *)lParam;\r
6687     if (ofnot->hdr.code == CDN_FILEOK) {\r
6688       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6689     }\r
6690     break;\r
6691   }\r
6692   return 0;\r
6693 }\r
6694 \r
6695 \r
6696 FILE *\r
6697 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6698                char *nameFilt, char *dlgTitle, UINT *number,\r
6699                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6700 {\r
6701   OPENFILENAME openFileName;\r
6702   char buf1[MSG_SIZ];\r
6703   FILE *f;\r
6704 \r
6705   if (fileName == NULL) fileName = buf1;\r
6706   if (defName == NULL) {\r
6707     strcpy(fileName, "*.");\r
6708     strcat(fileName, defExt);\r
6709   } else {\r
6710     strcpy(fileName, defName);\r
6711   }\r
6712   if (fileTitle) strcpy(fileTitle, "");\r
6713   if (number) *number = 0;\r
6714 \r
6715   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6716   openFileName.hwndOwner         = hwnd;\r
6717   openFileName.hInstance         = (HANDLE) hInst;\r
6718   openFileName.lpstrFilter       = nameFilt;\r
6719   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6720   openFileName.nMaxCustFilter    = 0L;\r
6721   openFileName.nFilterIndex      = 1L;\r
6722   openFileName.lpstrFile         = fileName;\r
6723   openFileName.nMaxFile          = MSG_SIZ;\r
6724   openFileName.lpstrFileTitle    = fileTitle;\r
6725   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6726   openFileName.lpstrInitialDir   = NULL;\r
6727   openFileName.lpstrTitle        = dlgTitle;\r
6728   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6729     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6730     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6731     | (oldDialog ? 0 : OFN_EXPLORER);\r
6732   openFileName.nFileOffset       = 0;\r
6733   openFileName.nFileExtension    = 0;\r
6734   openFileName.lpstrDefExt       = defExt;\r
6735   openFileName.lCustData         = (LONG) number;\r
6736   openFileName.lpfnHook          = oldDialog ?\r
6737     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6738   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6739 \r
6740   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6741                         GetOpenFileName(&openFileName)) {\r
6742     /* open the file */\r
6743     f = fopen(openFileName.lpstrFile, write);\r
6744     if (f == NULL) {\r
6745       MessageBox(hwnd, "File open failed", NULL,\r
6746                  MB_OK|MB_ICONEXCLAMATION);\r
6747       return NULL;\r
6748     }\r
6749   } else {\r
6750     int err = CommDlgExtendedError();\r
6751     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6752     return FALSE;\r
6753   }\r
6754   return f;\r
6755 }\r
6756 \r
6757 \r
6758 \r
6759 VOID APIENTRY\r
6760 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6761 {\r
6762   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6763 \r
6764   /*\r
6765    * Get the first pop-up menu in the menu template. This is the\r
6766    * menu that TrackPopupMenu displays.\r
6767    */\r
6768   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6769 \r
6770   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6771 \r
6772   /*\r
6773    * TrackPopup uses screen coordinates, so convert the\r
6774    * coordinates of the mouse click to screen coordinates.\r
6775    */\r
6776   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6777 \r
6778   /* Draw and track the floating pop-up menu. */\r
6779   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6780                  pt.x, pt.y, 0, hwnd, NULL);\r
6781 \r
6782   /* Destroy the menu.*/\r
6783   DestroyMenu(hmenu);\r
6784 }\r
6785    \r
6786 typedef struct {\r
6787   HWND hDlg, hText;\r
6788   int sizeX, sizeY, newSizeX, newSizeY;\r
6789   HDWP hdwp;\r
6790 } ResizeEditPlusButtonsClosure;\r
6791 \r
6792 BOOL CALLBACK\r
6793 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6794 {\r
6795   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6796   RECT rect;\r
6797   POINT pt;\r
6798 \r
6799   if (hChild == cl->hText) return TRUE;\r
6800   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6801   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6802   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6803   ScreenToClient(cl->hDlg, &pt);\r
6804   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6805     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6806   return TRUE;\r
6807 }\r
6808 \r
6809 /* Resize a dialog that has a (rich) edit field filling most of\r
6810    the top, with a row of buttons below */\r
6811 VOID\r
6812 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6813 {\r
6814   RECT rectText;\r
6815   int newTextHeight, newTextWidth;\r
6816   ResizeEditPlusButtonsClosure cl;\r
6817   \r
6818   /*if (IsIconic(hDlg)) return;*/\r
6819   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6820   \r
6821   cl.hdwp = BeginDeferWindowPos(8);\r
6822 \r
6823   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6824   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6825   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6826   if (newTextHeight < 0) {\r
6827     newSizeY += -newTextHeight;\r
6828     newTextHeight = 0;\r
6829   }\r
6830   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6831     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6832 \r
6833   cl.hDlg = hDlg;\r
6834   cl.hText = hText;\r
6835   cl.sizeX = sizeX;\r
6836   cl.sizeY = sizeY;\r
6837   cl.newSizeX = newSizeX;\r
6838   cl.newSizeY = newSizeY;\r
6839   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6840 \r
6841   EndDeferWindowPos(cl.hdwp);\r
6842 }\r
6843 \r
6844 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6845 {\r
6846     RECT    rChild, rParent;\r
6847     int     wChild, hChild, wParent, hParent;\r
6848     int     wScreen, hScreen, xNew, yNew;\r
6849     HDC     hdc;\r
6850 \r
6851     /* Get the Height and Width of the child window */\r
6852     GetWindowRect (hwndChild, &rChild);\r
6853     wChild = rChild.right - rChild.left;\r
6854     hChild = rChild.bottom - rChild.top;\r
6855 \r
6856     /* Get the Height and Width of the parent window */\r
6857     GetWindowRect (hwndParent, &rParent);\r
6858     wParent = rParent.right - rParent.left;\r
6859     hParent = rParent.bottom - rParent.top;\r
6860 \r
6861     /* Get the display limits */\r
6862     hdc = GetDC (hwndChild);\r
6863     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6864     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6865     ReleaseDC(hwndChild, hdc);\r
6866 \r
6867     /* Calculate new X position, then adjust for screen */\r
6868     xNew = rParent.left + ((wParent - wChild) /2);\r
6869     if (xNew < 0) {\r
6870         xNew = 0;\r
6871     } else if ((xNew+wChild) > wScreen) {\r
6872         xNew = wScreen - wChild;\r
6873     }\r
6874 \r
6875     /* Calculate new Y position, then adjust for screen */\r
6876     if( mode == 0 ) {\r
6877         yNew = rParent.top  + ((hParent - hChild) /2);\r
6878     }\r
6879     else {\r
6880         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6881     }\r
6882 \r
6883     if (yNew < 0) {\r
6884         yNew = 0;\r
6885     } else if ((yNew+hChild) > hScreen) {\r
6886         yNew = hScreen - hChild;\r
6887     }\r
6888 \r
6889     /* Set it, and return */\r
6890     return SetWindowPos (hwndChild, NULL,\r
6891                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6892 }\r
6893 \r
6894 /* Center one window over another */\r
6895 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6896 {\r
6897     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6898 }\r
6899 \r
6900 /*---------------------------------------------------------------------------*\\r
6901  *\r
6902  * Startup Dialog functions\r
6903  *\r
6904 \*---------------------------------------------------------------------------*/\r
6905 void\r
6906 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6907 {\r
6908   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6909 \r
6910   while (*cd != NULL) {\r
6911     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6912     cd++;\r
6913   }\r
6914 }\r
6915 \r
6916 void\r
6917 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6918 {\r
6919   char buf1[ARG_MAX];\r
6920   int len;\r
6921 \r
6922   if (str[0] == '@') {\r
6923     FILE* f = fopen(str + 1, "r");\r
6924     if (f == NULL) {\r
6925       DisplayFatalError(str + 1, errno, 2);\r
6926       return;\r
6927     }\r
6928     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6929     fclose(f);\r
6930     buf1[len] = NULLCHAR;\r
6931     str = buf1;\r
6932   }\r
6933 \r
6934   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6935 \r
6936   for (;;) {\r
6937     char buf[MSG_SIZ];\r
6938     char *end = strchr(str, '\n');\r
6939     if (end == NULL) return;\r
6940     memcpy(buf, str, end - str);\r
6941     buf[end - str] = NULLCHAR;\r
6942     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6943     str = end + 1;\r
6944   }\r
6945 }\r
6946 \r
6947 void\r
6948 SetStartupDialogEnables(HWND hDlg)\r
6949 {\r
6950   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6951     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6952     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6953   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6954     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6955   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6956     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6957   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6958     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6959   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6960     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6961     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6962     IsDlgButtonChecked(hDlg, OPT_View));\r
6963 }\r
6964 \r
6965 char *\r
6966 QuoteForFilename(char *filename)\r
6967 {\r
6968   int dquote, space;\r
6969   dquote = strchr(filename, '"') != NULL;\r
6970   space = strchr(filename, ' ') != NULL;\r
6971   if (dquote || space) {\r
6972     if (dquote) {\r
6973       return "'";\r
6974     } else {\r
6975       return "\"";\r
6976     }\r
6977   } else {\r
6978     return "";\r
6979   }\r
6980 }\r
6981 \r
6982 VOID\r
6983 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6984 {\r
6985   char buf[MSG_SIZ];\r
6986   char *q;\r
6987 \r
6988   InitComboStringsFromOption(hwndCombo, nthnames);\r
6989   q = QuoteForFilename(nthcp);\r
6990   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6991   if (*nthdir != NULLCHAR) {\r
6992     q = QuoteForFilename(nthdir);\r
6993     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6994   }\r
6995   if (*nthcp == NULLCHAR) {\r
6996     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6997   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6998     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6999     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7000   }\r
7001 }\r
7002 \r
7003 LRESULT CALLBACK\r
7004 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7005 {\r
7006   char buf[MSG_SIZ];\r
7007   HANDLE hwndCombo;\r
7008   char *p;\r
7009 \r
7010   switch (message) {\r
7011   case WM_INITDIALOG:\r
7012     /* Center the dialog */\r
7013     CenterWindow (hDlg, GetDesktopWindow());\r
7014     /* Initialize the dialog items */\r
7015     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7016                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7017                   firstChessProgramNames);\r
7018     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7019                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7020                   secondChessProgramNames);\r
7021     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7022     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7023     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7024     if (*appData.icsHelper != NULLCHAR) {\r
7025       char *q = QuoteForFilename(appData.icsHelper);\r
7026       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7027     }\r
7028     if (*appData.icsHost == NULLCHAR) {\r
7029       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7030       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7031     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7032       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7033       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7034     }\r
7035 \r
7036     if (appData.icsActive) {\r
7037       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7038     }\r
7039     else if (appData.noChessProgram) {\r
7040       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7041     }\r
7042     else {\r
7043       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7044     }\r
7045 \r
7046     SetStartupDialogEnables(hDlg);\r
7047     return TRUE;\r
7048 \r
7049   case WM_COMMAND:\r
7050     switch (LOWORD(wParam)) {\r
7051     case IDOK:\r
7052       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7053         strcpy(buf, "/fcp=");\r
7054         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7055         p = buf;\r
7056         ParseArgs(StringGet, &p);\r
7057         strcpy(buf, "/scp=");\r
7058         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7059         p = buf;\r
7060         ParseArgs(StringGet, &p);\r
7061         appData.noChessProgram = FALSE;\r
7062         appData.icsActive = FALSE;\r
7063       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7064         strcpy(buf, "/ics /icshost=");\r
7065         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7066         p = buf;\r
7067         ParseArgs(StringGet, &p);\r
7068         if (appData.zippyPlay) {\r
7069           strcpy(buf, "/fcp=");\r
7070           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7071           p = buf;\r
7072           ParseArgs(StringGet, &p);\r
7073         }\r
7074       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7075         appData.noChessProgram = TRUE;\r
7076         appData.icsActive = FALSE;\r
7077       } else {\r
7078         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7079                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7080         return TRUE;\r
7081       }\r
7082       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7083         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7084         p = buf;\r
7085         ParseArgs(StringGet, &p);\r
7086       }\r
7087       EndDialog(hDlg, TRUE);\r
7088       return TRUE;\r
7089 \r
7090     case IDCANCEL:\r
7091       ExitEvent(0);\r
7092       return TRUE;\r
7093 \r
7094     case IDM_HELPCONTENTS:\r
7095       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7096         MessageBox (GetFocus(),\r
7097                     "Unable to activate help",\r
7098                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7099       }\r
7100       break;\r
7101 \r
7102     default:\r
7103       SetStartupDialogEnables(hDlg);\r
7104       break;\r
7105     }\r
7106     break;\r
7107   }\r
7108   return FALSE;\r
7109 }\r
7110 \r
7111 /*---------------------------------------------------------------------------*\\r
7112  *\r
7113  * About box dialog functions\r
7114  *\r
7115 \*---------------------------------------------------------------------------*/\r
7116 \r
7117 /* Process messages for "About" dialog box */\r
7118 LRESULT CALLBACK\r
7119 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7120 {\r
7121   switch (message) {\r
7122   case WM_INITDIALOG: /* message: initialize dialog box */\r
7123     /* Center the dialog over the application window */\r
7124     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7125     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7126     JAWS_COPYRIGHT\r
7127     return (TRUE);\r
7128 \r
7129   case WM_COMMAND: /* message: received a command */\r
7130     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7131         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7132       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7133       return (TRUE);\r
7134     }\r
7135     break;\r
7136   }\r
7137   return (FALSE);\r
7138 }\r
7139 \r
7140 /*---------------------------------------------------------------------------*\\r
7141  *\r
7142  * Comment Dialog functions\r
7143  *\r
7144 \*---------------------------------------------------------------------------*/\r
7145 \r
7146 LRESULT CALLBACK\r
7147 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7148 {\r
7149   static HANDLE hwndText = NULL;\r
7150   int len, newSizeX, newSizeY, flags;\r
7151   static int sizeX, sizeY;\r
7152   char *str;\r
7153   RECT rect;\r
7154   MINMAXINFO *mmi;\r
7155 \r
7156   switch (message) {\r
7157   case WM_INITDIALOG: /* message: initialize dialog box */\r
7158     /* Initialize the dialog items */\r
7159     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7160     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7161     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7162     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7163     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7164     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7165     SetWindowText(hDlg, commentTitle);\r
7166     if (editComment) {\r
7167       SetFocus(hwndText);\r
7168     } else {\r
7169       SetFocus(GetDlgItem(hDlg, IDOK));\r
7170     }\r
7171     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7172                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7173                 MAKELPARAM(FALSE, 0));\r
7174     /* Size and position the dialog */\r
7175     if (!commentDialog) {\r
7176       commentDialog = hDlg;\r
7177       flags = SWP_NOZORDER;\r
7178       GetClientRect(hDlg, &rect);\r
7179       sizeX = rect.right;\r
7180       sizeY = rect.bottom;\r
7181       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7182           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7183         WINDOWPLACEMENT wp;\r
7184         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7185         wp.length = sizeof(WINDOWPLACEMENT);\r
7186         wp.flags = 0;\r
7187         wp.showCmd = SW_SHOW;\r
7188         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7189         wp.rcNormalPosition.left = commentX;\r
7190         wp.rcNormalPosition.right = commentX + commentW;\r
7191         wp.rcNormalPosition.top = commentY;\r
7192         wp.rcNormalPosition.bottom = commentY + commentH;\r
7193         SetWindowPlacement(hDlg, &wp);\r
7194 \r
7195         GetClientRect(hDlg, &rect);\r
7196         newSizeX = rect.right;\r
7197         newSizeY = rect.bottom;\r
7198         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7199                               newSizeX, newSizeY);\r
7200         sizeX = newSizeX;\r
7201         sizeY = newSizeY;\r
7202       }\r
7203     }\r
7204     return FALSE;\r
7205 \r
7206   case WM_COMMAND: /* message: received a command */\r
7207     switch (LOWORD(wParam)) {\r
7208     case IDOK:\r
7209       if (editComment) {\r
7210         char *p, *q;\r
7211         /* Read changed options from the dialog box */\r
7212         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7213         len = GetWindowTextLength(hwndText);\r
7214         str = (char *) malloc(len + 1);\r
7215         GetWindowText(hwndText, str, len + 1);\r
7216         p = q = str;\r
7217         while (*q) {\r
7218           if (*q == '\r')\r
7219             q++;\r
7220           else\r
7221             *p++ = *q++;\r
7222         }\r
7223         *p = NULLCHAR;\r
7224         ReplaceComment(commentIndex, str);\r
7225         free(str);\r
7226       }\r
7227       CommentPopDown();\r
7228       return TRUE;\r
7229 \r
7230     case IDCANCEL:\r
7231     case OPT_CancelComment:\r
7232       CommentPopDown();\r
7233       return TRUE;\r
7234 \r
7235     case OPT_ClearComment:\r
7236       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7237       break;\r
7238 \r
7239     case OPT_EditComment:\r
7240       EditCommentEvent();\r
7241       return TRUE;\r
7242 \r
7243     default:\r
7244       break;\r
7245     }\r
7246     break;\r
7247 \r
7248   case WM_SIZE:\r
7249     newSizeX = LOWORD(lParam);\r
7250     newSizeY = HIWORD(lParam);\r
7251     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7252     sizeX = newSizeX;\r
7253     sizeY = newSizeY;\r
7254     break;\r
7255 \r
7256   case WM_GETMINMAXINFO:\r
7257     /* Prevent resizing window too small */\r
7258     mmi = (MINMAXINFO *) lParam;\r
7259     mmi->ptMinTrackSize.x = 100;\r
7260     mmi->ptMinTrackSize.y = 100;\r
7261     break;\r
7262   }\r
7263   return FALSE;\r
7264 }\r
7265 \r
7266 VOID\r
7267 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7268 {\r
7269   FARPROC lpProc;\r
7270   char *p, *q;\r
7271 \r
7272   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7273 \r
7274   if (str == NULL) str = "";\r
7275   p = (char *) malloc(2 * strlen(str) + 2);\r
7276   q = p;\r
7277   while (*str) {\r
7278     if (*str == '\n') *q++ = '\r';\r
7279     *q++ = *str++;\r
7280   }\r
7281   *q = NULLCHAR;\r
7282   if (commentText != NULL) free(commentText);\r
7283 \r
7284   commentIndex = index;\r
7285   commentTitle = title;\r
7286   commentText = p;\r
7287   editComment = edit;\r
7288 \r
7289   if (commentDialog) {\r
7290     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7291     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7292   } else {\r
7293     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7294     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7295                  hwndMain, (DLGPROC)lpProc);\r
7296     FreeProcInstance(lpProc);\r
7297   }\r
7298   commentDialogUp = TRUE;\r
7299 }\r
7300 \r
7301 \r
7302 /*---------------------------------------------------------------------------*\\r
7303  *\r
7304  * Type-in move dialog functions\r
7305  * \r
7306 \*---------------------------------------------------------------------------*/\r
7307 \r
7308 LRESULT CALLBACK\r
7309 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7310 {\r
7311   char move[MSG_SIZ];\r
7312   HWND hInput;\r
7313   ChessMove moveType;\r
7314   int fromX, fromY, toX, toY;\r
7315   char promoChar;\r
7316 \r
7317   switch (message) {\r
7318   case WM_INITDIALOG:\r
7319     move[0] = (char) lParam;\r
7320     move[1] = NULLCHAR;\r
7321     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7322     hInput = GetDlgItem(hDlg, OPT_Move);\r
7323     SetWindowText(hInput, move);\r
7324     SetFocus(hInput);\r
7325     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7326     return FALSE;\r
7327 \r
7328   case WM_COMMAND:\r
7329     switch (LOWORD(wParam)) {\r
7330     case IDOK:\r
7331       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7332       { int n; Board board;\r
7333         // [HGM] FENedit\r
7334         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7335                 EditPositionPasteFEN(move);\r
7336                 EndDialog(hDlg, TRUE);\r
7337                 return TRUE;\r
7338         }\r
7339         // [HGM] movenum: allow move number to be typed in any mode\r
7340         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7341           currentMove = 2*n-1;\r
7342           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7343           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7344           EndDialog(hDlg, TRUE);\r
7345           DrawPosition(TRUE, boards[currentMove]);\r
7346           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7347           else DisplayMessage("", "");\r
7348           return TRUE;\r
7349         }\r
7350       }\r
7351       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7352         gameMode != Training) {\r
7353         DisplayMoveError("Displayed move is not current");\r
7354       } else {\r
7355 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7356         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7357           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7358         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7359         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7360           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7361           if (gameMode != Training)\r
7362               forwardMostMove = currentMove;\r
7363           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7364         } else {\r
7365           DisplayMoveError("Could not parse move");\r
7366         }\r
7367       }\r
7368       EndDialog(hDlg, TRUE);\r
7369       return TRUE;\r
7370     case IDCANCEL:\r
7371       EndDialog(hDlg, FALSE);\r
7372       return TRUE;\r
7373     default:\r
7374       break;\r
7375     }\r
7376     break;\r
7377   }\r
7378   return FALSE;\r
7379 }\r
7380 \r
7381 VOID\r
7382 PopUpMoveDialog(char firstchar)\r
7383 {\r
7384     FARPROC lpProc;\r
7385     \r
7386     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7387         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7388         gameMode == AnalyzeMode || gameMode == EditGame || \r
7389         gameMode == EditPosition || gameMode == IcsExamining ||\r
7390         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7391         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7392                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7393                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7394         gameMode == Training) {\r
7395       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7396       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7397         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7398       FreeProcInstance(lpProc);\r
7399     }\r
7400 }\r
7401 \r
7402 /*---------------------------------------------------------------------------*\\r
7403  *\r
7404  * Type-in name dialog functions\r
7405  * \r
7406 \*---------------------------------------------------------------------------*/\r
7407 \r
7408 LRESULT CALLBACK\r
7409 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7410 {\r
7411   char move[MSG_SIZ];\r
7412   HWND hInput;\r
7413 \r
7414   switch (message) {\r
7415   case WM_INITDIALOG:\r
7416     move[0] = (char) lParam;\r
7417     move[1] = NULLCHAR;\r
7418     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7419     hInput = GetDlgItem(hDlg, OPT_Name);\r
7420     SetWindowText(hInput, move);\r
7421     SetFocus(hInput);\r
7422     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7423     return FALSE;\r
7424 \r
7425   case WM_COMMAND:\r
7426     switch (LOWORD(wParam)) {\r
7427     case IDOK:\r
7428       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7429       appData.userName = strdup(move);\r
7430       SetUserLogo();\r
7431 \r
7432       EndDialog(hDlg, TRUE);\r
7433       return TRUE;\r
7434     case IDCANCEL:\r
7435       EndDialog(hDlg, FALSE);\r
7436       return TRUE;\r
7437     default:\r
7438       break;\r
7439     }\r
7440     break;\r
7441   }\r
7442   return FALSE;\r
7443 }\r
7444 \r
7445 VOID\r
7446 PopUpNameDialog(char firstchar)\r
7447 {\r
7448     FARPROC lpProc;\r
7449     \r
7450       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7451       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7452         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7453       FreeProcInstance(lpProc);\r
7454 }\r
7455 \r
7456 /*---------------------------------------------------------------------------*\\r
7457  *\r
7458  *  Error dialogs\r
7459  * \r
7460 \*---------------------------------------------------------------------------*/\r
7461 \r
7462 /* Nonmodal error box */\r
7463 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7464                              WPARAM wParam, LPARAM lParam);\r
7465 \r
7466 VOID\r
7467 ErrorPopUp(char *title, char *content)\r
7468 {\r
7469   FARPROC lpProc;\r
7470   char *p, *q;\r
7471   BOOLEAN modal = hwndMain == NULL;\r
7472 \r
7473   p = content;\r
7474   q = errorMessage;\r
7475   while (*p) {\r
7476     if (*p == '\n') {\r
7477       if (modal) {\r
7478         *q++ = ' ';\r
7479         p++;\r
7480       } else {\r
7481         *q++ = '\r';\r
7482         *q++ = *p++;\r
7483       }\r
7484     } else {\r
7485       *q++ = *p++;\r
7486     }\r
7487   }\r
7488   *q = NULLCHAR;\r
7489   strncpy(errorTitle, title, sizeof(errorTitle));\r
7490   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7491   \r
7492   if (modal) {\r
7493     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7494   } else {\r
7495     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7496     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7497                  hwndMain, (DLGPROC)lpProc);\r
7498     FreeProcInstance(lpProc);\r
7499   }\r
7500 }\r
7501 \r
7502 VOID\r
7503 ErrorPopDown()\r
7504 {\r
7505   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7506   if (errorDialog == NULL) return;\r
7507   DestroyWindow(errorDialog);\r
7508   errorDialog = NULL;\r
7509 }\r
7510 \r
7511 LRESULT CALLBACK\r
7512 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7513 {\r
7514   HANDLE hwndText;\r
7515   RECT rChild;\r
7516 \r
7517   switch (message) {\r
7518   case WM_INITDIALOG:\r
7519     GetWindowRect(hDlg, &rChild);\r
7520 \r
7521     /*\r
7522     SetWindowPos(hDlg, NULL, rChild.left,\r
7523       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7524       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7525     */\r
7526 \r
7527     /* \r
7528         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7529         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7530         and it doesn't work when you resize the dialog.\r
7531         For now, just give it a default position.\r
7532     */\r
7533     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7534 \r
7535     errorDialog = hDlg;\r
7536     SetWindowText(hDlg, errorTitle);\r
7537     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7538     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7539     return FALSE;\r
7540 \r
7541   case WM_COMMAND:\r
7542     switch (LOWORD(wParam)) {\r
7543     case IDOK:\r
7544     case IDCANCEL:\r
7545       if (errorDialog == hDlg) errorDialog = NULL;\r
7546       DestroyWindow(hDlg);\r
7547       return TRUE;\r
7548 \r
7549     default:\r
7550       break;\r
7551     }\r
7552     break;\r
7553   }\r
7554   return FALSE;\r
7555 }\r
7556 \r
7557 #ifdef GOTHIC\r
7558 HWND gothicDialog = NULL;\r
7559 \r
7560 LRESULT CALLBACK\r
7561 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7562 {\r
7563   HANDLE hwndText;\r
7564   RECT rChild;\r
7565   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7566 \r
7567   switch (message) {\r
7568   case WM_INITDIALOG:\r
7569     GetWindowRect(hDlg, &rChild);\r
7570 \r
7571     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7572                                                              SWP_NOZORDER);\r
7573 \r
7574     /* \r
7575         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7576         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7577         and it doesn't work when you resize the dialog.\r
7578         For now, just give it a default position.\r
7579     */\r
7580     gothicDialog = hDlg;\r
7581     SetWindowText(hDlg, errorTitle);\r
7582     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7583     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7584     return FALSE;\r
7585 \r
7586   case WM_COMMAND:\r
7587     switch (LOWORD(wParam)) {\r
7588     case IDOK:\r
7589     case IDCANCEL:\r
7590       if (errorDialog == hDlg) errorDialog = NULL;\r
7591       DestroyWindow(hDlg);\r
7592       return TRUE;\r
7593 \r
7594     default:\r
7595       break;\r
7596     }\r
7597     break;\r
7598   }\r
7599   return FALSE;\r
7600 }\r
7601 \r
7602 VOID\r
7603 GothicPopUp(char *title, VariantClass variant)\r
7604 {\r
7605   FARPROC lpProc;\r
7606   static char *lastTitle;\r
7607 \r
7608   strncpy(errorTitle, title, sizeof(errorTitle));\r
7609   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7610 \r
7611   if(lastTitle != title && gothicDialog != NULL) {\r
7612     DestroyWindow(gothicDialog);\r
7613     gothicDialog = NULL;\r
7614   }\r
7615   if(variant != VariantNormal && gothicDialog == NULL) {\r
7616     title = lastTitle;\r
7617     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7618     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7619                  hwndMain, (DLGPROC)lpProc);\r
7620     FreeProcInstance(lpProc);\r
7621   }\r
7622 }\r
7623 #endif\r
7624 \r
7625 /*---------------------------------------------------------------------------*\\r
7626  *\r
7627  *  Ics Interaction console functions\r
7628  *\r
7629 \*---------------------------------------------------------------------------*/\r
7630 \r
7631 #define HISTORY_SIZE 64\r
7632 static char *history[HISTORY_SIZE];\r
7633 int histIn = 0, histP = 0;\r
7634 \r
7635 VOID\r
7636 SaveInHistory(char *cmd)\r
7637 {\r
7638   if (history[histIn] != NULL) {\r
7639     free(history[histIn]);\r
7640     history[histIn] = NULL;\r
7641   }\r
7642   if (*cmd == NULLCHAR) return;\r
7643   history[histIn] = StrSave(cmd);\r
7644   histIn = (histIn + 1) % HISTORY_SIZE;\r
7645   if (history[histIn] != NULL) {\r
7646     free(history[histIn]);\r
7647     history[histIn] = NULL;\r
7648   }\r
7649   histP = histIn;\r
7650 }\r
7651 \r
7652 char *\r
7653 PrevInHistory(char *cmd)\r
7654 {\r
7655   int newhp;\r
7656   if (histP == histIn) {\r
7657     if (history[histIn] != NULL) free(history[histIn]);\r
7658     history[histIn] = StrSave(cmd);\r
7659   }\r
7660   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7661   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7662   histP = newhp;\r
7663   return history[histP];\r
7664 }\r
7665 \r
7666 char *\r
7667 NextInHistory()\r
7668 {\r
7669   if (histP == histIn) return NULL;\r
7670   histP = (histP + 1) % HISTORY_SIZE;\r
7671   return history[histP];\r
7672 }\r
7673 \r
7674 typedef struct {\r
7675   char *item;\r
7676   char *command;\r
7677   BOOLEAN getname;\r
7678   BOOLEAN immediate;\r
7679 } IcsTextMenuEntry;\r
7680 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7681 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7682 \r
7683 void\r
7684 ParseIcsTextMenu(char *icsTextMenuString)\r
7685 {\r
7686 //  int flags = 0;\r
7687   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7688   char *p = icsTextMenuString;\r
7689   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7690     free(e->item);\r
7691     e->item = NULL;\r
7692     if (e->command != NULL) {\r
7693       free(e->command);\r
7694       e->command = NULL;\r
7695     }\r
7696     e++;\r
7697   }\r
7698   e = icsTextMenuEntry;\r
7699   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7700     if (*p == ';' || *p == '\n') {\r
7701       e->item = strdup("-");\r
7702       e->command = NULL;\r
7703       p++;\r
7704     } else if (*p == '-') {\r
7705       e->item = strdup("-");\r
7706       e->command = NULL;\r
7707       p++;\r
7708       if (*p) p++;\r
7709     } else {\r
7710       char *q, *r, *s, *t;\r
7711       char c;\r
7712       q = strchr(p, ',');\r
7713       if (q == NULL) break;\r
7714       *q = NULLCHAR;\r
7715       r = strchr(q + 1, ',');\r
7716       if (r == NULL) break;\r
7717       *r = NULLCHAR;\r
7718       s = strchr(r + 1, ',');\r
7719       if (s == NULL) break;\r
7720       *s = NULLCHAR;\r
7721       c = ';';\r
7722       t = strchr(s + 1, c);\r
7723       if (t == NULL) {\r
7724         c = '\n';\r
7725         t = strchr(s + 1, c);\r
7726       }\r
7727       if (t != NULL) *t = NULLCHAR;\r
7728       e->item = strdup(p);\r
7729       e->command = strdup(q + 1);\r
7730       e->getname = *(r + 1) != '0';\r
7731       e->immediate = *(s + 1) != '0';\r
7732       *q = ',';\r
7733       *r = ',';\r
7734       *s = ',';\r
7735       if (t == NULL) break;\r
7736       *t = c;\r
7737       p = t + 1;\r
7738     }\r
7739     e++;\r
7740   } \r
7741 }\r
7742 \r
7743 HMENU\r
7744 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7745 {\r
7746   HMENU hmenu, h;\r
7747   int i = 0;\r
7748   hmenu = LoadMenu(hInst, "TextMenu");\r
7749   h = GetSubMenu(hmenu, 0);\r
7750   while (e->item) {\r
7751     if (strcmp(e->item, "-") == 0) {\r
7752       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7753     } else {\r
7754       if (e->item[0] == '|') {\r
7755         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7756                    IDM_CommandX + i, &e->item[1]);\r
7757       } else {\r
7758         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7759       }\r
7760     }\r
7761     e++;\r
7762     i++;\r
7763   } \r
7764   return hmenu;\r
7765 }\r
7766 \r
7767 WNDPROC consoleTextWindowProc;\r
7768 \r
7769 void\r
7770 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7771 {\r
7772   char buf[MSG_SIZ], name[MSG_SIZ];\r
7773   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7774   CHARRANGE sel;\r
7775 \r
7776   if (!getname) {\r
7777     SetWindowText(hInput, command);\r
7778     if (immediate) {\r
7779       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7780     } else {\r
7781       sel.cpMin = 999999;\r
7782       sel.cpMax = 999999;\r
7783       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7784       SetFocus(hInput);\r
7785     }\r
7786     return;\r
7787   }    \r
7788   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7789   if (sel.cpMin == sel.cpMax) {\r
7790     /* Expand to surrounding word */\r
7791     TEXTRANGE tr;\r
7792     do {\r
7793       tr.chrg.cpMax = sel.cpMin;\r
7794       tr.chrg.cpMin = --sel.cpMin;\r
7795       if (sel.cpMin < 0) break;\r
7796       tr.lpstrText = name;\r
7797       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7798     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7799     sel.cpMin++;\r
7800 \r
7801     do {\r
7802       tr.chrg.cpMin = sel.cpMax;\r
7803       tr.chrg.cpMax = ++sel.cpMax;\r
7804       tr.lpstrText = name;\r
7805       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7806     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7807     sel.cpMax--;\r
7808 \r
7809     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7810       MessageBeep(MB_ICONEXCLAMATION);\r
7811       return;\r
7812     }\r
7813     tr.chrg = sel;\r
7814     tr.lpstrText = name;\r
7815     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7816   } else {\r
7817     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7818       MessageBeep(MB_ICONEXCLAMATION);\r
7819       return;\r
7820     }\r
7821     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7822   }\r
7823   if (immediate) {\r
7824     sprintf(buf, "%s %s", command, name);\r
7825     SetWindowText(hInput, buf);\r
7826     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7827   } else {\r
7828     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7829     SetWindowText(hInput, buf);\r
7830     sel.cpMin = 999999;\r
7831     sel.cpMax = 999999;\r
7832     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7833     SetFocus(hInput);\r
7834   }\r
7835 }\r
7836 \r
7837 LRESULT CALLBACK \r
7838 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7839 {\r
7840   HWND hInput;\r
7841   CHARRANGE sel;\r
7842 \r
7843   switch (message) {\r
7844   case WM_KEYDOWN:\r
7845     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7846     switch (wParam) {\r
7847     case VK_PRIOR:\r
7848       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7849       return 0;\r
7850     case VK_NEXT:\r
7851       sel.cpMin = 999999;\r
7852       sel.cpMax = 999999;\r
7853       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7854       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7855       return 0;\r
7856     }\r
7857     break;\r
7858   case WM_CHAR:\r
7859    if(wParam != '\022') {\r
7860     if (wParam == '\t') {\r
7861       if (GetKeyState(VK_SHIFT) < 0) {\r
7862         /* shifted */\r
7863         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7864         if (buttonDesc[0].hwnd) {\r
7865           SetFocus(buttonDesc[0].hwnd);\r
7866         } else {\r
7867           SetFocus(hwndMain);\r
7868         }\r
7869       } else {\r
7870         /* unshifted */\r
7871         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7872       }\r
7873     } else {\r
7874       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7875       JAWS_DELETE( SetFocus(hInput); )\r
7876       SendMessage(hInput, message, wParam, lParam);\r
7877     }\r
7878     return 0;\r
7879    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7880   case WM_RBUTTONUP:\r
7881     if (GetKeyState(VK_SHIFT) & ~1) {\r
7882       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7883         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7884     } else {\r
7885       POINT pt;\r
7886       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7887       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7888       if (sel.cpMin == sel.cpMax) {\r
7889         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7890         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7891       }\r
7892       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7893         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7894       }\r
7895       pt.x = LOWORD(lParam);\r
7896       pt.y = HIWORD(lParam);\r
7897       MenuPopup(hwnd, pt, hmenu, -1);\r
7898     }\r
7899     return 0;\r
7900   case WM_PASTE:\r
7901     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7902     SetFocus(hInput);\r
7903     return SendMessage(hInput, message, wParam, lParam);\r
7904   case WM_MBUTTONDOWN:\r
7905     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7906   case WM_RBUTTONDOWN:\r
7907     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7908       /* Move selection here if it was empty */\r
7909       POINT pt;\r
7910       pt.x = LOWORD(lParam);\r
7911       pt.y = HIWORD(lParam);\r
7912       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7913       if (sel.cpMin == sel.cpMax) {\r
7914         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7915         sel.cpMax = sel.cpMin;\r
7916         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7917       }\r
7918       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7919     }\r
7920     return 0;\r
7921   case WM_COMMAND:\r
7922     switch (LOWORD(wParam)) {\r
7923     case IDM_QuickPaste:\r
7924       {\r
7925         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7926         if (sel.cpMin == sel.cpMax) {\r
7927           MessageBeep(MB_ICONEXCLAMATION);\r
7928           return 0;\r
7929         }\r
7930         SendMessage(hwnd, WM_COPY, 0, 0);\r
7931         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7932         SendMessage(hInput, WM_PASTE, 0, 0);\r
7933         SetFocus(hInput);\r
7934         return 0;\r
7935       }\r
7936     case IDM_Cut:\r
7937       SendMessage(hwnd, WM_CUT, 0, 0);\r
7938       return 0;\r
7939     case IDM_Paste:\r
7940       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7941       return 0;\r
7942     case IDM_Copy:\r
7943       SendMessage(hwnd, WM_COPY, 0, 0);\r
7944       return 0;\r
7945     default:\r
7946       {\r
7947         int i = LOWORD(wParam) - IDM_CommandX;\r
7948         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7949             icsTextMenuEntry[i].command != NULL) {\r
7950           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7951                    icsTextMenuEntry[i].getname,\r
7952                    icsTextMenuEntry[i].immediate);\r
7953           return 0;\r
7954         }\r
7955       }\r
7956       break;\r
7957     }\r
7958     break;\r
7959   }\r
7960   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7961 }\r
7962 \r
7963 WNDPROC consoleInputWindowProc;\r
7964 \r
7965 LRESULT CALLBACK\r
7966 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7967 {\r
7968   char buf[MSG_SIZ];\r
7969   char *p;\r
7970   static BOOL sendNextChar = FALSE;\r
7971   static BOOL quoteNextChar = FALSE;\r
7972   InputSource *is = consoleInputSource;\r
7973   CHARFORMAT cf;\r
7974   CHARRANGE sel;\r
7975 \r
7976   switch (message) {\r
7977   case WM_CHAR:\r
7978     if (!appData.localLineEditing || sendNextChar) {\r
7979       is->buf[0] = (CHAR) wParam;\r
7980       is->count = 1;\r
7981       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7982       sendNextChar = FALSE;\r
7983       return 0;\r
7984     }\r
7985     if (quoteNextChar) {\r
7986       buf[0] = (char) wParam;\r
7987       buf[1] = NULLCHAR;\r
7988       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7989       quoteNextChar = FALSE;\r
7990       return 0;\r
7991     }\r
7992     switch (wParam) {\r
7993     case '\r':   /* Enter key */\r
7994       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7995       if (consoleEcho) SaveInHistory(is->buf);\r
7996       is->buf[is->count++] = '\n';\r
7997       is->buf[is->count] = NULLCHAR;\r
7998       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7999       if (consoleEcho) {\r
8000         ConsoleOutput(is->buf, is->count, TRUE);\r
8001       } else if (appData.localLineEditing) {\r
8002         ConsoleOutput("\n", 1, TRUE);\r
8003       }\r
8004       /* fall thru */\r
8005     case '\033': /* Escape key */\r
8006       SetWindowText(hwnd, "");\r
8007       cf.cbSize = sizeof(CHARFORMAT);\r
8008       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8009       if (consoleEcho) {\r
8010         cf.crTextColor = textAttribs[ColorNormal].color;\r
8011       } else {\r
8012         cf.crTextColor = COLOR_ECHOOFF;\r
8013       }\r
8014       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8015       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8016       return 0;\r
8017     case '\t':   /* Tab key */\r
8018       if (GetKeyState(VK_SHIFT) < 0) {\r
8019         /* shifted */\r
8020         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8021       } else {\r
8022         /* unshifted */\r
8023         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8024         if (buttonDesc[0].hwnd) {\r
8025           SetFocus(buttonDesc[0].hwnd);\r
8026         } else {\r
8027           SetFocus(hwndMain);\r
8028         }\r
8029       }\r
8030       return 0;\r
8031     case '\023': /* Ctrl+S */\r
8032       sendNextChar = TRUE;\r
8033       return 0;\r
8034     case '\021': /* Ctrl+Q */\r
8035       quoteNextChar = TRUE;\r
8036       return 0;\r
8037     JAWS_REPLAY\r
8038     default:\r
8039       break;\r
8040     }\r
8041     break;\r
8042   case WM_KEYDOWN:\r
8043     switch (wParam) {\r
8044     case VK_UP:\r
8045       GetWindowText(hwnd, buf, MSG_SIZ);\r
8046       p = PrevInHistory(buf);\r
8047       if (p != NULL) {\r
8048         SetWindowText(hwnd, p);\r
8049         sel.cpMin = 999999;\r
8050         sel.cpMax = 999999;\r
8051         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8052         return 0;\r
8053       }\r
8054       break;\r
8055     case VK_DOWN:\r
8056       p = NextInHistory();\r
8057       if (p != NULL) {\r
8058         SetWindowText(hwnd, p);\r
8059         sel.cpMin = 999999;\r
8060         sel.cpMax = 999999;\r
8061         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8062         return 0;\r
8063       }\r
8064       break;\r
8065     case VK_HOME:\r
8066     case VK_END:\r
8067       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8068       /* fall thru */\r
8069     case VK_PRIOR:\r
8070     case VK_NEXT:\r
8071       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8072       return 0;\r
8073     }\r
8074     break;\r
8075   case WM_MBUTTONDOWN:\r
8076     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8077       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8078     break;\r
8079   case WM_RBUTTONUP:\r
8080     if (GetKeyState(VK_SHIFT) & ~1) {\r
8081       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8082         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8083     } else {\r
8084       POINT pt;\r
8085       HMENU hmenu;\r
8086       hmenu = LoadMenu(hInst, "InputMenu");\r
8087       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8088       if (sel.cpMin == sel.cpMax) {\r
8089         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8090         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8091       }\r
8092       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8093         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8094       }\r
8095       pt.x = LOWORD(lParam);\r
8096       pt.y = HIWORD(lParam);\r
8097       MenuPopup(hwnd, pt, hmenu, -1);\r
8098     }\r
8099     return 0;\r
8100   case WM_COMMAND:\r
8101     switch (LOWORD(wParam)) { \r
8102     case IDM_Undo:\r
8103       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8104       return 0;\r
8105     case IDM_SelectAll:\r
8106       sel.cpMin = 0;\r
8107       sel.cpMax = -1; /*999999?*/\r
8108       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8109       return 0;\r
8110     case IDM_Cut:\r
8111       SendMessage(hwnd, WM_CUT, 0, 0);\r
8112       return 0;\r
8113     case IDM_Paste:\r
8114       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8115       return 0;\r
8116     case IDM_Copy:\r
8117       SendMessage(hwnd, WM_COPY, 0, 0);\r
8118       return 0;\r
8119     }\r
8120     break;\r
8121   }\r
8122   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8123 }\r
8124 \r
8125 #define CO_MAX  100000\r
8126 #define CO_TRIM   1000\r
8127 \r
8128 LRESULT CALLBACK\r
8129 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8130 {\r
8131   static SnapData sd;\r
8132   HWND hText, hInput;\r
8133   RECT rect;\r
8134   static int sizeX, sizeY;\r
8135   int newSizeX, newSizeY;\r
8136   MINMAXINFO *mmi;\r
8137   WORD wMask;\r
8138 \r
8139   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8140   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8141 \r
8142   switch (message) {\r
8143   case WM_NOTIFY:\r
8144     if (((NMHDR*)lParam)->code == EN_LINK)\r
8145     {\r
8146       ENLINK *pLink = (ENLINK*)lParam;\r
8147       if (pLink->msg == WM_LBUTTONUP)\r
8148       {\r
8149         TEXTRANGE tr;\r
8150 \r
8151         tr.chrg = pLink->chrg;\r
8152         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8153         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8154         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8155         free(tr.lpstrText);\r
8156       }\r
8157     }\r
8158     break;\r
8159   case WM_INITDIALOG: /* message: initialize dialog box */\r
8160     hwndConsole = hDlg;\r
8161     SetFocus(hInput);\r
8162     consoleTextWindowProc = (WNDPROC)\r
8163       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8164     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8165     consoleInputWindowProc = (WNDPROC)\r
8166       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8167     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8168     Colorize(ColorNormal, TRUE);\r
8169     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8170     ChangedConsoleFont();\r
8171     GetClientRect(hDlg, &rect);\r
8172     sizeX = rect.right;\r
8173     sizeY = rect.bottom;\r
8174     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8175         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8176       WINDOWPLACEMENT wp;\r
8177       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8178       wp.length = sizeof(WINDOWPLACEMENT);\r
8179       wp.flags = 0;\r
8180       wp.showCmd = SW_SHOW;\r
8181       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8182       wp.rcNormalPosition.left = wpConsole.x;\r
8183       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8184       wp.rcNormalPosition.top = wpConsole.y;\r
8185       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8186       SetWindowPlacement(hDlg, &wp);\r
8187     }\r
8188 \r
8189    // [HGM] Chessknight's change 2004-07-13\r
8190    else { /* Determine Defaults */\r
8191        WINDOWPLACEMENT wp;\r
8192        wpConsole.x = winWidth + 1;\r
8193        wpConsole.y = boardY;\r
8194        wpConsole.width = screenWidth -  winWidth;\r
8195        wpConsole.height = winHeight;\r
8196        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8197        wp.length = sizeof(WINDOWPLACEMENT);\r
8198        wp.flags = 0;\r
8199        wp.showCmd = SW_SHOW;\r
8200        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8201        wp.rcNormalPosition.left = wpConsole.x;\r
8202        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8203        wp.rcNormalPosition.top = wpConsole.y;\r
8204        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8205        SetWindowPlacement(hDlg, &wp);\r
8206     }\r
8207 \r
8208    // Allow hText to highlight URLs and send notifications on them\r
8209    wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8210    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8211    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8212    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
8213 \r
8214     return FALSE;\r
8215 \r
8216   case WM_SETFOCUS:\r
8217     SetFocus(hInput);\r
8218     return 0;\r
8219 \r
8220   case WM_CLOSE:\r
8221     ExitEvent(0);\r
8222     /* not reached */\r
8223     break;\r
8224 \r
8225   case WM_SIZE:\r
8226     if (IsIconic(hDlg)) break;\r
8227     newSizeX = LOWORD(lParam);\r
8228     newSizeY = HIWORD(lParam);\r
8229     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8230       RECT rectText, rectInput;\r
8231       POINT pt;\r
8232       int newTextHeight, newTextWidth;\r
8233       GetWindowRect(hText, &rectText);\r
8234       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8235       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8236       if (newTextHeight < 0) {\r
8237         newSizeY += -newTextHeight;\r
8238         newTextHeight = 0;\r
8239       }\r
8240       SetWindowPos(hText, NULL, 0, 0,\r
8241         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8242       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8243       pt.x = rectInput.left;\r
8244       pt.y = rectInput.top + newSizeY - sizeY;\r
8245       ScreenToClient(hDlg, &pt);\r
8246       SetWindowPos(hInput, NULL, \r
8247         pt.x, pt.y, /* needs client coords */   \r
8248         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8249         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8250     }\r
8251     sizeX = newSizeX;\r
8252     sizeY = newSizeY;\r
8253     break;\r
8254 \r
8255   case WM_GETMINMAXINFO:\r
8256     /* Prevent resizing window too small */\r
8257     mmi = (MINMAXINFO *) lParam;\r
8258     mmi->ptMinTrackSize.x = 100;\r
8259     mmi->ptMinTrackSize.y = 100;\r
8260     break;\r
8261 \r
8262   /* [AS] Snapping */\r
8263   case WM_ENTERSIZEMOVE:\r
8264     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8265 \r
8266   case WM_SIZING:\r
8267     return OnSizing( &sd, hDlg, wParam, lParam );\r
8268 \r
8269   case WM_MOVING:\r
8270     return OnMoving( &sd, hDlg, wParam, lParam );\r
8271 \r
8272   case WM_EXITSIZEMOVE:\r
8273         UpdateICSWidth(hText);\r
8274     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8275   }\r
8276 \r
8277   return DefWindowProc(hDlg, message, wParam, lParam);\r
8278 }\r
8279 \r
8280 \r
8281 VOID\r
8282 ConsoleCreate()\r
8283 {\r
8284   HWND hCons;\r
8285   if (hwndConsole) return;\r
8286   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8287   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8288 }\r
8289 \r
8290 \r
8291 VOID\r
8292 ConsoleOutput(char* data, int length, int forceVisible)\r
8293 {\r
8294   HWND hText;\r
8295   int trim, exlen;\r
8296   char *p, *q;\r
8297   char buf[CO_MAX+1];\r
8298   POINT pEnd;\r
8299   RECT rect;\r
8300   static int delayLF = 0;\r
8301   CHARRANGE savesel, sel;\r
8302 \r
8303   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8304   p = data;\r
8305   q = buf;\r
8306   if (delayLF) {\r
8307     *q++ = '\r';\r
8308     *q++ = '\n';\r
8309     delayLF = 0;\r
8310   }\r
8311   while (length--) {\r
8312     if (*p == '\n') {\r
8313       if (*++p) {\r
8314         *q++ = '\r';\r
8315         *q++ = '\n';\r
8316       } else {\r
8317         delayLF = 1;\r
8318       }\r
8319     } else if (*p == '\007') {\r
8320        MyPlaySound(&sounds[(int)SoundBell]);\r
8321        p++;\r
8322     } else {\r
8323       *q++ = *p++;\r
8324     }\r
8325   }\r
8326   *q = NULLCHAR;\r
8327   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8328   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8329   /* Save current selection */\r
8330   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8331   exlen = GetWindowTextLength(hText);\r
8332   /* Find out whether current end of text is visible */\r
8333   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8334   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8335   /* Trim existing text if it's too long */\r
8336   if (exlen + (q - buf) > CO_MAX) {\r
8337     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8338     sel.cpMin = 0;\r
8339     sel.cpMax = trim;\r
8340     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8341     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8342     exlen -= trim;\r
8343     savesel.cpMin -= trim;\r
8344     savesel.cpMax -= trim;\r
8345     if (exlen < 0) exlen = 0;\r
8346     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8347     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8348   }\r
8349   /* Append the new text */\r
8350   sel.cpMin = exlen;\r
8351   sel.cpMax = exlen;\r
8352   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8353   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8354   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8355   if (forceVisible || exlen == 0 ||\r
8356       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8357        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8358     /* Scroll to make new end of text visible if old end of text\r
8359        was visible or new text is an echo of user typein */\r
8360     sel.cpMin = 9999999;\r
8361     sel.cpMax = 9999999;\r
8362     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8363     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8364     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8365     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8366   }\r
8367   if (savesel.cpMax == exlen || forceVisible) {\r
8368     /* Move insert point to new end of text if it was at the old\r
8369        end of text or if the new text is an echo of user typein */\r
8370     sel.cpMin = 9999999;\r
8371     sel.cpMax = 9999999;\r
8372     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8373   } else {\r
8374     /* Restore previous selection */\r
8375     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8376   }\r
8377   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8378 }\r
8379 \r
8380 /*---------*/\r
8381 \r
8382 \r
8383 void\r
8384 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8385 {\r
8386   char buf[100];\r
8387   char *str;\r
8388   COLORREF oldFg, oldBg;\r
8389   HFONT oldFont;\r
8390   RECT rect;\r
8391 \r
8392   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8393 \r
8394   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8395   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8396   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8397 \r
8398   rect.left = x;\r
8399   rect.right = x + squareSize;\r
8400   rect.top  = y;\r
8401   rect.bottom = y + squareSize;\r
8402   str = buf;\r
8403 \r
8404   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8405                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8406              y, ETO_CLIPPED|ETO_OPAQUE,\r
8407              &rect, str, strlen(str), NULL);\r
8408 \r
8409   (void) SetTextColor(hdc, oldFg);\r
8410   (void) SetBkColor(hdc, oldBg);\r
8411   (void) SelectObject(hdc, oldFont);\r
8412 }\r
8413 \r
8414 void\r
8415 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8416               RECT *rect, char *color, char *flagFell)\r
8417 {\r
8418   char buf[100];\r
8419   char *str;\r
8420   COLORREF oldFg, oldBg;\r
8421   HFONT oldFont;\r
8422 \r
8423   if (appData.clockMode) {\r
8424     if (tinyLayout)\r
8425       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8426     else\r
8427       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8428     str = buf;\r
8429   } else {\r
8430     str = color;\r
8431   }\r
8432 \r
8433   if (highlight) {\r
8434     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8435     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8436   } else {\r
8437     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8438     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8439   }\r
8440   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8441 \r
8442   JAWS_SILENCE\r
8443 \r
8444   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8445              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8446              rect, str, strlen(str), NULL);\r
8447   if(logoHeight > 0 && appData.clockMode) {\r
8448       RECT r;\r
8449       sprintf(buf, "%s %s", buf+7, flagFell);\r
8450       r.top = rect->top + logoHeight/2;\r
8451       r.left = rect->left;\r
8452       r.right = rect->right;\r
8453       r.bottom = rect->bottom;\r
8454       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8455                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8456                  &r, str, strlen(str), NULL);\r
8457   }\r
8458   (void) SetTextColor(hdc, oldFg);\r
8459   (void) SetBkColor(hdc, oldBg);\r
8460   (void) SelectObject(hdc, oldFont);\r
8461 }\r
8462 \r
8463 \r
8464 int\r
8465 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8466            OVERLAPPED *ovl)\r
8467 {\r
8468   int ok, err;\r
8469 \r
8470   /* [AS]  */\r
8471   if( count <= 0 ) {\r
8472     if (appData.debugMode) {\r
8473       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8474     }\r
8475 \r
8476     return ERROR_INVALID_USER_BUFFER;\r
8477   }\r
8478 \r
8479   ResetEvent(ovl->hEvent);\r
8480   ovl->Offset = ovl->OffsetHigh = 0;\r
8481   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8482   if (ok) {\r
8483     err = NO_ERROR;\r
8484   } else {\r
8485     err = GetLastError();\r
8486     if (err == ERROR_IO_PENDING) {\r
8487       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8488       if (ok)\r
8489         err = NO_ERROR;\r
8490       else\r
8491         err = GetLastError();\r
8492     }\r
8493   }\r
8494   return err;\r
8495 }\r
8496 \r
8497 int\r
8498 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8499             OVERLAPPED *ovl)\r
8500 {\r
8501   int ok, err;\r
8502 \r
8503   ResetEvent(ovl->hEvent);\r
8504   ovl->Offset = ovl->OffsetHigh = 0;\r
8505   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8506   if (ok) {\r
8507     err = NO_ERROR;\r
8508   } else {\r
8509     err = GetLastError();\r
8510     if (err == ERROR_IO_PENDING) {\r
8511       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8512       if (ok)\r
8513         err = NO_ERROR;\r
8514       else\r
8515         err = GetLastError();\r
8516     }\r
8517   }\r
8518   return err;\r
8519 }\r
8520 \r
8521 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8522 void CheckForInputBufferFull( InputSource * is )\r
8523 {\r
8524     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8525         /* Look for end of line */\r
8526         char * p = is->buf;\r
8527         \r
8528         while( p < is->next && *p != '\n' ) {\r
8529             p++;\r
8530         }\r
8531 \r
8532         if( p >= is->next ) {\r
8533             if (appData.debugMode) {\r
8534                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8535             }\r
8536 \r
8537             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8538             is->count = (DWORD) -1;\r
8539             is->next = is->buf;\r
8540         }\r
8541     }\r
8542 }\r
8543 \r
8544 DWORD\r
8545 InputThread(LPVOID arg)\r
8546 {\r
8547   InputSource *is;\r
8548   OVERLAPPED ovl;\r
8549 \r
8550   is = (InputSource *) arg;\r
8551   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8552   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8553   while (is->hThread != NULL) {\r
8554     is->error = DoReadFile(is->hFile, is->next,\r
8555                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8556                            &is->count, &ovl);\r
8557     if (is->error == NO_ERROR) {\r
8558       is->next += is->count;\r
8559     } else {\r
8560       if (is->error == ERROR_BROKEN_PIPE) {\r
8561         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8562         is->count = 0;\r
8563       } else {\r
8564         is->count = (DWORD) -1;\r
8565         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8566         break; \r
8567       }\r
8568     }\r
8569 \r
8570     CheckForInputBufferFull( is );\r
8571 \r
8572     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8573 \r
8574     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8575 \r
8576     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8577   }\r
8578 \r
8579   CloseHandle(ovl.hEvent);\r
8580   CloseHandle(is->hFile);\r
8581 \r
8582   if (appData.debugMode) {\r
8583     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8584   }\r
8585 \r
8586   return 0;\r
8587 }\r
8588 \r
8589 \r
8590 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8591 DWORD\r
8592 NonOvlInputThread(LPVOID arg)\r
8593 {\r
8594   InputSource *is;\r
8595   char *p, *q;\r
8596   int i;\r
8597   char prev;\r
8598 \r
8599   is = (InputSource *) arg;\r
8600   while (is->hThread != NULL) {\r
8601     is->error = ReadFile(is->hFile, is->next,\r
8602                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8603                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8604     if (is->error == NO_ERROR) {\r
8605       /* Change CRLF to LF */\r
8606       if (is->next > is->buf) {\r
8607         p = is->next - 1;\r
8608         i = is->count + 1;\r
8609       } else {\r
8610         p = is->next;\r
8611         i = is->count;\r
8612       }\r
8613       q = p;\r
8614       prev = NULLCHAR;\r
8615       while (i > 0) {\r
8616         if (prev == '\r' && *p == '\n') {\r
8617           *(q-1) = '\n';\r
8618           is->count--;\r
8619         } else { \r
8620           *q++ = *p;\r
8621         }\r
8622         prev = *p++;\r
8623         i--;\r
8624       }\r
8625       *q = NULLCHAR;\r
8626       is->next = q;\r
8627     } else {\r
8628       if (is->error == ERROR_BROKEN_PIPE) {\r
8629         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8630         is->count = 0; \r
8631       } else {\r
8632         is->count = (DWORD) -1;\r
8633       }\r
8634     }\r
8635 \r
8636     CheckForInputBufferFull( is );\r
8637 \r
8638     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8639 \r
8640     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8641 \r
8642     if (is->count < 0) break;  /* Quit on error */\r
8643   }\r
8644   CloseHandle(is->hFile);\r
8645   return 0;\r
8646 }\r
8647 \r
8648 DWORD\r
8649 SocketInputThread(LPVOID arg)\r
8650 {\r
8651   InputSource *is;\r
8652 \r
8653   is = (InputSource *) arg;\r
8654   while (is->hThread != NULL) {\r
8655     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8656     if ((int)is->count == SOCKET_ERROR) {\r
8657       is->count = (DWORD) -1;\r
8658       is->error = WSAGetLastError();\r
8659     } else {\r
8660       is->error = NO_ERROR;\r
8661       is->next += is->count;\r
8662       if (is->count == 0 && is->second == is) {\r
8663         /* End of file on stderr; quit with no message */\r
8664         break;\r
8665       }\r
8666     }\r
8667     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8668 \r
8669     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8670 \r
8671     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8672   }\r
8673   return 0;\r
8674 }\r
8675 \r
8676 VOID\r
8677 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8678 {\r
8679   InputSource *is;\r
8680 \r
8681   is = (InputSource *) lParam;\r
8682   if (is->lineByLine) {\r
8683     /* Feed in lines one by one */\r
8684     char *p = is->buf;\r
8685     char *q = p;\r
8686     while (q < is->next) {\r
8687       if (*q++ == '\n') {\r
8688         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8689         p = q;\r
8690       }\r
8691     }\r
8692     \r
8693     /* Move any partial line to the start of the buffer */\r
8694     q = is->buf;\r
8695     while (p < is->next) {\r
8696       *q++ = *p++;\r
8697     }\r
8698     is->next = q;\r
8699 \r
8700     if (is->error != NO_ERROR || is->count == 0) {\r
8701       /* Notify backend of the error.  Note: If there was a partial\r
8702          line at the end, it is not flushed through. */\r
8703       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8704     }\r
8705   } else {\r
8706     /* Feed in the whole chunk of input at once */\r
8707     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8708     is->next = is->buf;\r
8709   }\r
8710 }\r
8711 \r
8712 /*---------------------------------------------------------------------------*\\r
8713  *\r
8714  *  Menu enables. Used when setting various modes.\r
8715  *\r
8716 \*---------------------------------------------------------------------------*/\r
8717 \r
8718 typedef struct {\r
8719   int item;\r
8720   int flags;\r
8721 } Enables;\r
8722 \r
8723 VOID\r
8724 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8725 {\r
8726   while (enab->item > 0) {\r
8727     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8728     enab++;\r
8729   }\r
8730 }\r
8731 \r
8732 Enables gnuEnables[] = {\r
8733   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8734   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8735   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8736   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8737   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8738   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8739   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8740   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8741   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8742   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8743   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8744   { -1, -1 }\r
8745 };\r
8746 \r
8747 Enables icsEnables[] = {\r
8748   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8749   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8750   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8751   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8752   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8753   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8754   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8755   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8756   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8757   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8758   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8759   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8760   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8761   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8762   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8763   { -1, -1 }\r
8764 };\r
8765 \r
8766 #ifdef ZIPPY\r
8767 Enables zippyEnables[] = {\r
8768   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8769   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8770   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8771   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8772   { -1, -1 }\r
8773 };\r
8774 #endif\r
8775 \r
8776 Enables ncpEnables[] = {\r
8777   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8781   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8782   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8783   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8785   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8786   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8796   { -1, -1 }\r
8797 };\r
8798 \r
8799 Enables trainingOnEnables[] = {\r
8800   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8802   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8803   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8805   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8806   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8807   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8808   { -1, -1 }\r
8809 };\r
8810 \r
8811 Enables trainingOffEnables[] = {\r
8812   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8813   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8814   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8815   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8816   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8817   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8818   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8819   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8820   { -1, -1 }\r
8821 };\r
8822 \r
8823 /* These modify either ncpEnables or gnuEnables */\r
8824 Enables cmailEnables[] = {\r
8825   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8826   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8827   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8828   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8830   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8832   { -1, -1 }\r
8833 };\r
8834 \r
8835 Enables machineThinkingEnables[] = {\r
8836   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8841   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8843   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8844   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8845   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8846   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8847   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8848   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8849   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8850   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8851   { -1, -1 }\r
8852 };\r
8853 \r
8854 Enables userThinkingEnables[] = {\r
8855   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8856   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8857   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8858   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8860   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8861   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8862   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8863   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8864   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8865   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8866   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8867   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8868   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8869   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8870   { -1, -1 }\r
8871 };\r
8872 \r
8873 /*---------------------------------------------------------------------------*\\r
8874  *\r
8875  *  Front-end interface functions exported by XBoard.\r
8876  *  Functions appear in same order as prototypes in frontend.h.\r
8877  * \r
8878 \*---------------------------------------------------------------------------*/\r
8879 VOID\r
8880 ModeHighlight()\r
8881 {\r
8882   static UINT prevChecked = 0;\r
8883   static int prevPausing = 0;\r
8884   UINT nowChecked;\r
8885 \r
8886   if (pausing != prevPausing) {\r
8887     prevPausing = pausing;\r
8888     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8889                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8890     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8891   }\r
8892 \r
8893   switch (gameMode) {\r
8894   case BeginningOfGame:\r
8895     if (appData.icsActive)\r
8896       nowChecked = IDM_IcsClient;\r
8897     else if (appData.noChessProgram)\r
8898       nowChecked = IDM_EditGame;\r
8899     else\r
8900       nowChecked = IDM_MachineBlack;\r
8901     break;\r
8902   case MachinePlaysBlack:\r
8903     nowChecked = IDM_MachineBlack;\r
8904     break;\r
8905   case MachinePlaysWhite:\r
8906     nowChecked = IDM_MachineWhite;\r
8907     break;\r
8908   case TwoMachinesPlay:\r
8909     nowChecked = IDM_TwoMachines;\r
8910     break;\r
8911   case AnalyzeMode:\r
8912     nowChecked = IDM_AnalysisMode;\r
8913     break;\r
8914   case AnalyzeFile:\r
8915     nowChecked = IDM_AnalyzeFile;\r
8916     break;\r
8917   case EditGame:\r
8918     nowChecked = IDM_EditGame;\r
8919     break;\r
8920   case PlayFromGameFile:\r
8921     nowChecked = IDM_LoadGame;\r
8922     break;\r
8923   case EditPosition:\r
8924     nowChecked = IDM_EditPosition;\r
8925     break;\r
8926   case Training:\r
8927     nowChecked = IDM_Training;\r
8928     break;\r
8929   case IcsPlayingWhite:\r
8930   case IcsPlayingBlack:\r
8931   case IcsObserving:\r
8932   case IcsIdle:\r
8933     nowChecked = IDM_IcsClient;\r
8934     break;\r
8935   default:\r
8936   case EndOfGame:\r
8937     nowChecked = 0;\r
8938     break;\r
8939   }\r
8940   if (prevChecked != 0)\r
8941     (void) CheckMenuItem(GetMenu(hwndMain),\r
8942                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8943   if (nowChecked != 0)\r
8944     (void) CheckMenuItem(GetMenu(hwndMain),\r
8945                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8946 \r
8947   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8948     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8949                           MF_BYCOMMAND|MF_ENABLED);\r
8950   } else {\r
8951     (void) EnableMenuItem(GetMenu(hwndMain), \r
8952                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8953   }\r
8954 \r
8955   prevChecked = nowChecked;\r
8956 \r
8957   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8958   if (appData.icsActive) {\r
8959        if (appData.icsEngineAnalyze) {\r
8960                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8961                        MF_BYCOMMAND|MF_CHECKED);\r
8962        } else {\r
8963                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8964                        MF_BYCOMMAND|MF_UNCHECKED);\r
8965        }\r
8966   }\r
8967 }\r
8968 \r
8969 VOID\r
8970 SetICSMode()\r
8971 {\r
8972   HMENU hmenu = GetMenu(hwndMain);\r
8973   SetMenuEnables(hmenu, icsEnables);\r
8974   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8975     MF_BYPOSITION|MF_ENABLED);\r
8976 #ifdef ZIPPY\r
8977   if (appData.zippyPlay) {\r
8978     SetMenuEnables(hmenu, zippyEnables);\r
8979     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8980          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8981           MF_BYCOMMAND|MF_ENABLED);\r
8982   }\r
8983 #endif\r
8984 }\r
8985 \r
8986 VOID\r
8987 SetGNUMode()\r
8988 {\r
8989   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8990 }\r
8991 \r
8992 VOID\r
8993 SetNCPMode()\r
8994 {\r
8995   HMENU hmenu = GetMenu(hwndMain);\r
8996   SetMenuEnables(hmenu, ncpEnables);\r
8997   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8998     MF_BYPOSITION|MF_GRAYED);\r
8999     DrawMenuBar(hwndMain);\r
9000 }\r
9001 \r
9002 VOID\r
9003 SetCmailMode()\r
9004 {\r
9005   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9006 }\r
9007 \r
9008 VOID \r
9009 SetTrainingModeOn()\r
9010 {\r
9011   int i;\r
9012   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9013   for (i = 0; i < N_BUTTONS; i++) {\r
9014     if (buttonDesc[i].hwnd != NULL)\r
9015       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9016   }\r
9017   CommentPopDown();\r
9018 }\r
9019 \r
9020 VOID SetTrainingModeOff()\r
9021 {\r
9022   int i;\r
9023   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9024   for (i = 0; i < N_BUTTONS; i++) {\r
9025     if (buttonDesc[i].hwnd != NULL)\r
9026       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9027   }\r
9028 }\r
9029 \r
9030 \r
9031 VOID\r
9032 SetUserThinkingEnables()\r
9033 {\r
9034   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9035 }\r
9036 \r
9037 VOID\r
9038 SetMachineThinkingEnables()\r
9039 {\r
9040   HMENU hMenu = GetMenu(hwndMain);\r
9041   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9042 \r
9043   SetMenuEnables(hMenu, machineThinkingEnables);\r
9044 \r
9045   if (gameMode == MachinePlaysBlack) {\r
9046     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9047   } else if (gameMode == MachinePlaysWhite) {\r
9048     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9049   } else if (gameMode == TwoMachinesPlay) {\r
9050     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9051   }\r
9052 }\r
9053 \r
9054 \r
9055 VOID\r
9056 DisplayTitle(char *str)\r
9057 {\r
9058   char title[MSG_SIZ], *host;\r
9059   if (str[0] != NULLCHAR) {\r
9060     strcpy(title, str);\r
9061   } else if (appData.icsActive) {\r
9062     if (appData.icsCommPort[0] != NULLCHAR)\r
9063       host = "ICS";\r
9064     else \r
9065       host = appData.icsHost;\r
9066     sprintf(title, "%s: %s", szTitle, host);\r
9067   } else if (appData.noChessProgram) {\r
9068     strcpy(title, szTitle);\r
9069   } else {\r
9070     strcpy(title, szTitle);\r
9071     strcat(title, ": ");\r
9072     strcat(title, first.tidy);\r
9073   }\r
9074   SetWindowText(hwndMain, title);\r
9075 }\r
9076 \r
9077 \r
9078 VOID\r
9079 DisplayMessage(char *str1, char *str2)\r
9080 {\r
9081   HDC hdc;\r
9082   HFONT oldFont;\r
9083   int remain = MESSAGE_TEXT_MAX - 1;\r
9084   int len;\r
9085 \r
9086   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9087   messageText[0] = NULLCHAR;\r
9088   if (*str1) {\r
9089     len = strlen(str1);\r
9090     if (len > remain) len = remain;\r
9091     strncpy(messageText, str1, len);\r
9092     messageText[len] = NULLCHAR;\r
9093     remain -= len;\r
9094   }\r
9095   if (*str2 && remain >= 2) {\r
9096     if (*str1) {\r
9097       strcat(messageText, "  ");\r
9098       remain -= 2;\r
9099     }\r
9100     len = strlen(str2);\r
9101     if (len > remain) len = remain;\r
9102     strncat(messageText, str2, len);\r
9103   }\r
9104   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9105 \r
9106   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9107 \r
9108   SAYMACHINEMOVE();\r
9109 \r
9110   hdc = GetDC(hwndMain);\r
9111   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9112   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9113              &messageRect, messageText, strlen(messageText), NULL);\r
9114   (void) SelectObject(hdc, oldFont);\r
9115   (void) ReleaseDC(hwndMain, hdc);\r
9116 }\r
9117 \r
9118 VOID\r
9119 DisplayError(char *str, int error)\r
9120 {\r
9121   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9122   int len;\r
9123 \r
9124   if (error == 0) {\r
9125     strcpy(buf, str);\r
9126   } else {\r
9127     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9128                         NULL, error, LANG_NEUTRAL,\r
9129                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9130     if (len > 0) {\r
9131       sprintf(buf, "%s:\n%s", str, buf2);\r
9132     } else {\r
9133       ErrorMap *em = errmap;\r
9134       while (em->err != 0 && em->err != error) em++;\r
9135       if (em->err != 0) {\r
9136         sprintf(buf, "%s:\n%s", str, em->msg);\r
9137       } else {\r
9138         sprintf(buf, "%s:\nError code %d", str, error);\r
9139       }\r
9140     }\r
9141   }\r
9142   \r
9143   ErrorPopUp("Error", buf);\r
9144 }\r
9145 \r
9146 \r
9147 VOID\r
9148 DisplayMoveError(char *str)\r
9149 {\r
9150   fromX = fromY = -1;\r
9151   ClearHighlights();\r
9152   DrawPosition(FALSE, NULL);\r
9153   if (appData.popupMoveErrors) {\r
9154     ErrorPopUp("Error", str);\r
9155   } else {\r
9156     DisplayMessage(str, "");\r
9157     moveErrorMessageUp = TRUE;\r
9158   }\r
9159 }\r
9160 \r
9161 VOID\r
9162 DisplayFatalError(char *str, int error, int exitStatus)\r
9163 {\r
9164   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9165   int len;\r
9166   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9167 \r
9168   if (error != 0) {\r
9169     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9170                         NULL, error, LANG_NEUTRAL,\r
9171                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9172     if (len > 0) {\r
9173       sprintf(buf, "%s:\n%s", str, buf2);\r
9174     } else {\r
9175       ErrorMap *em = errmap;\r
9176       while (em->err != 0 && em->err != error) em++;\r
9177       if (em->err != 0) {\r
9178         sprintf(buf, "%s:\n%s", str, em->msg);\r
9179       } else {\r
9180         sprintf(buf, "%s:\nError code %d", str, error);\r
9181       }\r
9182     }\r
9183     str = buf;\r
9184   }\r
9185   if (appData.debugMode) {\r
9186     fprintf(debugFP, "%s: %s\n", label, str);\r
9187   }\r
9188   if (appData.popupExitMessage) {\r
9189     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9190                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9191   }\r
9192   ExitEvent(exitStatus);\r
9193 }\r
9194 \r
9195 \r
9196 VOID\r
9197 DisplayInformation(char *str)\r
9198 {\r
9199   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9200 }\r
9201 \r
9202 \r
9203 VOID\r
9204 DisplayNote(char *str)\r
9205 {\r
9206   ErrorPopUp("Note", str);\r
9207 }\r
9208 \r
9209 \r
9210 typedef struct {\r
9211   char *title, *question, *replyPrefix;\r
9212   ProcRef pr;\r
9213 } QuestionParams;\r
9214 \r
9215 LRESULT CALLBACK\r
9216 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9217 {\r
9218   static QuestionParams *qp;\r
9219   char reply[MSG_SIZ];\r
9220   int len, err;\r
9221 \r
9222   switch (message) {\r
9223   case WM_INITDIALOG:\r
9224     qp = (QuestionParams *) lParam;\r
9225     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9226     SetWindowText(hDlg, qp->title);\r
9227     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9228     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9229     return FALSE;\r
9230 \r
9231   case WM_COMMAND:\r
9232     switch (LOWORD(wParam)) {\r
9233     case IDOK:\r
9234       strcpy(reply, qp->replyPrefix);\r
9235       if (*reply) strcat(reply, " ");\r
9236       len = strlen(reply);\r
9237       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9238       strcat(reply, "\n");\r
9239       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9240       EndDialog(hDlg, TRUE);\r
9241       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9242       return TRUE;\r
9243     case IDCANCEL:\r
9244       EndDialog(hDlg, FALSE);\r
9245       return TRUE;\r
9246     default:\r
9247       break;\r
9248     }\r
9249     break;\r
9250   }\r
9251   return FALSE;\r
9252 }\r
9253 \r
9254 VOID\r
9255 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9256 {\r
9257     QuestionParams qp;\r
9258     FARPROC lpProc;\r
9259     \r
9260     qp.title = title;\r
9261     qp.question = question;\r
9262     qp.replyPrefix = replyPrefix;\r
9263     qp.pr = pr;\r
9264     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9265     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9266       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9267     FreeProcInstance(lpProc);\r
9268 }\r
9269 \r
9270 /* [AS] Pick FRC position */\r
9271 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9272 {\r
9273     static int * lpIndexFRC;\r
9274     BOOL index_is_ok;\r
9275     char buf[16];\r
9276 \r
9277     switch( message )\r
9278     {\r
9279     case WM_INITDIALOG:\r
9280         lpIndexFRC = (int *) lParam;\r
9281 \r
9282         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9283 \r
9284         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9285         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9286         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9287         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9288 \r
9289         break;\r
9290 \r
9291     case WM_COMMAND:\r
9292         switch( LOWORD(wParam) ) {\r
9293         case IDOK:\r
9294             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9295             EndDialog( hDlg, 0 );\r
9296             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9297             return TRUE;\r
9298         case IDCANCEL:\r
9299             EndDialog( hDlg, 1 );   \r
9300             return TRUE;\r
9301         case IDC_NFG_Edit:\r
9302             if( HIWORD(wParam) == EN_CHANGE ) {\r
9303                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9304 \r
9305                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9306             }\r
9307             return TRUE;\r
9308         case IDC_NFG_Random:\r
9309             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9310             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9311             return TRUE;\r
9312         }\r
9313 \r
9314         break;\r
9315     }\r
9316 \r
9317     return FALSE;\r
9318 }\r
9319 \r
9320 int NewGameFRC()\r
9321 {\r
9322     int result;\r
9323     int index = appData.defaultFrcPosition;\r
9324     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9325 \r
9326     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9327 \r
9328     if( result == 0 ) {\r
9329         appData.defaultFrcPosition = index;\r
9330     }\r
9331 \r
9332     return result;\r
9333 }\r
9334 \r
9335 /* [AS] Game list options */\r
9336 typedef struct {\r
9337     char id;\r
9338     char * name;\r
9339 } GLT_Item;\r
9340 \r
9341 static GLT_Item GLT_ItemInfo[] = {\r
9342     { GLT_EVENT,      "Event" },\r
9343     { GLT_SITE,       "Site" },\r
9344     { GLT_DATE,       "Date" },\r
9345     { GLT_ROUND,      "Round" },\r
9346     { GLT_PLAYERS,    "Players" },\r
9347     { GLT_RESULT,     "Result" },\r
9348     { GLT_WHITE_ELO,  "White Rating" },\r
9349     { GLT_BLACK_ELO,  "Black Rating" },\r
9350     { GLT_TIME_CONTROL,"Time Control" },\r
9351     { GLT_VARIANT,    "Variant" },\r
9352     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9353     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9354     { 0, 0 }\r
9355 };\r
9356 \r
9357 const char * GLT_FindItem( char id )\r
9358 {\r
9359     const char * result = 0;\r
9360 \r
9361     GLT_Item * list = GLT_ItemInfo;\r
9362 \r
9363     while( list->id != 0 ) {\r
9364         if( list->id == id ) {\r
9365             result = list->name;\r
9366             break;\r
9367         }\r
9368 \r
9369         list++;\r
9370     }\r
9371 \r
9372     return result;\r
9373 }\r
9374 \r
9375 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9376 {\r
9377     const char * name = GLT_FindItem( id );\r
9378 \r
9379     if( name != 0 ) {\r
9380         if( index >= 0 ) {\r
9381             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9382         }\r
9383         else {\r
9384             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9385         }\r
9386     }\r
9387 }\r
9388 \r
9389 void GLT_TagsToList( HWND hDlg, char * tags )\r
9390 {\r
9391     char * pc = tags;\r
9392 \r
9393     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9394 \r
9395     while( *pc ) {\r
9396         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9397         pc++;\r
9398     }\r
9399 \r
9400     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9401 \r
9402     pc = GLT_ALL_TAGS;\r
9403 \r
9404     while( *pc ) {\r
9405         if( strchr( tags, *pc ) == 0 ) {\r
9406             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9407         }\r
9408         pc++;\r
9409     }\r
9410 \r
9411     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9412 }\r
9413 \r
9414 char GLT_ListItemToTag( HWND hDlg, int index )\r
9415 {\r
9416     char result = '\0';\r
9417     char name[128];\r
9418 \r
9419     GLT_Item * list = GLT_ItemInfo;\r
9420 \r
9421     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9422         while( list->id != 0 ) {\r
9423             if( strcmp( list->name, name ) == 0 ) {\r
9424                 result = list->id;\r
9425                 break;\r
9426             }\r
9427 \r
9428             list++;\r
9429         }\r
9430     }\r
9431 \r
9432     return result;\r
9433 }\r
9434 \r
9435 void GLT_MoveSelection( HWND hDlg, int delta )\r
9436 {\r
9437     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9438     int idx2 = idx1 + delta;\r
9439     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9440 \r
9441     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9442         char buf[128];\r
9443 \r
9444         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9445         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9446         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9447         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9448     }\r
9449 }\r
9450 \r
9451 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9452 {\r
9453     static char glt[64];\r
9454     static char * lpUserGLT;\r
9455 \r
9456     switch( message )\r
9457     {\r
9458     case WM_INITDIALOG:\r
9459         lpUserGLT = (char *) lParam;\r
9460         \r
9461         strcpy( glt, lpUserGLT );\r
9462 \r
9463         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9464 \r
9465         /* Initialize list */\r
9466         GLT_TagsToList( hDlg, glt );\r
9467 \r
9468         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9469 \r
9470         break;\r
9471 \r
9472     case WM_COMMAND:\r
9473         switch( LOWORD(wParam) ) {\r
9474         case IDOK:\r
9475             {\r
9476                 char * pc = lpUserGLT;\r
9477                 int idx = 0;\r
9478 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9479                 char id;\r
9480 \r
9481                 do {\r
9482                     id = GLT_ListItemToTag( hDlg, idx );\r
9483 \r
9484                     *pc++ = id;\r
9485                     idx++;\r
9486                 } while( id != '\0' );\r
9487             }\r
9488             EndDialog( hDlg, 0 );\r
9489             return TRUE;\r
9490         case IDCANCEL:\r
9491             EndDialog( hDlg, 1 );\r
9492             return TRUE;\r
9493 \r
9494         case IDC_GLT_Default:\r
9495             strcpy( glt, GLT_DEFAULT_TAGS );\r
9496             GLT_TagsToList( hDlg, glt );\r
9497             return TRUE;\r
9498 \r
9499         case IDC_GLT_Restore:\r
9500             strcpy( glt, lpUserGLT );\r
9501             GLT_TagsToList( hDlg, glt );\r
9502             return TRUE;\r
9503 \r
9504         case IDC_GLT_Up:\r
9505             GLT_MoveSelection( hDlg, -1 );\r
9506             return TRUE;\r
9507 \r
9508         case IDC_GLT_Down:\r
9509             GLT_MoveSelection( hDlg, +1 );\r
9510             return TRUE;\r
9511         }\r
9512 \r
9513         break;\r
9514     }\r
9515 \r
9516     return FALSE;\r
9517 }\r
9518 \r
9519 int GameListOptions()\r
9520 {\r
9521     char glt[64];\r
9522     int result;\r
9523     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9524 \r
9525     strcpy( glt, appData.gameListTags );\r
9526 \r
9527     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9528 \r
9529     if( result == 0 ) {\r
9530         /* [AS] Memory leak here! */\r
9531         appData.gameListTags = strdup( glt ); \r
9532     }\r
9533 \r
9534     return result;\r
9535 }\r
9536 \r
9537 \r
9538 VOID\r
9539 DisplayIcsInteractionTitle(char *str)\r
9540 {\r
9541   char consoleTitle[MSG_SIZ];\r
9542 \r
9543   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9544   SetWindowText(hwndConsole, consoleTitle);\r
9545 }\r
9546 \r
9547 void\r
9548 DrawPosition(int fullRedraw, Board board)\r
9549 {\r
9550   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9551 }\r
9552 \r
9553 void NotifyFrontendLogin()\r
9554 {\r
9555         if (hwndConsole)\r
9556                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9557 }\r
9558 \r
9559 VOID\r
9560 ResetFrontEnd()\r
9561 {\r
9562   fromX = fromY = -1;\r
9563   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9564     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9565     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9566     dragInfo.lastpos = dragInfo.pos;\r
9567     dragInfo.start.x = dragInfo.start.y = -1;\r
9568     dragInfo.from = dragInfo.start;\r
9569     ReleaseCapture();\r
9570     DrawPosition(TRUE, NULL);\r
9571   }\r
9572 }\r
9573 \r
9574 \r
9575 VOID\r
9576 CommentPopUp(char *title, char *str)\r
9577 {\r
9578   HWND hwnd = GetActiveWindow();\r
9579   EitherCommentPopUp(0, title, str, FALSE);\r
9580   SAY(str);\r
9581   SetActiveWindow(hwnd);\r
9582 }\r
9583 \r
9584 VOID\r
9585 CommentPopDown(void)\r
9586 {\r
9587   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9588   if (commentDialog) {\r
9589     ShowWindow(commentDialog, SW_HIDE);\r
9590   }\r
9591   commentDialogUp = FALSE;\r
9592 }\r
9593 \r
9594 VOID\r
9595 EditCommentPopUp(int index, char *title, char *str)\r
9596 {\r
9597   EitherCommentPopUp(index, title, str, TRUE);\r
9598 }\r
9599 \r
9600 \r
9601 VOID\r
9602 RingBell()\r
9603 {\r
9604   MyPlaySound(&sounds[(int)SoundMove]);\r
9605 }\r
9606 \r
9607 VOID PlayIcsWinSound()\r
9608 {\r
9609   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9610 }\r
9611 \r
9612 VOID PlayIcsLossSound()\r
9613 {\r
9614   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9615 }\r
9616 \r
9617 VOID PlayIcsDrawSound()\r
9618 {\r
9619   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9620 }\r
9621 \r
9622 VOID PlayIcsUnfinishedSound()\r
9623 {\r
9624   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9625 }\r
9626 \r
9627 VOID\r
9628 PlayAlarmSound()\r
9629 {\r
9630   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9631 }\r
9632 \r
9633 \r
9634 VOID\r
9635 EchoOn()\r
9636 {\r
9637   HWND hInput;\r
9638   consoleEcho = TRUE;\r
9639   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9640   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9641   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9642 }\r
9643 \r
9644 \r
9645 VOID\r
9646 EchoOff()\r
9647 {\r
9648   CHARFORMAT cf;\r
9649   HWND hInput;\r
9650   consoleEcho = FALSE;\r
9651   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9652   /* This works OK: set text and background both to the same color */\r
9653   cf = consoleCF;\r
9654   cf.crTextColor = COLOR_ECHOOFF;\r
9655   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9656   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9657 }\r
9658 \r
9659 /* No Raw()...? */\r
9660 \r
9661 void Colorize(ColorClass cc, int continuation)\r
9662 {\r
9663   currentColorClass = cc;\r
9664   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9665   consoleCF.crTextColor = textAttribs[cc].color;\r
9666   consoleCF.dwEffects = textAttribs[cc].effects;\r
9667   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9668 }\r
9669 \r
9670 char *\r
9671 UserName()\r
9672 {\r
9673   static char buf[MSG_SIZ];\r
9674   DWORD bufsiz = MSG_SIZ;\r
9675 \r
9676   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9677         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9678   }\r
9679   if (!GetUserName(buf, &bufsiz)) {\r
9680     /*DisplayError("Error getting user name", GetLastError());*/\r
9681     strcpy(buf, "User");\r
9682   }\r
9683   return buf;\r
9684 }\r
9685 \r
9686 char *\r
9687 HostName()\r
9688 {\r
9689   static char buf[MSG_SIZ];\r
9690   DWORD bufsiz = MSG_SIZ;\r
9691 \r
9692   if (!GetComputerName(buf, &bufsiz)) {\r
9693     /*DisplayError("Error getting host name", GetLastError());*/\r
9694     strcpy(buf, "Unknown");\r
9695   }\r
9696   return buf;\r
9697 }\r
9698 \r
9699 \r
9700 int\r
9701 ClockTimerRunning()\r
9702 {\r
9703   return clockTimerEvent != 0;\r
9704 }\r
9705 \r
9706 int\r
9707 StopClockTimer()\r
9708 {\r
9709   if (clockTimerEvent == 0) return FALSE;\r
9710   KillTimer(hwndMain, clockTimerEvent);\r
9711   clockTimerEvent = 0;\r
9712   return TRUE;\r
9713 }\r
9714 \r
9715 void\r
9716 StartClockTimer(long millisec)\r
9717 {\r
9718   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9719                              (UINT) millisec, NULL);\r
9720 }\r
9721 \r
9722 void\r
9723 DisplayWhiteClock(long timeRemaining, int highlight)\r
9724 {\r
9725   HDC hdc;\r
9726   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9727 \r
9728   if(appData.noGUI) return;\r
9729   hdc = GetDC(hwndMain);\r
9730   if (!IsIconic(hwndMain)) {\r
9731     DisplayAClock(hdc, timeRemaining, highlight, \r
9732                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9733   }\r
9734   if (highlight && iconCurrent == iconBlack) {\r
9735     iconCurrent = iconWhite;\r
9736     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9737     if (IsIconic(hwndMain)) {\r
9738       DrawIcon(hdc, 2, 2, iconCurrent);\r
9739     }\r
9740   }\r
9741   (void) ReleaseDC(hwndMain, hdc);\r
9742   if (hwndConsole)\r
9743     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9744 }\r
9745 \r
9746 void\r
9747 DisplayBlackClock(long timeRemaining, int highlight)\r
9748 {\r
9749   HDC hdc;\r
9750   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9751 \r
9752   if(appData.noGUI) return;\r
9753   hdc = GetDC(hwndMain);\r
9754   if (!IsIconic(hwndMain)) {\r
9755     DisplayAClock(hdc, timeRemaining, highlight, \r
9756                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9757   }\r
9758   if (highlight && iconCurrent == iconWhite) {\r
9759     iconCurrent = iconBlack;\r
9760     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9761     if (IsIconic(hwndMain)) {\r
9762       DrawIcon(hdc, 2, 2, iconCurrent);\r
9763     }\r
9764   }\r
9765   (void) ReleaseDC(hwndMain, hdc);\r
9766   if (hwndConsole)\r
9767     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9768 }\r
9769 \r
9770 \r
9771 int\r
9772 LoadGameTimerRunning()\r
9773 {\r
9774   return loadGameTimerEvent != 0;\r
9775 }\r
9776 \r
9777 int\r
9778 StopLoadGameTimer()\r
9779 {\r
9780   if (loadGameTimerEvent == 0) return FALSE;\r
9781   KillTimer(hwndMain, loadGameTimerEvent);\r
9782   loadGameTimerEvent = 0;\r
9783   return TRUE;\r
9784 }\r
9785 \r
9786 void\r
9787 StartLoadGameTimer(long millisec)\r
9788 {\r
9789   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9790                                 (UINT) millisec, NULL);\r
9791 }\r
9792 \r
9793 void\r
9794 AutoSaveGame()\r
9795 {\r
9796   char *defName;\r
9797   FILE *f;\r
9798   char fileTitle[MSG_SIZ];\r
9799 \r
9800   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9801   f = OpenFileDialog(hwndMain, "a", defName,\r
9802                      appData.oldSaveStyle ? "gam" : "pgn",\r
9803                      GAME_FILT, \r
9804                      "Save Game to File", NULL, fileTitle, NULL);\r
9805   if (f != NULL) {\r
9806     SaveGame(f, 0, "");\r
9807     fclose(f);\r
9808   }\r
9809 }\r
9810 \r
9811 \r
9812 void\r
9813 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9814 {\r
9815   if (delayedTimerEvent != 0) {\r
9816     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9817       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9818     }\r
9819     KillTimer(hwndMain, delayedTimerEvent);\r
9820     delayedTimerEvent = 0;\r
9821     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9822     delayedTimerCallback();\r
9823   }\r
9824   delayedTimerCallback = cb;\r
9825   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9826                                 (UINT) millisec, NULL);\r
9827 }\r
9828 \r
9829 DelayedEventCallback\r
9830 GetDelayedEvent()\r
9831 {\r
9832   if (delayedTimerEvent) {\r
9833     return delayedTimerCallback;\r
9834   } else {\r
9835     return NULL;\r
9836   }\r
9837 }\r
9838 \r
9839 void\r
9840 CancelDelayedEvent()\r
9841 {\r
9842   if (delayedTimerEvent) {\r
9843     KillTimer(hwndMain, delayedTimerEvent);\r
9844     delayedTimerEvent = 0;\r
9845   }\r
9846 }\r
9847 \r
9848 DWORD GetWin32Priority(int nice)\r
9849 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9850 /*\r
9851 REALTIME_PRIORITY_CLASS     0x00000100\r
9852 HIGH_PRIORITY_CLASS         0x00000080\r
9853 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9854 NORMAL_PRIORITY_CLASS       0x00000020\r
9855 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9856 IDLE_PRIORITY_CLASS         0x00000040\r
9857 */\r
9858         if (nice < -15) return 0x00000080;\r
9859         if (nice < 0)   return 0x00008000;\r
9860         if (nice == 0)  return 0x00000020;\r
9861         if (nice < 15)  return 0x00004000;\r
9862         return 0x00000040;\r
9863 }\r
9864 \r
9865 /* Start a child process running the given program.\r
9866    The process's standard output can be read from "from", and its\r
9867    standard input can be written to "to".\r
9868    Exit with fatal error if anything goes wrong.\r
9869    Returns an opaque pointer that can be used to destroy the process\r
9870    later.\r
9871 */\r
9872 int\r
9873 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9874 {\r
9875 #define BUFSIZE 4096\r
9876 \r
9877   HANDLE hChildStdinRd, hChildStdinWr,\r
9878     hChildStdoutRd, hChildStdoutWr;\r
9879   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9880   SECURITY_ATTRIBUTES saAttr;\r
9881   BOOL fSuccess;\r
9882   PROCESS_INFORMATION piProcInfo;\r
9883   STARTUPINFO siStartInfo;\r
9884   ChildProc *cp;\r
9885   char buf[MSG_SIZ];\r
9886   DWORD err;\r
9887 \r
9888   if (appData.debugMode) {\r
9889     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9890   }\r
9891 \r
9892   *pr = NoProc;\r
9893 \r
9894   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9895   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9896   saAttr.bInheritHandle = TRUE;\r
9897   saAttr.lpSecurityDescriptor = NULL;\r
9898 \r
9899   /*\r
9900    * The steps for redirecting child's STDOUT:\r
9901    *     1. Create anonymous pipe to be STDOUT for child.\r
9902    *     2. Create a noninheritable duplicate of read handle,\r
9903    *         and close the inheritable read handle.\r
9904    */\r
9905 \r
9906   /* Create a pipe for the child's STDOUT. */\r
9907   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9908     return GetLastError();\r
9909   }\r
9910 \r
9911   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9912   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9913                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9914                              FALSE,     /* not inherited */\r
9915                              DUPLICATE_SAME_ACCESS);\r
9916   if (! fSuccess) {\r
9917     return GetLastError();\r
9918   }\r
9919   CloseHandle(hChildStdoutRd);\r
9920 \r
9921   /*\r
9922    * The steps for redirecting child's STDIN:\r
9923    *     1. Create anonymous pipe to be STDIN for child.\r
9924    *     2. Create a noninheritable duplicate of write handle,\r
9925    *         and close the inheritable write handle.\r
9926    */\r
9927 \r
9928   /* Create a pipe for the child's STDIN. */\r
9929   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9930     return GetLastError();\r
9931   }\r
9932 \r
9933   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9934   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9935                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9936                              FALSE,     /* not inherited */\r
9937                              DUPLICATE_SAME_ACCESS);\r
9938   if (! fSuccess) {\r
9939     return GetLastError();\r
9940   }\r
9941   CloseHandle(hChildStdinWr);\r
9942 \r
9943   /* Arrange to (1) look in dir for the child .exe file, and\r
9944    * (2) have dir be the child's working directory.  Interpret\r
9945    * dir relative to the directory WinBoard loaded from. */\r
9946   GetCurrentDirectory(MSG_SIZ, buf);\r
9947   SetCurrentDirectory(installDir);\r
9948   SetCurrentDirectory(dir);\r
9949 \r
9950   /* Now create the child process. */\r
9951 \r
9952   siStartInfo.cb = sizeof(STARTUPINFO);\r
9953   siStartInfo.lpReserved = NULL;\r
9954   siStartInfo.lpDesktop = NULL;\r
9955   siStartInfo.lpTitle = NULL;\r
9956   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9957   siStartInfo.cbReserved2 = 0;\r
9958   siStartInfo.lpReserved2 = NULL;\r
9959   siStartInfo.hStdInput = hChildStdinRd;\r
9960   siStartInfo.hStdOutput = hChildStdoutWr;\r
9961   siStartInfo.hStdError = hChildStdoutWr;\r
9962 \r
9963   fSuccess = CreateProcess(NULL,\r
9964                            cmdLine,        /* command line */\r
9965                            NULL,           /* process security attributes */\r
9966                            NULL,           /* primary thread security attrs */\r
9967                            TRUE,           /* handles are inherited */\r
9968                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9969                            NULL,           /* use parent's environment */\r
9970                            NULL,\r
9971                            &siStartInfo, /* STARTUPINFO pointer */\r
9972                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9973 \r
9974   err = GetLastError();\r
9975   SetCurrentDirectory(buf); /* return to prev directory */\r
9976   if (! fSuccess) {\r
9977     return err;\r
9978   }\r
9979 \r
9980   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9981     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9982     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9983   }\r
9984 \r
9985   /* Close the handles we don't need in the parent */\r
9986   CloseHandle(piProcInfo.hThread);\r
9987   CloseHandle(hChildStdinRd);\r
9988   CloseHandle(hChildStdoutWr);\r
9989 \r
9990   /* Prepare return value */\r
9991   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9992   cp->kind = CPReal;\r
9993   cp->hProcess = piProcInfo.hProcess;\r
9994   cp->pid = piProcInfo.dwProcessId;\r
9995   cp->hFrom = hChildStdoutRdDup;\r
9996   cp->hTo = hChildStdinWrDup;\r
9997 \r
9998   *pr = (void *) cp;\r
9999 \r
10000   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10001      2000 where engines sometimes don't see the initial command(s)\r
10002      from WinBoard and hang.  I don't understand how that can happen,\r
10003      but the Sleep is harmless, so I've put it in.  Others have also\r
10004      reported what may be the same problem, so hopefully this will fix\r
10005      it for them too.  */\r
10006   Sleep(500);\r
10007 \r
10008   return NO_ERROR;\r
10009 }\r
10010 \r
10011 \r
10012 void\r
10013 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10014 {\r
10015   ChildProc *cp; int result;\r
10016 \r
10017   cp = (ChildProc *) pr;\r
10018   if (cp == NULL) return;\r
10019 \r
10020   switch (cp->kind) {\r
10021   case CPReal:\r
10022     /* TerminateProcess is considered harmful, so... */\r
10023     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10024     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10025     /* The following doesn't work because the chess program\r
10026        doesn't "have the same console" as WinBoard.  Maybe\r
10027        we could arrange for this even though neither WinBoard\r
10028        nor the chess program uses a console for stdio? */\r
10029     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10030 \r
10031     /* [AS] Special termination modes for misbehaving programs... */\r
10032     if( signal == 9 ) { \r
10033         result = TerminateProcess( cp->hProcess, 0 );\r
10034 \r
10035         if ( appData.debugMode) {\r
10036             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10037         }\r
10038     }\r
10039     else if( signal == 10 ) {\r
10040         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10041 \r
10042         if( dw != WAIT_OBJECT_0 ) {\r
10043             result = TerminateProcess( cp->hProcess, 0 );\r
10044 \r
10045             if ( appData.debugMode) {\r
10046                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10047             }\r
10048 \r
10049         }\r
10050     }\r
10051 \r
10052     CloseHandle(cp->hProcess);\r
10053     break;\r
10054 \r
10055   case CPComm:\r
10056     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10057     break;\r
10058 \r
10059   case CPSock:\r
10060     closesocket(cp->sock);\r
10061     WSACleanup();\r
10062     break;\r
10063 \r
10064   case CPRcmd:\r
10065     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10066     closesocket(cp->sock);\r
10067     closesocket(cp->sock2);\r
10068     WSACleanup();\r
10069     break;\r
10070   }\r
10071   free(cp);\r
10072 }\r
10073 \r
10074 void\r
10075 InterruptChildProcess(ProcRef pr)\r
10076 {\r
10077   ChildProc *cp;\r
10078 \r
10079   cp = (ChildProc *) pr;\r
10080   if (cp == NULL) return;\r
10081   switch (cp->kind) {\r
10082   case CPReal:\r
10083     /* The following doesn't work because the chess program\r
10084        doesn't "have the same console" as WinBoard.  Maybe\r
10085        we could arrange for this even though neither WinBoard\r
10086        nor the chess program uses a console for stdio */\r
10087     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10088     break;\r
10089 \r
10090   case CPComm:\r
10091   case CPSock:\r
10092     /* Can't interrupt */\r
10093     break;\r
10094 \r
10095   case CPRcmd:\r
10096     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10097     break;\r
10098   }\r
10099 }\r
10100 \r
10101 \r
10102 int\r
10103 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10104 {\r
10105   char cmdLine[MSG_SIZ];\r
10106 \r
10107   if (port[0] == NULLCHAR) {\r
10108     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10109   } else {\r
10110     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10111   }\r
10112   return StartChildProcess(cmdLine, "", pr);\r
10113 }\r
10114 \r
10115 \r
10116 /* Code to open TCP sockets */\r
10117 \r
10118 int\r
10119 OpenTCP(char *host, char *port, ProcRef *pr)\r
10120 {\r
10121   ChildProc *cp;\r
10122   int err;\r
10123   SOCKET s;\r
10124   struct sockaddr_in sa, mysa;\r
10125   struct hostent FAR *hp;\r
10126   unsigned short uport;\r
10127   WORD wVersionRequested;\r
10128   WSADATA wsaData;\r
10129 \r
10130   /* Initialize socket DLL */\r
10131   wVersionRequested = MAKEWORD(1, 1);\r
10132   err = WSAStartup(wVersionRequested, &wsaData);\r
10133   if (err != 0) return err;\r
10134 \r
10135   /* Make socket */\r
10136   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10137     err = WSAGetLastError();\r
10138     WSACleanup();\r
10139     return err;\r
10140   }\r
10141 \r
10142   /* Bind local address using (mostly) don't-care values.\r
10143    */\r
10144   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10145   mysa.sin_family = AF_INET;\r
10146   mysa.sin_addr.s_addr = INADDR_ANY;\r
10147   uport = (unsigned short) 0;\r
10148   mysa.sin_port = htons(uport);\r
10149   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10150       == SOCKET_ERROR) {\r
10151     err = WSAGetLastError();\r
10152     WSACleanup();\r
10153     return err;\r
10154   }\r
10155 \r
10156   /* Resolve remote host name */\r
10157   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10158   if (!(hp = gethostbyname(host))) {\r
10159     unsigned int b0, b1, b2, b3;\r
10160 \r
10161     err = WSAGetLastError();\r
10162 \r
10163     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10164       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10165       hp->h_addrtype = AF_INET;\r
10166       hp->h_length = 4;\r
10167       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10168       hp->h_addr_list[0] = (char *) malloc(4);\r
10169       hp->h_addr_list[0][0] = (char) b0;\r
10170       hp->h_addr_list[0][1] = (char) b1;\r
10171       hp->h_addr_list[0][2] = (char) b2;\r
10172       hp->h_addr_list[0][3] = (char) b3;\r
10173     } else {\r
10174       WSACleanup();\r
10175       return err;\r
10176     }\r
10177   }\r
10178   sa.sin_family = hp->h_addrtype;\r
10179   uport = (unsigned short) atoi(port);\r
10180   sa.sin_port = htons(uport);\r
10181   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10182 \r
10183   /* Make connection */\r
10184   if (connect(s, (struct sockaddr *) &sa,\r
10185               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10186     err = WSAGetLastError();\r
10187     WSACleanup();\r
10188     return err;\r
10189   }\r
10190 \r
10191   /* Prepare return value */\r
10192   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10193   cp->kind = CPSock;\r
10194   cp->sock = s;\r
10195   *pr = (ProcRef *) cp;\r
10196 \r
10197   return NO_ERROR;\r
10198 }\r
10199 \r
10200 int\r
10201 OpenCommPort(char *name, ProcRef *pr)\r
10202 {\r
10203   HANDLE h;\r
10204   COMMTIMEOUTS ct;\r
10205   ChildProc *cp;\r
10206   char fullname[MSG_SIZ];\r
10207 \r
10208   if (*name != '\\')\r
10209     sprintf(fullname, "\\\\.\\%s", name);\r
10210   else\r
10211     strcpy(fullname, name);\r
10212 \r
10213   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10214                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10215   if (h == (HANDLE) -1) {\r
10216     return GetLastError();\r
10217   }\r
10218   hCommPort = h;\r
10219 \r
10220   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10221 \r
10222   /* Accumulate characters until a 100ms pause, then parse */\r
10223   ct.ReadIntervalTimeout = 100;\r
10224   ct.ReadTotalTimeoutMultiplier = 0;\r
10225   ct.ReadTotalTimeoutConstant = 0;\r
10226   ct.WriteTotalTimeoutMultiplier = 0;\r
10227   ct.WriteTotalTimeoutConstant = 0;\r
10228   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10229 \r
10230   /* Prepare return value */\r
10231   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10232   cp->kind = CPComm;\r
10233   cp->hFrom = h;\r
10234   cp->hTo = h;\r
10235   *pr = (ProcRef *) cp;\r
10236 \r
10237   return NO_ERROR;\r
10238 }\r
10239 \r
10240 int\r
10241 OpenLoopback(ProcRef *pr)\r
10242 {\r
10243   DisplayFatalError("Not implemented", 0, 1);\r
10244   return NO_ERROR;\r
10245 }\r
10246 \r
10247 \r
10248 int\r
10249 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10250 {\r
10251   ChildProc *cp;\r
10252   int err;\r
10253   SOCKET s, s2, s3;\r
10254   struct sockaddr_in sa, mysa;\r
10255   struct hostent FAR *hp;\r
10256   unsigned short uport;\r
10257   WORD wVersionRequested;\r
10258   WSADATA wsaData;\r
10259   int fromPort;\r
10260   char stderrPortStr[MSG_SIZ];\r
10261 \r
10262   /* Initialize socket DLL */\r
10263   wVersionRequested = MAKEWORD(1, 1);\r
10264   err = WSAStartup(wVersionRequested, &wsaData);\r
10265   if (err != 0) return err;\r
10266 \r
10267   /* Resolve remote host name */\r
10268   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10269   if (!(hp = gethostbyname(host))) {\r
10270     unsigned int b0, b1, b2, b3;\r
10271 \r
10272     err = WSAGetLastError();\r
10273 \r
10274     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10275       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10276       hp->h_addrtype = AF_INET;\r
10277       hp->h_length = 4;\r
10278       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10279       hp->h_addr_list[0] = (char *) malloc(4);\r
10280       hp->h_addr_list[0][0] = (char) b0;\r
10281       hp->h_addr_list[0][1] = (char) b1;\r
10282       hp->h_addr_list[0][2] = (char) b2;\r
10283       hp->h_addr_list[0][3] = (char) b3;\r
10284     } else {\r
10285       WSACleanup();\r
10286       return err;\r
10287     }\r
10288   }\r
10289   sa.sin_family = hp->h_addrtype;\r
10290   uport = (unsigned short) 514;\r
10291   sa.sin_port = htons(uport);\r
10292   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10293 \r
10294   /* Bind local socket to unused "privileged" port address\r
10295    */\r
10296   s = INVALID_SOCKET;\r
10297   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10298   mysa.sin_family = AF_INET;\r
10299   mysa.sin_addr.s_addr = INADDR_ANY;\r
10300   for (fromPort = 1023;; fromPort--) {\r
10301     if (fromPort < 0) {\r
10302       WSACleanup();\r
10303       return WSAEADDRINUSE;\r
10304     }\r
10305     if (s == INVALID_SOCKET) {\r
10306       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10307         err = WSAGetLastError();\r
10308         WSACleanup();\r
10309         return err;\r
10310       }\r
10311     }\r
10312     uport = (unsigned short) fromPort;\r
10313     mysa.sin_port = htons(uport);\r
10314     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10315         == SOCKET_ERROR) {\r
10316       err = WSAGetLastError();\r
10317       if (err == WSAEADDRINUSE) continue;\r
10318       WSACleanup();\r
10319       return err;\r
10320     }\r
10321     if (connect(s, (struct sockaddr *) &sa,\r
10322       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10323       err = WSAGetLastError();\r
10324       if (err == WSAEADDRINUSE) {\r
10325         closesocket(s);\r
10326         s = -1;\r
10327         continue;\r
10328       }\r
10329       WSACleanup();\r
10330       return err;\r
10331     }\r
10332     break;\r
10333   }\r
10334 \r
10335   /* Bind stderr local socket to unused "privileged" port address\r
10336    */\r
10337   s2 = INVALID_SOCKET;\r
10338   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10339   mysa.sin_family = AF_INET;\r
10340   mysa.sin_addr.s_addr = INADDR_ANY;\r
10341   for (fromPort = 1023;; fromPort--) {\r
10342     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10343     if (fromPort < 0) {\r
10344       (void) closesocket(s);\r
10345       WSACleanup();\r
10346       return WSAEADDRINUSE;\r
10347     }\r
10348     if (s2 == INVALID_SOCKET) {\r
10349       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10350         err = WSAGetLastError();\r
10351         closesocket(s);\r
10352         WSACleanup();\r
10353         return err;\r
10354       }\r
10355     }\r
10356     uport = (unsigned short) fromPort;\r
10357     mysa.sin_port = htons(uport);\r
10358     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10359         == SOCKET_ERROR) {\r
10360       err = WSAGetLastError();\r
10361       if (err == WSAEADDRINUSE) continue;\r
10362       (void) closesocket(s);\r
10363       WSACleanup();\r
10364       return err;\r
10365     }\r
10366     if (listen(s2, 1) == SOCKET_ERROR) {\r
10367       err = WSAGetLastError();\r
10368       if (err == WSAEADDRINUSE) {\r
10369         closesocket(s2);\r
10370         s2 = INVALID_SOCKET;\r
10371         continue;\r
10372       }\r
10373       (void) closesocket(s);\r
10374       (void) closesocket(s2);\r
10375       WSACleanup();\r
10376       return err;\r
10377     }\r
10378     break;\r
10379   }\r
10380   prevStderrPort = fromPort; // remember port used\r
10381   sprintf(stderrPortStr, "%d", fromPort);\r
10382 \r
10383   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10384     err = WSAGetLastError();\r
10385     (void) closesocket(s);\r
10386     (void) closesocket(s2);\r
10387     WSACleanup();\r
10388     return err;\r
10389   }\r
10390 \r
10391   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10392     err = WSAGetLastError();\r
10393     (void) closesocket(s);\r
10394     (void) closesocket(s2);\r
10395     WSACleanup();\r
10396     return err;\r
10397   }\r
10398   if (*user == NULLCHAR) user = UserName();\r
10399   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10400     err = WSAGetLastError();\r
10401     (void) closesocket(s);\r
10402     (void) closesocket(s2);\r
10403     WSACleanup();\r
10404     return err;\r
10405   }\r
10406   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10407     err = WSAGetLastError();\r
10408     (void) closesocket(s);\r
10409     (void) closesocket(s2);\r
10410     WSACleanup();\r
10411     return err;\r
10412   }\r
10413 \r
10414   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10415     err = WSAGetLastError();\r
10416     (void) closesocket(s);\r
10417     (void) closesocket(s2);\r
10418     WSACleanup();\r
10419     return err;\r
10420   }\r
10421   (void) closesocket(s2);  /* Stop listening */\r
10422 \r
10423   /* Prepare return value */\r
10424   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10425   cp->kind = CPRcmd;\r
10426   cp->sock = s;\r
10427   cp->sock2 = s3;\r
10428   *pr = (ProcRef *) cp;\r
10429 \r
10430   return NO_ERROR;\r
10431 }\r
10432 \r
10433 \r
10434 InputSourceRef\r
10435 AddInputSource(ProcRef pr, int lineByLine,\r
10436                InputCallback func, VOIDSTAR closure)\r
10437 {\r
10438   InputSource *is, *is2 = NULL;\r
10439   ChildProc *cp = (ChildProc *) pr;\r
10440 \r
10441   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10442   is->lineByLine = lineByLine;\r
10443   is->func = func;\r
10444   is->closure = closure;\r
10445   is->second = NULL;\r
10446   is->next = is->buf;\r
10447   if (pr == NoProc) {\r
10448     is->kind = CPReal;\r
10449     consoleInputSource = is;\r
10450   } else {\r
10451     is->kind = cp->kind;\r
10452     /* \r
10453         [AS] Try to avoid a race condition if the thread is given control too early:\r
10454         we create all threads suspended so that the is->hThread variable can be\r
10455         safely assigned, then let the threads start with ResumeThread.\r
10456     */\r
10457     switch (cp->kind) {\r
10458     case CPReal:\r
10459       is->hFile = cp->hFrom;\r
10460       cp->hFrom = NULL; /* now owned by InputThread */\r
10461       is->hThread =\r
10462         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10463                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10464       break;\r
10465 \r
10466     case CPComm:\r
10467       is->hFile = cp->hFrom;\r
10468       cp->hFrom = NULL; /* now owned by InputThread */\r
10469       is->hThread =\r
10470         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10471                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10472       break;\r
10473 \r
10474     case CPSock:\r
10475       is->sock = cp->sock;\r
10476       is->hThread =\r
10477         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10478                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10479       break;\r
10480 \r
10481     case CPRcmd:\r
10482       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10483       *is2 = *is;\r
10484       is->sock = cp->sock;\r
10485       is->second = is2;\r
10486       is2->sock = cp->sock2;\r
10487       is2->second = is2;\r
10488       is->hThread =\r
10489         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10490                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10491       is2->hThread =\r
10492         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10493                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10494       break;\r
10495     }\r
10496 \r
10497     if( is->hThread != NULL ) {\r
10498         ResumeThread( is->hThread );\r
10499     }\r
10500 \r
10501     if( is2 != NULL && is2->hThread != NULL ) {\r
10502         ResumeThread( is2->hThread );\r
10503     }\r
10504   }\r
10505 \r
10506   return (InputSourceRef) is;\r
10507 }\r
10508 \r
10509 void\r
10510 RemoveInputSource(InputSourceRef isr)\r
10511 {\r
10512   InputSource *is;\r
10513 \r
10514   is = (InputSource *) isr;\r
10515   is->hThread = NULL;  /* tell thread to stop */\r
10516   CloseHandle(is->hThread);\r
10517   if (is->second != NULL) {\r
10518     is->second->hThread = NULL;\r
10519     CloseHandle(is->second->hThread);\r
10520   }\r
10521 }\r
10522 \r
10523 \r
10524 int\r
10525 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10526 {\r
10527   DWORD dOutCount;\r
10528   int outCount = SOCKET_ERROR;\r
10529   ChildProc *cp = (ChildProc *) pr;\r
10530   static OVERLAPPED ovl;\r
10531 \r
10532   if (pr == NoProc) {\r
10533     ConsoleOutput(message, count, FALSE);\r
10534     return count;\r
10535   } \r
10536 \r
10537   if (ovl.hEvent == NULL) {\r
10538     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10539   }\r
10540   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10541 \r
10542   switch (cp->kind) {\r
10543   case CPSock:\r
10544   case CPRcmd:\r
10545     outCount = send(cp->sock, message, count, 0);\r
10546     if (outCount == SOCKET_ERROR) {\r
10547       *outError = WSAGetLastError();\r
10548     } else {\r
10549       *outError = NO_ERROR;\r
10550     }\r
10551     break;\r
10552 \r
10553   case CPReal:\r
10554     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10555                   &dOutCount, NULL)) {\r
10556       *outError = NO_ERROR;\r
10557       outCount = (int) dOutCount;\r
10558     } else {\r
10559       *outError = GetLastError();\r
10560     }\r
10561     break;\r
10562 \r
10563   case CPComm:\r
10564     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10565                             &dOutCount, &ovl);\r
10566     if (*outError == NO_ERROR) {\r
10567       outCount = (int) dOutCount;\r
10568     }\r
10569     break;\r
10570   }\r
10571   return outCount;\r
10572 }\r
10573 \r
10574 int\r
10575 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10576                        long msdelay)\r
10577 {\r
10578   /* Ignore delay, not implemented for WinBoard */\r
10579   return OutputToProcess(pr, message, count, outError);\r
10580 }\r
10581 \r
10582 \r
10583 void\r
10584 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10585                         char *buf, int count, int error)\r
10586 {\r
10587   DisplayFatalError("Not implemented", 0, 1);\r
10588 }\r
10589 \r
10590 /* see wgamelist.c for Game List functions */\r
10591 /* see wedittags.c for Edit Tags functions */\r
10592 \r
10593 \r
10594 VOID\r
10595 ICSInitScript()\r
10596 {\r
10597   FILE *f;\r
10598   char buf[MSG_SIZ];\r
10599   char *dummy;\r
10600 \r
10601   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10602     f = fopen(buf, "r");\r
10603     if (f != NULL) {\r
10604       ProcessICSInitScript(f);\r
10605       fclose(f);\r
10606     }\r
10607   }\r
10608 }\r
10609 \r
10610 \r
10611 VOID\r
10612 StartAnalysisClock()\r
10613 {\r
10614   if (analysisTimerEvent) return;\r
10615   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10616                                         (UINT) 2000, NULL);\r
10617 }\r
10618 \r
10619 LRESULT CALLBACK\r
10620 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10621 {\r
10622   static HANDLE hwndText;\r
10623   RECT rect;\r
10624   static int sizeX, sizeY;\r
10625   int newSizeX, newSizeY, flags;\r
10626   MINMAXINFO *mmi;\r
10627 \r
10628   switch (message) {\r
10629   case WM_INITDIALOG: /* message: initialize dialog box */\r
10630     /* Initialize the dialog items */\r
10631     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10632     SetWindowText(hDlg, analysisTitle);\r
10633     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10634     /* Size and position the dialog */\r
10635     if (!analysisDialog) {\r
10636       analysisDialog = hDlg;\r
10637       flags = SWP_NOZORDER;\r
10638       GetClientRect(hDlg, &rect);\r
10639       sizeX = rect.right;\r
10640       sizeY = rect.bottom;\r
10641       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10642           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10643         WINDOWPLACEMENT wp;\r
10644         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10645         wp.length = sizeof(WINDOWPLACEMENT);\r
10646         wp.flags = 0;\r
10647         wp.showCmd = SW_SHOW;\r
10648         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10649         wp.rcNormalPosition.left = analysisX;\r
10650         wp.rcNormalPosition.right = analysisX + analysisW;\r
10651         wp.rcNormalPosition.top = analysisY;\r
10652         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10653         SetWindowPlacement(hDlg, &wp);\r
10654 \r
10655         GetClientRect(hDlg, &rect);\r
10656         newSizeX = rect.right;\r
10657         newSizeY = rect.bottom;\r
10658         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10659                               newSizeX, newSizeY);\r
10660         sizeX = newSizeX;\r
10661         sizeY = newSizeY;\r
10662       }\r
10663     }\r
10664     return FALSE;\r
10665 \r
10666   case WM_COMMAND: /* message: received a command */\r
10667     switch (LOWORD(wParam)) {\r
10668     case IDCANCEL:\r
10669       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10670           ExitAnalyzeMode();\r
10671           ModeHighlight();\r
10672           return TRUE;\r
10673       }\r
10674       EditGameEvent();\r
10675       return TRUE;\r
10676     default:\r
10677       break;\r
10678     }\r
10679     break;\r
10680 \r
10681   case WM_SIZE:\r
10682     newSizeX = LOWORD(lParam);\r
10683     newSizeY = HIWORD(lParam);\r
10684     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10685     sizeX = newSizeX;\r
10686     sizeY = newSizeY;\r
10687     break;\r
10688 \r
10689   case WM_GETMINMAXINFO:\r
10690     /* Prevent resizing window too small */\r
10691     mmi = (MINMAXINFO *) lParam;\r
10692     mmi->ptMinTrackSize.x = 100;\r
10693     mmi->ptMinTrackSize.y = 100;\r
10694     break;\r
10695   }\r
10696   return FALSE;\r
10697 }\r
10698 \r
10699 VOID\r
10700 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10701 {\r
10702   highlightInfo.sq[0].x = fromX;\r
10703   highlightInfo.sq[0].y = fromY;\r
10704   highlightInfo.sq[1].x = toX;\r
10705   highlightInfo.sq[1].y = toY;\r
10706 }\r
10707 \r
10708 VOID\r
10709 ClearHighlights()\r
10710 {\r
10711   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10712     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10713 }\r
10714 \r
10715 VOID\r
10716 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10717 {\r
10718   premoveHighlightInfo.sq[0].x = fromX;\r
10719   premoveHighlightInfo.sq[0].y = fromY;\r
10720   premoveHighlightInfo.sq[1].x = toX;\r
10721   premoveHighlightInfo.sq[1].y = toY;\r
10722 }\r
10723 \r
10724 VOID\r
10725 ClearPremoveHighlights()\r
10726 {\r
10727   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10728     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10729 }\r
10730 \r
10731 VOID\r
10732 ShutDownFrontEnd()\r
10733 {\r
10734   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10735   DeleteClipboardTempFiles();\r
10736 }\r
10737 \r
10738 void\r
10739 BoardToTop()\r
10740 {\r
10741     if (IsIconic(hwndMain))\r
10742       ShowWindow(hwndMain, SW_RESTORE);\r
10743 \r
10744     SetActiveWindow(hwndMain);\r
10745 }\r
10746 \r
10747 /*\r
10748  * Prototypes for animation support routines\r
10749  */\r
10750 static void ScreenSquare(int column, int row, POINT * pt);\r
10751 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10752      POINT frames[], int * nFrames);\r
10753 \r
10754 \r
10755 void\r
10756 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10757 {       // [HGM] atomic: animate blast wave\r
10758         int i;\r
10759 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10760         explodeInfo.fromX = fromX;\r
10761         explodeInfo.fromY = fromY;\r
10762         explodeInfo.toX = toX;\r
10763         explodeInfo.toY = toY;\r
10764         for(i=1; i<nFrames; i++) {\r
10765             explodeInfo.radius = (i*180)/(nFrames-1);\r
10766             DrawPosition(FALSE, NULL);\r
10767             Sleep(appData.animSpeed);\r
10768         }\r
10769         explodeInfo.radius = 0;\r
10770         DrawPosition(TRUE, NULL);\r
10771 }\r
10772 \r
10773 #define kFactor 4\r
10774 \r
10775 void\r
10776 AnimateMove(board, fromX, fromY, toX, toY)\r
10777      Board board;\r
10778      int fromX;\r
10779      int fromY;\r
10780      int toX;\r
10781      int toY;\r
10782 {\r
10783   ChessSquare piece;\r
10784   POINT start, finish, mid;\r
10785   POINT frames[kFactor * 2 + 1];\r
10786   int nFrames, n;\r
10787 \r
10788   if (!appData.animate) return;\r
10789   if (doingSizing) return;\r
10790   if (fromY < 0 || fromX < 0) return;\r
10791   piece = board[fromY][fromX];\r
10792   if (piece >= EmptySquare) return;\r
10793 \r
10794   ScreenSquare(fromX, fromY, &start);\r
10795   ScreenSquare(toX, toY, &finish);\r
10796 \r
10797   /* All pieces except knights move in straight line */\r
10798   if (piece != WhiteKnight && piece != BlackKnight) {\r
10799     mid.x = start.x + (finish.x - start.x) / 2;\r
10800     mid.y = start.y + (finish.y - start.y) / 2;\r
10801   } else {\r
10802     /* Knight: make diagonal movement then straight */\r
10803     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10804        mid.x = start.x + (finish.x - start.x) / 2;\r
10805        mid.y = finish.y;\r
10806      } else {\r
10807        mid.x = finish.x;\r
10808        mid.y = start.y + (finish.y - start.y) / 2;\r
10809      }\r
10810   }\r
10811   \r
10812   /* Don't use as many frames for very short moves */\r
10813   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10814     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10815   else\r
10816     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10817 \r
10818   animInfo.from.x = fromX;\r
10819   animInfo.from.y = fromY;\r
10820   animInfo.to.x = toX;\r
10821   animInfo.to.y = toY;\r
10822   animInfo.lastpos = start;\r
10823   animInfo.piece = piece;\r
10824   for (n = 0; n < nFrames; n++) {\r
10825     animInfo.pos = frames[n];\r
10826     DrawPosition(FALSE, NULL);\r
10827     animInfo.lastpos = animInfo.pos;\r
10828     Sleep(appData.animSpeed);\r
10829   }\r
10830   animInfo.pos = finish;\r
10831   DrawPosition(FALSE, NULL);\r
10832   animInfo.piece = EmptySquare;\r
10833   if(gameInfo.variant == VariantAtomic && \r
10834      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10835         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10836 }\r
10837 \r
10838 /*      Convert board position to corner of screen rect and color       */\r
10839 \r
10840 static void\r
10841 ScreenSquare(column, row, pt)\r
10842      int column; int row; POINT * pt;\r
10843 {\r
10844   if (flipView) {\r
10845     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10846     pt->y = lineGap + row * (squareSize + lineGap);\r
10847   } else {\r
10848     pt->x = lineGap + column * (squareSize + lineGap);\r
10849     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10850   }\r
10851 }\r
10852 \r
10853 /*      Generate a series of frame coords from start->mid->finish.\r
10854         The movement rate doubles until the half way point is\r
10855         reached, then halves back down to the final destination,\r
10856         which gives a nice slow in/out effect. The algorithmn\r
10857         may seem to generate too many intermediates for short\r
10858         moves, but remember that the purpose is to attract the\r
10859         viewers attention to the piece about to be moved and\r
10860         then to where it ends up. Too few frames would be less\r
10861         noticeable.                                             */\r
10862 \r
10863 static void\r
10864 Tween(start, mid, finish, factor, frames, nFrames)\r
10865      POINT * start; POINT * mid;\r
10866      POINT * finish; int factor;\r
10867      POINT frames[]; int * nFrames;\r
10868 {\r
10869   int n, fraction = 1, count = 0;\r
10870 \r
10871   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10872   for (n = 0; n < factor; n++)\r
10873     fraction *= 2;\r
10874   for (n = 0; n < factor; n++) {\r
10875     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10876     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10877     count ++;\r
10878     fraction = fraction / 2;\r
10879   }\r
10880   \r
10881   /* Midpoint */\r
10882   frames[count] = *mid;\r
10883   count ++;\r
10884   \r
10885   /* Slow out, stepping 1/2, then 1/4, ... */\r
10886   fraction = 2;\r
10887   for (n = 0; n < factor; n++) {\r
10888     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10889     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10890     count ++;\r
10891     fraction = fraction * 2;\r
10892   }\r
10893   *nFrames = count;\r
10894 }\r
10895 \r
10896 void\r
10897 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10898 {\r
10899     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10900 \r
10901     EvalGraphSet( first, last, current, pvInfoList );\r
10902 }\r
10903 \r
10904 void SetProgramStats( FrontEndProgramStats * stats )\r
10905 {\r
10906     EngineOutputUpdate( stats );\r
10907 }\r